@bongos/core 1.19.971 → 1.19.973

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.bongos-core.json +116 -76
  2. package/docs/adr/0111-instance-hosting-provisioning-module.md +1 -0
  3. package/docs/adr/0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md +1 -1
  4. package/docs/adr/0325-the-managed-fleet-control-plane-is-the-hub-we-already-run.md +1 -1
  5. package/docs/adr/0327-a-cloud-host-runs-on-the-owners-account-and-the-key-is-borrowed.md +107 -0
  6. package/docs/adr/README.md +1 -0
  7. package/docs/api/openapi.json +1 -1
  8. package/docs/api-reference.md +1 -1
  9. package/docs/architecture.md +6 -6
  10. package/docs/branding-contract.md +1 -1
  11. package/docs/canonical-permissions.md +24 -24
  12. package/docs/copy-inventory.md +193 -187
  13. package/docs/copy-registry.json +320 -276
  14. package/docs/design/projects-hub-direction-v2.md +1 -1
  15. package/docs/design/vanilla-hall-ui-redesign-scope.md +2 -2
  16. package/docs/file-map.md +7 -7
  17. package/docs/handoff-template.md +1 -1
  18. package/docs/module-api-changelog.md +4 -0
  19. package/docs/modules-contract.md +1 -1
  20. package/docs/onboarding/drachmae-and-karma.md +2 -2
  21. package/docs/packs/engineer.md +5 -5
  22. package/docs/packs/ideator.md +1 -1
  23. package/docs/recipes/README.md +1 -1
  24. package/docs/recipes/audit-replay.md +2 -2
  25. package/docs/recipes/gds-db-backup.md +3 -3
  26. package/docs/recipes/managed-settings-remote-control.md +9 -9
  27. package/docs/recipes/memory-sync-conflicts.md +2 -2
  28. package/docs/recipes/ops-gotchas.md +24 -5
  29. package/docs/recipes/overnight-builder.md +3 -3
  30. package/docs/recipes/parallel-goal-waves.md +3 -3
  31. package/docs/recipes/reranking-a-goals-board.md +2 -2
  32. package/docs/recipes/search-index-ingestion.md +1 -1
  33. package/docs/recipes/ship-and-regrade.md +2 -2
  34. package/docs/recipes/staging-environment.md +1 -1
  35. package/docs/recipes/windows-builders.md +1 -1
  36. package/modules/hall-ui/public/deploy.html +10 -5
  37. package/modules/hall-ui/public/deploy.js +199 -110
  38. package/modules/hall-ui/public/goals-page.js +6 -0
  39. package/modules/hall-ui/public/goals-render.js +16 -0
  40. package/modules/hall-ui/public/mine-lens.js +172 -0
  41. package/modules/hall-ui/public/project-settings.css +36 -0
  42. package/modules/hall-ui/public/work-summary.js +90 -0
  43. package/modules/hall-ui/public/work.css +50 -0
  44. package/modules/hall-ui/public/work.html +24 -0
  45. package/modules/hall-ui/public/work.js +38 -51
  46. package/modules/lifecycle/db-goals.js +20 -3
  47. package/modules/lifecycle/routes/goals.js +2 -2
  48. package/modules/provisioning/core-upgrade.js +61 -2
  49. package/modules/public-landing/public/contact.html +1 -1
  50. package/modules/public-landing/public/index.html +1 -1
  51. package/modules/public-landing/public/privacy.html +1 -1
  52. package/modules/public-landing/public/projects.html +1 -1
  53. package/modules/public-landing/public/terms.html +1 -1
  54. package/package-lock.json +2 -2
  55. package/package.json +1 -1
  56. package/scripts/gds/api-alias-caller-scan.js +47 -9
  57. package/scripts/gds/cloudbongos-token-isolation.conf +33 -0
  58. package/scripts/gds/fitness.js +3 -0
  59. package/scripts/gds/provision-core-upgrade.js +132 -1
  60. package/scripts/gds/run-unit-tests.js +14 -9
  61. package/scripts/gds/scope-roster-guard.js +118 -0
  62. package/scripts/hall-preview/server.js +5 -0
  63. package/src/bongos/module-scope-map.js +1 -0
  64. package/src/module-api.js +1 -1
  65. package/tests/api_alias_caller_ratchet.mjs +68 -2
  66. package/tests/core_upgrade_door.mjs +81 -0
  67. package/tests/core_upgrade_runner.mjs +132 -0
  68. package/tests/deploy_page_projects.mjs +234 -79
  69. package/tests/fitness_scope_roster.mjs +117 -0
  70. package/tests/goal_mine_lens.mjs +233 -0
  71. package/tests/goal_tier.mjs +30 -4
  72. package/tests/hall_mine_lens.mjs +381 -0
  73. package/tests/idea_spark_hall.mjs +101 -12
  74. package/tests/known_failing_ratchet.mjs +19 -9
  75. package/tests/landing_legal_pages.mjs +5 -1
  76. package/tests/projects_hub_pre_uat.mjs +12 -6
  77. package/tests/served_paths_shipped.mjs +36 -2
@@ -85,7 +85,7 @@ Every item is tagged as the builders tagged it: **given** (one of the eight), **
85
85
  These were found by the test and page mappers and confirmed against the feeds; they belong to tasks 1003300 / 1003301, not to this round.
86
86
 
87
87
  - **No feed carries a per-project hall address.** `clientBranding()` deliberately withholds domains and `{{buildersOrigin}}` is one value per instance, so "hall entry only where the hall address differs from the site address" needs a new field on `/api/bongos/projects/featured` (or `/my-projects`), e.g. `hall_origin`, null where the hall is at `<site>/builders`. On the apex the hall is `builders.cloudbongos.com` (differs); on a child it is `<site>/builders` (same).
88
- - **No feed carries faces or a 30-day count.** The enrichment (`modules/platform-identity/project-stats.js`) returns `builders`, `tags`, `owner`, `owner_avatar`; the faces need the remote leaderboard's avatar rows kept (the count already comes from them), and `shipped_30d` needs the remote `/api/gds/public/recent-shipped` (row-capped at 30, hence the `N+` rule). `art_url` is already on the feed and is read by nothing.
88
+ - **No feed carries faces or a 30-day count.** The enrichment (`modules/platform-identity/project-stats.js`) returns `builders`, `tags`, `owner`, `owner_avatar`; the faces need the remote leaderboard's avatar rows kept (the count already comes from them), and `shipped_30d` needs the remote `/api/bongos/public/recent-shipped` (row-capped at 30, hence the `N+` rule). `art_url` is already on the feed and is read by nothing.
89
89
  - **Tests that pin the retired chrome and must be retargeted in the build** (from `tests/projects_hub.mjs`, `tests/projects_hub_pre_uat.mjs`, `tests/landing_page.mjs`): the `Home` link (`projects_hub.mjs:40, 46, 48`), the hall pill's `data-link-hall href="{{buildersOrigin}}"` (`landing_page.mjs:214-220`, `projects_hub.mjs:105`), the `.shell .navrow` rule in cosmos (`projects_hub_pre_uat.mjs:203`), the card's slice rule (`projects_hub.mjs:487`: the first adjacent `</div></div>` ends the slice, so every new card region must be a direct child and the banner a bare `<img>`), the `<td>` count across the header, `#selfRow` and the row template if the glyph becomes a cell (`projects_hub.mjs:531-535`), and the singular `querySelector('[data-nav="mine"]')` at `projects.html:5307` (a `My projects` item with `data-nav="mine"` would be a second node; hide by `querySelectorAll` or hook it differently). The dropdown items keep `href="?view=map" data-nav="map"` (etc.) as adjacent attributes so `projects_hub.mjs:42-43` holds unchanged. A new bar-ground token goes into cosmos with both dark twins and a `PAIRS` row in `tests/cosmos_token_contrast.mjs`.
90
90
  - **Copy.** Every string that changes on the built page re-runs `node scripts/gds/copy-inventory.js` (CI checks the registry).
91
91
 
@@ -82,7 +82,7 @@ One shell wraps every page, replacing both the header text-link stack and the pe
82
82
  | **Content** | Scrollable view region | Right of sidebar, below top bar | max 1120px, gutter 24–32 | Only region that scrolls. One view at a time; centers past max. |
83
83
  | **Footer** | Minimal meta line | End of content flow | auto | Reduced to sign-out + instance name; no lore. |
84
84
 
85
- > **Seam preserved.** The shell is chrome only. The hall-widget registry (`dom-utils.js` · `contributeHallWidget`) and the `/api/gds/*` reads are unchanged — widgets mount into view containers instead of one `<main>`.
85
+ > **Seam preserved.** The shell is chrome only. The hall-widget registry (`dom-utils.js` · `contributeHallWidget`) and the `/api/bongos/*` reads are unchanged — widgets mount into view containers instead of one `<main>`.
86
86
 
87
87
  ---
88
88
 
@@ -268,7 +268,7 @@ Not layout, but it rides along. Most of this already flows through the brand pac
268
268
  The hall has hard architectural constraints (see `modules/hall-ui/CLAUDE.md`). Placement changes are cheap only if they respect these.
269
269
 
270
270
  - **Widget registry is the source of truth.** A fitness check (`scripts/gds/fitness.js checkHallSectionRegistry`) reds the build if a `<section class="scroll">` is hardcoded into `<main>`. Views must mount registered widgets, not inline them.
271
- - **No framework, no build step.** Static HTML/CSS/JS polling `/api/gds/*`. The shell is hand-rolled CSS + a small amount of JS — no framework may be introduced.
271
+ - **No framework, no build step.** Static HTML/CSS/JS polling `/api/bongos/*`. The shell is hand-rolled CSS + a small amount of JS — no framework may be introduced.
272
272
  - **Dedicated serving block.** `src/bongos/serve-internal.js` serves this from `BUILDERS_DIR` with the subdomain rewrite shim, canonical redirects, page gates, and asset-stamping. New assets must carry the content-hash `?v=` stamp.
273
273
  - **Server-side gates stay authoritative.** Hiding a sidebar item is cosmetic; `requireGovernancePage` and the sign-in redirects remain the real access control.
274
274
  - **Brand pack drives copy.** Neutralizing voice = a neutral default pack + removing hardcoded lore, not English strings baked into markup.
package/docs/file-map.md CHANGED
@@ -86,8 +86,8 @@
86
86
  │ ├── box-dormant-reclaim.{service,timer} ← (#598) daily CONTROL-PLANE sweep: deprovision long-parked dev boxes
87
87
  │ ├── box-intent-runner.{service,timer} ← (#701) 1-min CONTROL-PLANE runner: drain box_intents (auto-provision + wake-on-connect) via `box.js run-intents --apply`
88
88
  │ ├── box-drift-reconcile.{service,timer} ← (#1289/ADR 0071) hourly CONTROL-PLANE sweep: repair DO⇄DB drift via `box.js reconcile-drift --apply` (kill zombie droplets, mark ghost rows destroyed)
89
- │ ├── box-heartbeat.sh ← (#598) runs ON THE BOX: pings /api/gds/box/heartbeat while in use, so the idle sweep measures activity not uptime
90
- │ ├── box-report-terminal.sh ← (#760/ADR 0038) runs ON THE BOX: POSTs the box's web-terminal URL + basic-auth credential UP to /api/gds/box/terminal so a Chromebook builder reads them from the web
89
+ │ ├── box-heartbeat.sh ← (#598) runs ON THE BOX: pings /api/bongos/box/heartbeat while in use, so the idle sweep measures activity not uptime
90
+ │ ├── box-report-terminal.sh ← (#760/ADR 0038) runs ON THE BOX: POSTs the box's web-terminal URL + basic-auth credential UP to /api/bongos/box/terminal so a Chromebook builder reads them from the web
91
91
  │ ├── box-source-fetch.sh ← (#600/#898/ADR 0052) runs ON THE BOX post-clone: scope-aware source fetch (sparse starter / full) + idempotently installs & enables the box units (box-game-preview, box-terminal, close-box) on every fetch so existing boxes self-heal; also self-tests (task 843) — every cycle it black-box-probes box-terminal/cloudflared/box-game-preview + the terminal credential and restarts whatever's unhealthy, not just what's absent
92
92
  │ ├── builder-box-cloud-init.yaml ← (#598/#599/#760) first-boot bootstrap: Docker + devcontainer up, Claude Code + ttyd + cloudflared on host, box-terminal.service + cloudflared.service + heartbeat cron wired (substituted by box.js)
93
93
  │ ├── managed-settings.template.json ← (#599) Claude Desktop sshConfigs template (locked to one approved box); filled per-builder by gen-managed-settings.sh
@@ -164,7 +164,7 @@
164
164
  │ ├── discord-channels.js ← Archon CLI: plan/apply the Discord channel layout via the API (ADR 0036, #727)
165
165
  │ ├── setup.js ← one-time GitHub Device Flow → ~/.config/otb/gds-session.json
166
166
  │ ├── start.js ← list claimable tasks + active claims (`/builder-start`)
167
- │ ├── claim.js ← atomic POST /api/gds/claims (`/builder-claim`); runs the worktree-per-claim guard (worktree-claim-guard.js) BEFORE the session check and the POST; prints the claim-time context pack incl. next-free ADR/migration numbers (V4.R26); printDisciplineMode() reads discipline-modes.json to route a claimed task to its playbook skill
167
+ │ ├── claim.js ← atomic POST /api/bongos/claims (`/builder-claim`); runs the worktree-per-claim guard (worktree-claim-guard.js) BEFORE the session check and the POST; prints the claim-time context pack incl. next-free ADR/migration numbers (V4.R26); printDisciplineMode() reads discipline-modes.json to route a claimed task to its playbook skill
168
168
  │ ├── discipline-modes.json ← the ROLE REGISTRY read by claim.js: engineer/artist/ideator → their role pack under docs/packs/, module-contributed ui → the /design skill; absence of an entry = the default build-and-ship loop, fail-open at claim time — but scripts/gds/role-pack-guard.js hard-fails CI if an entry names a pack that is not in the tree ([#1267](https://example.com/builders#/task/1267))
169
169
  │ ├── next-number.js ← deterministic next-free ADR + migration number from origin/main ∪ local working tree; supersedes the drift-prone claim-time DB counter (V4.R26, [#964](https://example.com/builders#/task/964)). `node scripts/gds/next-number.js [--json]`
170
170
  │ ├── gen-repo-map.js ← Path B (ADR 0063, [#1224](https://example.com/builders#/task/1224)): zero-dep, deterministic symbol-skeleton generator → docs/repo-map.md + the generated factual block in each nested CLAUDE.md; ranks exported symbols by reference frequency. `--check` = CI gate; regenerated on deploy by ship.js regenerateRepoMap()
@@ -187,7 +187,7 @@
187
187
  │ ├── me.js ← current builder profile + leaderboard
188
188
  │ ├── goal.js ← goal board CLI: `list` (rollup per goal) + `show <id>` (criteria, tasks, waits-on, next claimable)
189
189
  │ ├── seed-v3-tasks.js ← V3 Planning Session #1: 80 net-new GDS-V3 tasks (V3.R01–V3.R89) + 10 idea_inbox promotions; idempotent on source_ref
190
- │ ├── backfill-cost.js ← read art ledger → POST /api/gds/cost (idempotent)
190
+ │ ├── backfill-cost.js ← read art ledger → POST /api/bongos/cost (idempotent)
191
191
  │ ├── smoke-write.sh ← write-path smoke (create→claim→ship round-trip)
192
192
  │ ├── backup-dropin-sweep.sh ← (task 1003930) control-plane sweep: retires the temporary task-1003925 backup drop-in from each project whose pinned core carries the fix (1.19.750+), proving one backup succeeds without it first; survey by default, `--apply` to act; re-run after each pin moves
193
193
  │ ├── stray-unit-sweep.sh ← (task 1003939) control-plane sweep: the ledger's torn-down slugs that still have a service/backup unit loaded or on disk (a torn-down project holding memory outside the ADR 0285 count); survey by default, `--apply` disables, removes, reloads, reset-failed's; DB/dumps/checkout/Caddy left alone
@@ -229,7 +229,7 @@
229
229
  │ │ ├── catalog.js ← the browsable module catalog model (ADR 0167 / ADR 0107 §2/§3): `source` (which TREE ships it — core package vs instance, read off the loader's discovery root) + resolved author/origin `credit` with an `inferred` flag. The ONE builder behind both `GET /api/bongos/modules` and `bongos module list --catalog`, so the hall + CLI can't drift.
230
230
  │ │ └── semver.js ← minimal semver `satisfies(version, range)` used by the loader (no npm dep).
231
231
  │ ├── platform-server.js ← BONGOS-ONLY entrypoint (ADR 0062 R51, task 1190): boots Express + the shared internal surface (API + hall + status) with NO game in the process; the inverse of preview-server.js. `node src/platform-server.js`
232
- │ ├── preview-server.js ← GAME-ONLY entrypoint for the per-box sandbox (ADR 0052): Phaser/Colyseus + tiles, NO /api/gds
232
+ │ ├── preview-server.js ← GAME-ONLY entrypoint for the per-box sandbox (ADR 0052): Phaser/Colyseus + tiles, NO /api/bongos
233
233
  │ ├── seats.js ← in-process seat counter + EventEmitter
234
234
  │ ├── rooms/
235
235
  │ │ ├── WorldRoom.js ← singleton room: move + interact + identity
@@ -401,7 +401,7 @@ tests/
401
401
  ├── builder-exit/SKILL.md ← /builder-exit — gracefully offboard (or reactivate) a builder: release claims, seal session logs, drop rank to xenos
402
402
  ├── builder-key/SKILL.md ← /builder-key — list API-key status, render the visual 'key needed' callout for anything missing, drive the UI-first Settings add-flow + re-sync/confirm; never accepts a key pasted in chat (task 1391)
403
403
  ├── builder-reauth/SKILL.md ← /builder-reauth — refresh an expired Bongos CLI session the UI-first way (no terminal Device Flow)
404
- ├── builder-redteam/SKILL.md ← /builder-redteam — file a red-team / vulnerability report (severity, target, repro) to /api/gds/security
404
+ ├── builder-redteam/SKILL.md ← /builder-redteam — file a red-team / vulnerability report (severity, target, repro) to /api/bongos/security
405
405
  ├── builder-release/SKILL.md ← /builder-release — abandon a claim (task returns to ready, no credits)
406
406
  ├── builder-sequence/SKILL.md ← /builder-sequence N — walk a whole dependency chain from one kickoff (claim → build → ship → next), one claim at a time; halts and reports on migration/security/protected-path/thin-spec/judgement tasks (scripts/gds/sequence.js, task 1002484)
407
407
  ├── builder-setup/SKILL.md ← /builder-setup — one-time GitHub Device Flow auth (Bongos); multi-builder-on-one-device gate
@@ -489,7 +489,7 @@ To install on a new machine, symlink each routine's directory under `~/.claude/s
489
489
  ├── session-start.js ← SessionStart: <redacted> .env.local into a fresh worktree, then chains onboarding-ship + rank-change greeting + memory pull
490
490
  ├── rank-change-greeting.js ← promotion/demotion greeting (V3.R28 #249); spawned by session-start.js, also runnable standalone
491
491
  ├── pull-memory.js ← memory pull-on-session-start (V3.R56 #275); spawned by session-start.js (kill-switches: OTB_MEMORY_PULL_OFF / OTB_SUBAGENT)
492
- ├── recall-hints.js ← SessionStart: when the session opens holding a claim, surface the top project-knowledge hits for that task via POST /api/gds/search (#963/V4.R25, ADR 0060)
492
+ ├── recall-hints.js ← SessionStart: when the session opens holding a claim, surface the top project-knowledge hits for that task via POST /api/bongos/search (#963/V4.R25, ADR 0060)
493
493
  ├── conductor.js ← UserPromptSubmit: MAS Conductor advisory + dispatch directive (#427/#439); self-gates on OTB_CONDUCTOR_OFF / OTB_SUBAGENT
494
494
  ├── session-cards.js ← UserPromptSubmit: deterministic delivery of READ-ONLY in-session cards (builder-start, status) — pre-renders the card + injects a one-step render directive (task 1288)
495
495
  ├── lifecycle-card.js ← PostToolUse (Bash|PowerShell): deterministic delivery of MUTATING lifecycle cards (claim/ship/release) — the write-side mirror of session-cards.js (task 1288)
@@ -27,7 +27,7 @@
27
27
  **New limitations / risks / blockers introduced this session:**
28
28
  - {new V1 architecture limitation → added to limitations/current-version.md}
29
29
  - {new ops gotcha → long-form recipe under docs/recipes/, OR short rule via `node scripts/gds/learning-capture.js`}
30
- - {new blocker for Lars → POST /api/gds/blockers (the markdown file is frozen archive only)}
30
+ - {new blocker for Lars → POST /api/bongos/blockers (the markdown file is frozen archive only)}
31
31
  - {(or "none")}
32
32
 
33
33
  **Generative ideas captured this session:**
@@ -2416,5 +2416,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
2416
2416
  landed since 1.19.969 with no explicit bump. run 35804568036. (task 1002620)
2417
2417
  1.19.971 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2418
2418
  landed since 1.19.970 with no explicit bump. run 35805746484. (task 1002620)
2419
+ 1.19.972 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2420
+ landed since 1.19.971 with no explicit bump. run 35806478158. (task 1002620)
2421
+ 1.19.973 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2422
+ landed since 1.19.972 with no explicit bump. run 35813381436. (task 1002620)
2419
2423
  ---------------------------------------------------------------------------
2420
2424
  ```
@@ -296,7 +296,7 @@ Each module's `modules/<key>/CLAUDE.md` is the authoritative per-module referenc
296
296
  | `isModuleEnabled(name)` | Gate a mount or feature. Throws on an unknown name (programmer error). |
297
297
  | `enabledModules()` | The resolved on-set (an array of enabled module keys). |
298
298
  | `enabledDisciplines()` | The union of `contributes.disciplines` across enabled modules. |
299
- | `clientModules()` | `{ enabled: {…booleans}, disciplines: […] }` — injected into hall/status HTML as `window.__MODULES__` and served at `GET /api/gds/public/modules`. Front-ends read it to hide UI sections for off modules. |
299
+ | `clientModules()` | `{ enabled: {…booleans}, disciplines: […] }` — injected into hall/status HTML as `window.__MODULES__` and served at `GET /api/bongos/public/modules`. Front-ends read it to hide UI sections for off modules. |
300
300
  | `resolveModules(opts)` | The pure resolver (unit-tested, zero I/O). |
301
301
 
302
302
  ---
@@ -118,7 +118,7 @@ Builders are **uncapped by default** ([ADR 0085](../adr/0085-remove-default-per-
118
118
 
119
119
  - **No default cap.** `monthly_budget_usd` is unset for most builders, which means uncapped. The MTD figure still resets at the start of each calendar month.
120
120
  - A cap, when set, is there to **catch a runaway**, not to ration normal work — the **$50 default was removed** ([ADR 0085](../adr/0085-remove-default-per-builder-spend-cap.md)) because a new builder's own session cost crossed it within a day or two, freezing the very work it was meant to enable.
121
- - An **Archon** can set or clear a specific builder's ceiling (`PATCH /api/gds/builders/:id/budget`); clearing it (`null`) returns the builder to uncapped.
121
+ - An **Archon** can set or clear a specific builder's ceiling (`PATCH /api/bongos/builders/:id/budget`); clearing it (`null`) returns the builder to uncapped.
122
122
 
123
123
  If a cap is set and you hit `over`, you're not in trouble — you've just reached the month's ceiling. Ask an Archon to raise or clear it if the work justifies it.
124
124
 
@@ -130,7 +130,7 @@ Karma (added in migration 040) is a **completely separate** counter from example
130
130
 
131
131
  ### How karma is earned (and lost)
132
132
 
133
- - **Peer votes on confirmed work.** Builders upvote/downvote each other's confirmed tasks. Only **Metic-and-above** can cast a vote (`POST /api/gds/tasks/:id/vote` is rank-gated). Votes are **tallied weekly** into one net `peer_vote_weekly` karma entry per builder. There's an **anti-self-vote** guard and an anti-retaliation rule (a downvote can't dent your karma within the same week).
133
+ - **Peer votes on confirmed work.** Builders upvote/downvote each other's confirmed tasks. Only **Metic-and-above** can cast a vote (`POST /api/bongos/tasks/:id/vote` is rank-gated). Votes are **tallied weekly** into one net `peer_vote_weekly` karma entry per builder. There's an **anti-self-vote** guard and an anti-retaliation rule (a downvote can't dent your karma within the same week).
134
134
  - **Proposal ratification.** When a criterion-proposal you made is ratified, you get **+3 karma** (`proposal_ratified`).
135
135
  - **Idea promotion.** Promoting an idea into a real task awards karma to the capturer.
136
136
  - **It can go down.** The [BFG](../adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md) good-citizenship corrections can apply a *non-positive* karma delta. Karma is a slower, more reflective signal than example — you can ship fast and still lose karma if you break things.
@@ -33,13 +33,13 @@ Engineering is the project's **default loop**, so this pack introduces no new dy
33
33
  - **Every change is backed by a claimed Bongos task. No exceptions.** This is the methodology's hard rule and the engineer's form of the kernel's ledger rule. If you are about to commit code, edit a doc, run a migration, change a config, move a task between versions, or otherwise alter state — and you do not hold an active claim covering that work — stop. Create or find the task, claim it, *then* make the change. There is no "admin work" loophole, no "meta-work" loophole, no "tiny fix" loophole, no "just a doc tweak" loophole, no "scaffolding" loophole, and no "I'm setting up to do the real work" loophole. Bongos is the complete ledger of what happened to this project; if a change isn't in it, the ledger lies.
34
34
  - **Direct DB writes are work too.** Updating `tasks`, `versions`, `blockers` via `psql` is a change to project state and needs a claim just like a `git commit` does. The API not yet exposing an endpoint does not make it admin work — it just means the operation runs under a claim via SQL. Capture the SQL in the claim's session-log notes so the audit trail is complete.
35
35
  - **Start every session with `/builder-start`.** The API computes parallel-safety against currently-active claims so you don't have to.
36
- - **Claim before working, from a dedicated worktree** — never the main checkout; `claim.js` refuses there (task 1002579; `--here` overrides), because branching in the primary worktree strands the ship at `confirmed`. `/builder-claim N` locks the task to your session. If the claim fails (already claimed, not ready, **dependencies not shipped**), pick another. If nothing exists for the work you are about to do, create the task first via `POST /api/gds/tasks` or `node scripts/gds/capture.js`, then claim it.
36
+ - **Claim before working, from a dedicated worktree** — never the main checkout; `claim.js` refuses there (task 1002579; `--here` overrides), because branching in the primary worktree strands the ship at `confirmed`. `/builder-claim N` locks the task to your session. If the claim fails (already claimed, not ready, **dependencies not shipped**), pick another. If nothing exists for the work you are about to do, create the task first via `POST /api/bongos/tasks` or `node scripts/gds/capture.js`, then claim it.
37
37
  - **Dependencies gate everything.** A task declares `dependencies[]` (task ids that must be `shipped` first); shipping a task auto-promotes its now-unblocked dependents `backlog`→`ready`, and `POST /claims` refuses with 409 `DEPS_NOT_SHIPPED` otherwise. The `V##.R##` rank prefix is decorative; deps are load-bearing. Full mechanics + CRUD endpoints in ADR 0015.
38
38
  - **Ship or release at session end.** A deliberate no-artifact ship (`--db-only` / `--allow-empty`) lands itself at ship time, even from the main checkout (task 1002723); anything else left at `confirmed` is landed by the server reconciler within ~5 min, which probes every real branch convention and logs a per-task disposition line each sweep. `/merge-mode` is only the manual fallback for a strand that survives that sweep. `/builder-release` returns the task with no credits.
39
39
  - **Code you write or touch carries a doc-comment** — each module a header, each public function or class a line or two, saying *what* it does and *why* it exists (never how), in the words someone would search for. Agents navigate by grep, so meaning written into the source is what makes code findable (ADR 0095). Convention + examples: [`docs/code-conventions.md`](../code-conventions.md). Advisory — `fitness.js` warns, never blocks.
40
40
  - **Non-obvious decision → an ADR** in [`docs/adr/`](../adr/), linked from the shipping task's notes.
41
41
  - **A discovery that took more than ~10 minutes to diagnose → a learning** (`node scripts/gds/learning-capture.js`, DB-backed). A multi-page how-to → a recipe under [`docs/recipes/`](../recipes/).
42
- - **Stuck on something only the owner can decide or provide → a blocker** (`POST /api/gds/blockers`), with the `blocker_ref` attached to the task.
42
+ - **Stuck on something only the owner can decide or provide → a blocker** (`POST /api/bongos/blockers`), with the `blocker_ref` attached to the task.
43
43
  - **Never hand-edit a generated artifact.** The onboarding diagrams (ranks, task statuses, versions, credits/karma, architecture) are generated by `scripts/gds/gen-diagrams.js` on deploy — `.svg`/`.png` + `assertions.json` in lockstep. Edit the template in the generator, run it, commit. Same for `gen-repo-map.js`, `gen-file-map.js`, `gen-session-index.js`.
44
44
 
45
45
  ### Where an idea goes when it isn't this task
@@ -48,7 +48,7 @@ Engineering is the project's **default loop**, so this pack introduces no new dy
48
48
 
49
49
  - Fits inside the existing claim → just ship it under that claim.
50
50
  - Does not fit the claim, but is small and in the current version → finish the claim first, then claim a new task for it.
51
- - Bigger or out of scope → create a task via `POST /api/gds/tasks` (status `backlog`), or capture it with `node scripts/gds/capture.js` for `idea_inbox` triage.
51
+ - Bigger or out of scope → create a task via `POST /api/bongos/tasks` (status `backlog`), or capture it with `node scripts/gds/capture.js` for `idea_inbox` triage.
52
52
 
53
53
  ---
54
54
 
@@ -73,8 +73,8 @@ If this is an autonomous, bypass-permissions, or scheduled run and no one will a
73
73
 
74
74
  1. **Resolve your claim** — `/builder-ship` with handoff notes and a value summary, or `/builder-release` on abandonment. Bongos records the session-log row and awards credits.
75
75
  2. **Write a session log** as its own file at `docs/session-logs/YYYY-MM-DD-<slug>.md`, using [`docs/handoff-template.md`](../handoff-template.md). Do not hand-edit the kernel's §13 index or `docs/session-log-index.md` — both regenerate from your file at ship. For a single-task session the Bongos `session_logs` row is enough and a separate file isn't needed.
76
- 3. **Review the `idea_inbox`** via `GET /api/gds/inbox`, or run `/idea-triage`.
77
- 4. **Update the detail files** if anything changed: a new gotcha → a recipe or a captured learning; a new blocker → `POST /api/gds/blockers`; a decision → a new ADR.
76
+ 3. **Review the `idea_inbox`** via `GET /api/bongos/inbox`, or run `/idea-triage`.
77
+ 4. **Update the detail files** if anything changed: a new gotcha → a recipe or a captured learning; a new blocker → `POST /api/bongos/blockers`; a decision → a new ADR.
78
78
  5. **Update [`docs/architecture.md`](../architecture.md)** if the live system changed, and [`docs/file-map.md`](../file-map.md) if files or folders moved.
79
79
 
80
80
  If nothing changed in a section, leave it alone — don't churn.
@@ -39,7 +39,7 @@ If unsure, ask once: *"Are you here to ideate with me, or should I run this auto
39
39
 
40
40
  So that every idea lands against reality rather than in a vacuum:
41
41
 
42
- 1. **What's already filed** — `node scripts/gds/api.js GET /api/gds/inbox`. You will not propose near-duplicates of open ideas; if a thread overlaps one, say so and build *on* it.
42
+ 1. **What's already filed** — `node scripts/gds/api.js GET /api/bongos/inbox`. You will not propose near-duplicates of open ideas; if a thread overlaps one, say so and build *on* it.
43
43
  2. **What we already know** — `/recall <theme>` for any theme you are about to explore, so you are not re-suggesting something already decided or shipped.
44
44
  3. **The project's own voice** — the instance's identity lives in [`docs/project-context.template.md`](../project-context.template.md) (the scaffold `cloudbongos init` fills) and [`config/branding.neutral.json`](../../config/branding.neutral.json). For product ideas, hold them to that voice. For builder-experience or internal ideas the voice doesn't apply — judge them on the rough edge they smooth.
45
45
 
@@ -17,7 +17,7 @@ A "recipe" is not a limitation (something the project cannot do). It is a multi-
17
17
 
18
18
  - Architectural decisions → [`docs/adr/`](../../docs/adr/)
19
19
  - Things the project cannot do → `limitations/` (instance-side)
20
- - Things stuck on the prompter's input → the blockers queue (`POST /api/gds/blockers`, reviewed via `/blocker-review`)
20
+ - Things stuck on the prompter's input → the blockers queue (`POST /api/bongos/blockers`, reviewed via `/blocker-review`)
21
21
 
22
22
  ## Read order while working
23
23
 
@@ -23,7 +23,7 @@ Run by an Archon when investigating an incident, a suspected compromise, or a "w
23
23
  Notably absent — and worth knowing about so you can ask follow-up questions if needed:
24
24
 
25
25
  - **GET requests** are not in `audit_log` (by design; high volume, low signal). Read patterns aren't part of this replay.
26
- - **Builder rank changes** show up as `HTTP_PATCH route=/api/gds/builders/:id/rank` rows and as `CREDIT` rows when the rank change carries a example adjustment. The new rank value lives in `audit_log.request_body_redacted`.
26
+ - **Builder rank changes** show up as `HTTP_PATCH route=/api/bongos/builders/:id/rank` rows and as `CREDIT` rows when the rank change carries a example adjustment. The new rank value lives in `audit_log.request_body_redacted`.
27
27
  - **Game-server WebSocket events** (movement, world state) live in `world_events` and are NOT included here — this script is Bongos-scoped. If a game-side investigation is needed, query `world_events` separately.
28
28
 
29
29
  ## Running the script
@@ -73,7 +73,7 @@ A "gap" is something the builder did that does NOT appear in the output. The rep
73
73
  - **Direct DB writes via psql.** If an Archon runs `psql … UPDATE tasks SET …` directly, that bypasses the API and produces NO `audit_log` row. Mitigation: don't do that without documenting in the corresponding claim's `session_logs.notes_md`. (See CLAUDE.md §8 working rules — direct DB writes need a claim too.)
74
74
  - **Game-server side effects** (player position updates, WorldRoom state). These intentionally live in `world_events` and are out of scope here; a separate game-side replay would be needed for that.
75
75
  - **Webhooks / scheduled tasks** that act as a builder identity. Each scheduled task runs as `example-owner` (the droplet's service identity); its actions ARE captured as that builder's audit log entries, but a forensic reader needs to know to filter them by `route` shape (e.g. routes that no human ever hits).
76
- - **Build-broadcast posts** to Discord `#ship-news` (`scripts/discord/`, webhook — replaced the retired Google Chat `scripts/chat/` on 2026-06-05). The broadcast itself is not in `audit_log`, but the *trigger* (which task shipped) IS, as a `POST /api/gds/tasks/:id/ship`. So the chain is reconstructable: the audit-replay shows "X shipped at T"; the Discord `#ship-news` channel shows the broadcast went out shortly after.
76
+ - **Build-broadcast posts** to Discord `#ship-news` (`scripts/discord/`, webhook — replaced the retired Google Chat `scripts/chat/` on 2026-06-05). The broadcast itself is not in `audit_log`, but the *trigger* (which task shipped) IS, as a `POST /api/bongos/tasks/:id/ship`. So the chain is reconstructable: the audit-replay shows "X shipped at T"; the Discord `#ship-news` channel shows the broadcast went out shortly after.
77
77
 
78
78
  If you find a class of action that produces NEITHER a row in any of the six tables NOR a record elsewhere, file it as a task — that's a real audit-trail gap.
79
79
 
@@ -265,9 +265,9 @@ gunzip -c "$DUMP" | sudo -u postgres psql -d example
265
265
  # 4. Restart the app.
266
266
  sudo systemctl start example
267
267
 
268
- # 5. Smoke: hit /api/gds/healthz, /api/gds/versions, /api/gds/public/progress.
269
- curl -s https://example.com/api/gds/healthz
270
- curl -s https://example.com/api/gds/public/progress | jq '.progress[0]'
268
+ # 5. Smoke: hit /api/bongos/healthz, /api/bongos/versions, /api/bongos/public/progress.
269
+ curl -s https://example.com/api/bongos/healthz
270
+ curl -s https://example.com/api/bongos/public/progress | jq '.progress[0]'
271
271
  ```
272
272
 
273
273
  ## Bug attachments — the state that is not in the database (task 1003918)
@@ -35,10 +35,10 @@ Both paths land the builder on the same box and the same `/workspace`. The choic
35
35
 
36
36
  ```bash
37
37
  # macOS / Linux
38
- curl -fsSL https://example.com/api/gds/box/connect.sh | bash
38
+ curl -fsSL https://example.com/api/bongos/box/connect.sh | bash
39
39
  # Windows (PowerShell)
40
- irm https://example.com/api/gds/box/connect.ps1 | iex
41
- # Plain-text guide (no auth): https://example.com/api/gds/box/connect
40
+ irm https://example.com/api/bongos/box/connect.ps1 | iex
41
+ # Plain-text guide (no auth): https://example.com/api/bongos/box/connect
42
42
  ```
43
43
 
44
44
  The bootstrap (`infra/box-connect.sh` / `.ps1`, served by `GET /box/connect.sh|.ps1`)
@@ -191,10 +191,10 @@ No SSH and no pairing script - two API calls:
191
191
 
192
192
  ```bash
193
193
  # 1. Get a box (provisions or wakes it; see Part 0 / #701):
194
- POST /api/gds/box/ensure
194
+ POST /api/bongos/box/ensure
195
195
 
196
196
  # 2. Poll until the box has reported its terminal up:
197
- GET /api/gds/box/terminal
197
+ GET /api/bongos/box/terminal
198
198
  # -> {"url": "https://<id>.trycloudflare.com", "credential": "<redacted>"}
199
199
  ```
200
200
 
@@ -276,7 +276,7 @@ The build is unit- and smoke-covered, but the loop crosses real hardware + a hum
276
276
  **Leg A — quick tunnel (zero secrets).**
277
277
 
278
278
  1. Provision a box for a test builder: `node scripts/gds/box.js onboard <login> --apply` (builder must have a registered SSH key, or `--allow-no-keys`). Confirm cloud-init finished: SSH in and `cloud-init status --wait` → `done`; `systemctl is-active box-terminal cloudflared` → `active`.
279
- 2. Confirm the box published its terminal: `tail /tmp/box-report-terminal.log` should show `terminal access reported`. Then as the builder: `GET /api/gds/box/terminal` (or `/builder-box`) → returns `{ url: https://<id>.trycloudflare.com, credential }`.
279
+ 2. Confirm the box published its terminal: `tail /tmp/box-report-terminal.log` should show `terminal access reported`. Then as the builder: `GET /api/bongos/box/terminal` (or `/builder-box`) → returns `{ url: https://<id>.trycloudflare.com, credential }`.
280
280
  3. Open the `url` in a browser. Basic-auth as **`otb`** + the `credential`. You should land in a `/workspace` shell.
281
281
  4. In that terminal run `claude`. Complete the browser `/login` (trust is pre-seeded, [#824](https://example.com/builders#/task/824) — accept once if still prompted) and Trusted-Devices enrollment. Confirm an interactive session starts. **This is the leg `claude rc` could never do** ([#745](https://example.com/builders#/task/745)).
282
282
  5. Inside that session, run **`/remote-control`** (the slash command, NOT `claude rc`). Confirm it prints a pairing link; open the link and confirm the Claude web/mobile app drives the box (ADR 0040, verified live 2026-06-07).
@@ -299,7 +299,7 @@ The build is unit- and smoke-covered, but the loop crosses real hardware + a hum
299
299
 
300
300
  ## Part 3 — The box-heartbeat cron
301
301
 
302
- The cloud-init also wires `infra/box-heartbeat.sh` as a 5-minute cron job. This pings `/api/gds/box/heartbeat` while the box is in active use (interactive SSH session or CPU load > 0.2). Silence = the idle-suspend sweep ([#598](https://example.com/builders#/task/598)) is free to park the box.
302
+ The cloud-init also wires `infra/box-heartbeat.sh` as a 5-minute cron job. This pings `/api/bongos/box/heartbeat` while the box is in active use (interactive SSH session or CPU load > 0.2). Silence = the idle-suspend sweep ([#598](https://example.com/builders#/task/598)) is free to park the box.
303
303
 
304
304
  Without this cron the idle sweep measures **uptime** instead of **activity** and may park a box someone is actively using. Always verify it's wired:
305
305
 
@@ -323,7 +323,7 @@ from Bongos at boot and on a 10-minute cron, via `infra/box-source-fetch.sh`
323
323
  1. The box reads the builder's Bongos session token (it appears after they authenticate
324
324
  Claude Code / run `/builder-reauth` — so the **first clone lands after the builder
325
325
  connects**, not at boot).
326
- 2. It calls `GET /api/gds/box/source-access`. Bongos checks the builder's **live**
326
+ 2. It calls `GET /api/bongos/box/source-access`. Bongos checks the builder's **live**
327
327
  rank + status (ADR 0016 — unforgeable from the box):
328
328
  - **403 `SOURCE_ACCESS_REVOKED`** — builder demoted below the floor or deactivated.
329
329
  The script cuts the credentialed remote (box-side clawback); `box.js reconcile`
@@ -400,7 +400,7 @@ bash infra/gen-managed-settings.sh alice > /tmp/alice-managed-settings.json
400
400
  # 4. For thin-device (Chromebook) path: the web terminal is ready automatically
401
401
  # (box-terminal.service + cloudflared.service start at boot; box-report-terminal
402
402
  # publishes the URL + credential to Bongos within ~1 min)
403
- # Builder self-serves: POST /api/gds/box/ensure, then poll GET /api/gds/box/terminal
403
+ # Builder self-serves: POST /api/bongos/box/ensure, then poll GET /api/bongos/box/terminal
404
404
  # -> open the url, basic-auth as user 'otb' with the returned credential, run: claude
405
405
  # -> then /remote-control to drive the box from the Claude app (ADR 0040)
406
406
 
@@ -12,8 +12,8 @@ Memory is **local-first, server-canonical on ship**. There are exactly two sync
12
12
 
13
13
  | Trigger | Direction | What happens |
14
14
  |---|---|---|
15
- | `/builder-ship` succeeds ([#274](https://example.com/builders#/task/274) R55) | **push** local → server | `POST /api/gds/memory/sync` upserts each local memory file into `builder_memory`. Every file is written unconditionally — the **last push to land wins**. |
16
- | `SessionStart` / `/builder-start` ([#275](https://example.com/builders#/task/275) R56) | **pull** server → local | client reports its newest local `mtime`; `GET /api/gds/memory/since?mtime=…` returns files the server holds that are **newer**; client writes them over local copies. |
15
+ | `/builder-ship` succeeds ([#274](https://example.com/builders#/task/274) R55) | **push** local → server | `POST /api/bongos/memory/sync` upserts each local memory file into `builder_memory`. Every file is written unconditionally — the **last push to land wins**. |
16
+ | `SessionStart` / `/builder-start` ([#275](https://example.com/builders#/task/275) R56) | **pull** server → local | client reports its newest local `mtime`; `GET /api/bongos/memory/since?mtime=…` returns files the server holds that are **newer**; client writes them over local copies. |
17
17
 
18
18
  The stored `mtime` is the client's reported file mtime and is **advisory** — used only by the pull side's newer-than check. The server's own `updated_at` (trigger-maintained) is the authority for "when did this row actually change." A client cannot win a conflict by forging a future mtime: an unparseable mtime is normalized to `NULL`, and the server clock governs.
19
19
 
@@ -87,6 +87,25 @@ Organized by topic, not chronology. New entries go in the matching section; crea
87
87
 
88
88
  ---
89
89
 
90
+ ## systemd
91
+
92
+ ### The web tier is blocked from the cloud tokens by a drop-in — don't "tidy" it away
93
+
94
+ `/etc/systemd/system/cloudbongos.service.d/<redacted>.conf` carries `InaccessiblePaths=/etc/cloudbongos/box.env`, so the internet-facing process cannot read the DigitalOcean and Cloudflare tokens (ADR 0327 §5, task 1004176). The repo copy is `scripts/gds/cloudbongos-token-isolation.conf`.
95
+
96
+ Without it the file is plain mode 600 owned by `lars` and the web tier runs as `lars`, so it *could* read them — ADR 0111 §2's boundary is real in the code but was not enforced by the kernel. Deleting the drop-in silently restores that exposure.
97
+
98
+ **Verifying it needs the service's own user, not root.** A mode-000 over-mount is bypassed by `CAP_DAC_OVERRIDE`, so a root read succeeds and makes a working configuration look like it did nothing:
99
+
100
+ ```
101
+ PID=$(systemctl show -p MainPID --value cloudbongos)
102
+ sudo nsenter -t "$PID" -m -- setpriv --reuid=lars --regid=lars --clear-groups \
103
+ cat /etc/cloudbongos/box.env # must FAIL
104
+ sudo -u lars cat /etc/cloudbongos/box.env >/dev/null # must SUCCEED (runners need it)
105
+ ```
106
+
107
+ Inside the namespace the file lists as `---------- 1 root root 0`. The runner units load it via `EnvironmentFile=` in the host namespace and are unaffected.
108
+
90
109
  ## SSH + droplet
91
110
 
92
111
  ### Don't `apt-get upgrade` in a plain SSH session
@@ -181,7 +200,7 @@ Organized by topic, not chronology. New entries go in the matching section; crea
181
200
 
182
201
  ### PowerShell 5.1 turns a clean `node scripts/gds/*.js` exit into a false failure
183
202
 
184
- - **Symptom:** Running `node scripts/gds/api.js GET /api/gds/me 2>&1` (or the same on `claim.js`/`ship.js`) inside a PowerShell 5.1 session reports failure — `$?` is `$false` and the stderr line surfaces as a `NativeCommandError` — even though node exited 0 and the JSON on stdout is exactly right.
203
+ - **Symptom:** Running `node scripts/gds/api.js GET /api/bongos/me 2>&1` (or the same on `claim.js`/`ship.js`) inside a PowerShell 5.1 session reports failure — `$?` is `$false` and the stderr line surfaces as a `NativeCommandError` — even though node exited 0 and the JSON on stdout is exactly right.
185
204
  - **Root cause:** `api.js` prints its status banner to stderr unconditionally, by design (stdout stays clean JSON for piping); `claim.js`/`ship.js` also write diagnostics to stderr during a normal run. PS 5.1 wraps any stderr output from a native command into an `ErrorRecord` once the two streams are merged with `2>&1`, and sets `$?` false regardless of the actual exit code.
186
205
  - **Fix:** Never merge streams on these calls. Prefer `scripts/gds/api.ps1` (a thin wrapper that discards stderr and writes clean JSON to stdout, safe to pipe into `ConvertFrom-Json`), or call `node scripts/gds/api.js ...` plain, with no `2>&1`.
187
206
  - **Why it bites:** The exit code and JSON body are both fine — only the redirection is wrong — so the failure looks like a real API error and wastes time chasing a phantom bug in the call itself.
@@ -209,17 +228,17 @@ Organized by topic, not chronology. New entries go in the matching section; crea
209
228
  - **Root cause (one known class, fixed):** the grader read a `null` baseline because the task had empty `touches[]`, so it diffed against nothing and "saw" zero files. Fixed in [#536](https://example.com/builders#/task/536) — the grader baseline is now derived from `git merge-base` independent of `touches[]`. If you hit a *different* false-negative class, treat the below as the general escape hatch.
210
229
  - **The process (canonical — works at any rank):**
211
230
  1. The **stuck builder** (any rank, incl. Xenos) files an override request — this is the one builder-rank-available escape hatch:
212
- `POST /api/gds/tasks/:id/override-request` with `{ "rationale": "<≥20 chars: why the grade is wrong>" }`.
231
+ `POST /api/bongos/tasks/:id/override-request` with `{ "rationale": "<≥20 chars: why the grade is wrong>" }`.
213
232
  This is *not* routing around the trust boundary — Xenos are allowed to file; only the decision is privileged.
214
- 2. An **Archon** reviews the open queue (`GET /api/gds/override-requests?status=open`) and decides:
215
- `POST /api/gds/override-requests/:id/decide` with `{ "decision": "approve", "note": "..." }`.
233
+ 2. An **Archon** reviews the open queue (`GET /api/bongos/override-requests?status=open`) and decides:
234
+ `POST /api/bongos/override-requests/:id/decide` with `{ "decision": "approve", "note": "..." }`.
216
235
  3. On approve, the task flips `completed → confirmed` and **both the credit AND the ship attribution (`shipped_by`) land with the original requester**, never the approving Archon ([#538](https://example.com/builders#/task/538)). The whole exchange is permanently audited in `task_override_requests` (requester, decider, rationale, decision note, timestamps).
217
236
  - **Do NOT** use `POST /tasks/:id/confirm` to unstick *another* builder's task — it credits + attributes to the *caller* (the Archon), not the original builder. `/confirm` is for the shipping builder's own `--skip-grade` path. The override-request → approve flow is the correct tool when the work was someone else's.
218
237
  - **Why it bites:** the grader defaults to fail (by design — it's adversarial), so a false-negative looks identical to a real rejection. The fix is a documented, audited human-in-the-loop appeal, not a grader bypass — and the appeal pays the right person.
219
238
 
220
239
  ### A task sits at `confirmed` and every sweep logs `pr_not_landed` for a PR GitHub says is MERGED
221
240
 
222
- - **Symptom:** `journalctl -u <instance>.service | grep publish-reconciler` shows the same task in the `dispositions:` line sweep after sweep as `pr_not_landed`, while `gh pr view <n>` reports `MERGED` with green checks. Only a manual `POST /api/gds/tasks/<id>/ship` clears it. Observed live 2026-08-12 (task 1002695 / PR 91, 18 min across 4 sweeps).
241
+ - **Symptom:** `journalctl -u <instance>.service | grep publish-reconciler` shows the same task in the `dispositions:` line sweep after sweep as `pr_not_landed`, while `gh pr view <n>` reports `MERGED` with green checks. Only a manual `POST /api/bongos/tasks/<id>/ship` clears it. Observed live 2026-08-12 (task 1002695 / PR 91, 18 min across 4 sweeps).
223
242
  - **Read the disposition first.** Since task 1002724 the line carries the probe detail — `1002695:pr_not_landed(deploy=unavailable http=403 pr=91 …)` — so you can tell the three cases apart without a shell:
224
243
  - `deploy=pending` — a real deploy is in flight. Correct; wait.
225
244
  - `deploy=unavailable http=403` — **the server's GitHub token cannot read Actions.** The probe is blind, not waiting. The sweep now falls back to the git-history land proof and lands the task anyway (a blind probe must not outrank main's history), but the underlying token permission is still worth fixing: grant the App/PAT `actions:read` so the deploy gate is real again. On a repo with no `deploy-prod.yml` at all, a token that CAN read Actions gets a clean 404 → `unconfigured` → the merge is the land.
@@ -25,12 +25,12 @@ Parallel runs several tasks at once in **separate isolated worktrees**. It gets
25
25
 
26
26
  > "Run overnight-builder, **parallel 3**, 6-hour budget, stop at $80."
27
27
 
28
- The runner only fills parallel slots with tasks explicitly flagged **`parallel_safe = true`** (and, as always, with non-overlapping file footprints). Flag a task parallel-safe at creation (`parallel_safe: true` in `POST /api/gds/tasks`) or after the fact:
28
+ The runner only fills parallel slots with tasks explicitly flagged **`parallel_safe = true`** (and, as always, with non-overlapping file footprints). Flag a task parallel-safe at creation (`parallel_safe: true` in `POST /api/bongos/tasks`) or after the fact:
29
29
 
30
30
  ```bash
31
31
  TOKEN=$(node -e "console.log(require(require('os').homedir()+'/.config/otb/pms-session.json').token)")
32
32
  curl -sS -X PATCH -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
33
- -d '{"parallel_safe": true}' https://example.com/api/gds/tasks/<id>
33
+ -d '{"parallel_safe": true}' https://example.com/api/bongos/tasks/<id>
34
34
  ```
35
35
 
36
36
  In parallel mode each worker stops at `confirmed` (verified, not yet paid — [ADR 0120](../adr/0120-pay-on-land-and-builder-owned-rebase-gate.md) books credits at land) and the runner lands the merges **one at a time** — concurrent merges to `main` are never allowed. Credits land as each merge completes.
@@ -40,7 +40,7 @@ In parallel mode each worker stops at `confirmed` (verified, not yet paid — [A
40
40
  The end-of-run report buckets everything; the only things needing you are:
41
41
 
42
42
  - **Parked — needs merge** (`confirmed`): a ship passed quality review but hasn't landed yet. The server reconciler sweeps these to `shipped` on its own within ~5 min — re-check the report before acting. Only if one is *still* parked after that does it need **`/merge-mode`** by hand (a conflict the server couldn't auto-resolve).
43
- - **Parked — needs grade review** (`completed`): the quality grader failed or flaked (it flakes ~1 in 3). Eyeball the change; if it's good, manually confirm it (`POST /api/gds/tasks/:id/confirm`, archon-only).
43
+ - **Parked — needs grade review** (`completed`): the quality grader failed or flaked (it flakes ~1 in 3). Eyeball the change; if it's good, manually confirm it (`POST /api/bongos/tasks/:id/confirm`, archon-only).
44
44
  - **Released with feedback**: the runner couldn't finish these and put them back in the queue with a note (a captured learning) explaining why — so the next builder, you or it, doesn't start from zero. Nothing to do unless you want to unblock them.
45
45
 
46
46
  Everything under **Shipped** is already merged and deployed.
@@ -17,8 +17,8 @@ Dependencies tell you what must **ship** first. They tell you nothing about what
17
17
  Start by pulling the real graph — status, `dependencies[]`, `dependents[]`, and `requires_rank` for every task on the goal:
18
18
 
19
19
  ```
20
- node scripts/gds/api.js GET "/api/gds/tasks?goal_id=<goal>&limit=100"
21
- node scripts/gds/api.js GET "/api/gds/tasks/<id>" # includes dependencies + dependents
20
+ node scripts/gds/api.js GET "/api/bongos/tasks?goal_id=<goal>&limit=100"
21
+ node scripts/gds/api.js GET "/api/bongos/tasks/<id>" # includes dependencies + dependents
22
22
  ```
23
23
 
24
24
  > **Gotcha (cost us ~40 minutes).** The generated API client wraps responses as `{status, ok, data:{...}}`. Reading `res.tasks` gives `undefined` and every dependency array silently looks empty — which reads as "this goal has no dependencies at all". Unwrap `.data` first. An empty dependency graph should make you suspicious, not relieved.
@@ -64,7 +64,7 @@ Concurrent bound claims from one machine **used to be impossible**: the app-laye
64
64
  **Use the batch endpoint**, which was built for exactly this:
65
65
 
66
66
  ```
67
- node scripts/gds/api.js POST /api/gds/claims/batch \
67
+ node scripts/gds/api.js POST /api/bongos/claims/batch \
68
68
  --body-file batch.json # {"task_ids":[111,222,333],"bind_session":false}
69
69
  ```
70
70
 
@@ -35,7 +35,7 @@ Three rules that did the most work in practice:
35
35
 
36
36
  ```bash
37
37
  echo '{"priority":2}' > /tmp/p.json
38
- node scripts/gds/api.js PATCH /api/gds/tasks/1002884 --body-file /tmp/p.json
38
+ node scripts/gds/api.js PATCH /api/bongos/tasks/1002884 --body-file /tmp/p.json
39
39
  ```
40
40
 
41
41
  Two things to know before you batch this:
@@ -54,7 +54,7 @@ Core reaches a live instance only through a release plus a pin move (gate 3, [`c
54
54
  Generate the statements from a checked map rather than typing ids, keep it in one transaction, and **write the revert first** — the prior values are only recoverable if you captured them before the write:
55
55
 
56
56
  ```bash
57
- node scripts/gds/api.js GET '/api/gds/tasks?goal_id=<goal>&limit=200' --quiet > before.json
57
+ node scripts/gds/api.js GET '/api/bongos/tasks?goal_id=<goal>&limit=200' --quiet > before.json
58
58
  ```
59
59
 
60
60
  Then group the ids by target tier and emit one `UPDATE` per tier, guarded to the goal and to open statuses so a concurrent ship cannot be clobbered:
@@ -2,7 +2,7 @@
2
2
 
3
3
  How the `search_chunks` index (the recall layer, [ADR 0060](../adr/0060-gds-retrieval-layer.md)) gets populated from repo markdown, and how to operate it. Read before touching `scripts/gds/search-ingest-md.js` or the deploy hook.
4
4
 
5
- > The query side is [`modules/memory/search.js`](../../modules/memory/search.js) + [`POST /api/gds/search`](../../modules/memory/routes/search.js) ([#949](https://example.com/builders#/task/949)); rank/owner scoping + its isolation suite are [#950](https://example.com/builders#/task/950). Ingestion has two halves: **repo markdown** (this recipe's first sections — [#947](https://example.com/builders#/task/947), the deploy hook) and **DB prose** (the [DB-prose nightly](#db-prose-nightly-timer) section below — [#948](https://example.com/builders#/task/948), a systemd timer).
5
+ > The query side is [`modules/memory/search.js`](../../modules/memory/search.js) + [`POST /api/bongos/search`](../../modules/memory/routes/search.js) ([#949](https://example.com/builders#/task/949)); rank/owner scoping + its isolation suite are [#950](https://example.com/builders#/task/950). Ingestion has two halves: **repo markdown** (this recipe's first sections — [#947](https://example.com/builders#/task/947), the deploy hook) and **DB prose** (the [DB-prose nightly](#db-prose-nightly-timer) section below — [#948](https://example.com/builders#/task/948), a systemd timer).
6
6
 
7
7
  ## What it does
8
8
 
@@ -35,7 +35,7 @@ node scripts/gds/ship.js <task-id> --regrade
35
35
 
36
36
  This re-runs **ONLY the grade step** against the committed diff, with **no claim required**:
37
37
 
38
- 1. **Eligibility** — the task must be at `completed`, and you must either have *last held the claim* or be an **Archon**. (The ownership data comes from the `last_claim` field on `GET /api/gds/tasks/:id`; the gate is `evaluateRegradeEligibility` in `ship.js`.)
38
+ 1. **Eligibility** — the task must be at `completed`, and you must either have *last held the claim* or be an **Archon**. (The ownership data comes from the `last_claim` field on `GET /api/bongos/tasks/:id`; the gate is `evaluateRegradeEligibility` in `ship.js`.)
39
39
  2. **Preflight** — your work must be committed (the grader reads committed history, not your working tree) and your local ship/grade tooling must be current with `main` (a stale `grader.js`/`ship.js` silently fails the same way — the [#662](https://example.com/builders#/task/662) lesson). `--force` overrides the freshness checks.
40
40
  3. **Empty-diff guard** — the grader reads committed history, so an empty committed diff aborts cleanly.
41
41
  4. **Grade** — runs the subagent grader and POSTs to the rank-open `/tasks/:id/grade` endpoint. `applyGrade` promotes `completed → confirmed` and awards credits on pass (idempotent — re-grading after the credit already landed is a no-op).
@@ -60,7 +60,7 @@ node scripts/gds/ship.js 600 --regrade # re-grades the committed diff; on pass
60
60
  Only if the grade was genuinely *wrong* (not "I don't want to fix it"):
61
61
 
62
62
  ```bash
63
- curl -X POST -H "Authorization: Bearer <token>" https://example.com/api/gds/tasks/<id>/confirm
63
+ curl -X POST -H "Authorization: Bearer <token>" https://example.com/api/bongos/tasks/<id>/confirm
64
64
  ```
65
65
 
66
66
  This writes a `task_grades` row with `grader_kind='lars'` so the audit trail shows it was a manual override. Prefer `--regrade`.
@@ -182,7 +182,7 @@ The things that ARE separated (process, port, DB, OAuth app, env, chat webhook)
182
182
  ```bash
183
183
  # Full staging smoke from a laptop
184
184
  HOST=https://staging.example.com
185
- for path in /healthz /api/gds/healthz /api/gds/public/versions; do
185
+ for path in /healthz /api/bongos/healthz /api/bongos/public/versions; do
186
186
  status=$(curl -o /dev/null -sS -w '%{http_code}' "${HOST}${path}")
187
187
  printf " %-40s %s\n" "${HOST}${path}" "${status}"
188
188
  done
@@ -183,7 +183,7 @@ you see a ship-call failure from older tooling, check `GET /tasks/<id>` before r
183
183
 
184
184
  ## `git fetch` / `git push` / `ls-remote` fail here BY DESIGN
185
185
 
186
- - This machine holds no GitHub push credential (ADR 0031 §6 / ADR 0055). The scan found 22 `git fetch` attempts in three days, all wasted. Landing is server-mediated: `/builder-ship` uploads a bundle and the server pushes, opens the PR and lands it. To see whether work reached GitHub use `GET /api/gds/tasks/:id/publish-status`, never a raw git probe. To base a new worktree on fresh main, `node scripts/gds/worktree.js add <task-id> --base origin/main` (worktree.js fetches through the sanctioned path).
186
+ - This machine holds no GitHub push credential (ADR 0031 §6 / ADR 0055). The scan found 22 `git fetch` attempts in three days, all wasted. Landing is server-mediated: `/builder-ship` uploads a bundle and the server pushes, opens the PR and lands it. To see whether work reached GitHub use `GET /api/bongos/tasks/:id/publish-status`, never a raw git probe. To base a new worktree on fresh main, `node scripts/gds/worktree.js add <task-id> --base origin/main` (worktree.js fetches through the sanctioned path).
187
187
 
188
188
  ## A worktree without `node_modules` fails 300+ tests — and the count is not the signal
189
189
 
@@ -4,11 +4,11 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Deploy — {{worldName}}</title>
7
- <meta name="description" content="Move this platform or any project it hosts onto a newer core, and read back the version each box actually serves — the deploy gate, without a shell or an ssh key.">
7
+ <meta name="description" content="Move this project onto a newer core and read back the version its box actually serves — the deploy gate, without a shell or an ssh key.">
8
8
  <link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link rel="stylesheet" href="/builders/style.css?v=2026-09-22-movefeedback1">
11
+ <link rel="stylesheet" href="/builders/style.css?v=2026-09-23-changelist1">
12
12
  <link rel="stylesheet" href="/builders/oversight.css">
13
13
  <link rel="stylesheet" href="/builders/project-settings.css">
14
14
  </head>
@@ -36,10 +36,15 @@
36
36
  <!-- The honest framing, and the one thing a reader must not misread:
37
37
  shipping work and deploying it are different acts. "Merged" does not
38
38
  mean visible (ADR 0137's three gates); this page is the third one. -->
39
+ <!-- The page is about THIS project and no other (task 1004171) — every other
40
+ project's upgrade is offered in its own hall, so a page that listed them
41
+ would be the wrong surface as well as the wrong count. -->
39
42
  <p class="scroll__lede">
40
43
  Merging work does not put it in front of anyone. This is the step that does:
41
- it moves this platform — or any project it hosts — onto a newer core, waits
42
- for that box to answer, and puts it back if it does not.
44
+ it moves this project onto a newer core, waits for its box to answer, and
45
+ puts it back if it does not. Every project has this control in its own hall,
46
+ this platform included — so what you see below is one project, the one whose
47
+ hall you are in.
43
48
  </p>
44
49
  <!-- One mount, written by deploy.js. Nothing here is authored as markup with
45
50
  a version in it: every number on this page is a reading the control plane
@@ -59,6 +64,6 @@
59
64
  <script src="/builders/palette.js" defer></script>
60
65
  <script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
61
66
  <script src="/builders/hall-kit.js"></script>
62
- <script src="/builders/deploy.js?v=2026-09-22-movefeedback1"></script>
67
+ <script src="/builders/deploy.js?v=2026-09-23-changelist1"></script>
63
68
  </body>
64
69
  </html>