@bongos/core 1.19.972 → 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 (59) hide show
  1. package/.bongos-core.json +74 -64
  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/architecture.md +6 -6
  8. package/docs/branding-contract.md +1 -1
  9. package/docs/canonical-permissions.md +24 -24
  10. package/docs/copy-inventory.md +45 -38
  11. package/docs/copy-registry.json +130 -67
  12. package/docs/design/projects-hub-direction-v2.md +1 -1
  13. package/docs/design/vanilla-hall-ui-redesign-scope.md +2 -2
  14. package/docs/file-map.md +7 -7
  15. package/docs/handoff-template.md +1 -1
  16. package/docs/module-api-changelog.md +2 -0
  17. package/docs/modules-contract.md +1 -1
  18. package/docs/onboarding/drachmae-and-karma.md +2 -2
  19. package/docs/packs/engineer.md +5 -5
  20. package/docs/packs/ideator.md +1 -1
  21. package/docs/recipes/README.md +1 -1
  22. package/docs/recipes/audit-replay.md +2 -2
  23. package/docs/recipes/gds-db-backup.md +3 -3
  24. package/docs/recipes/managed-settings-remote-control.md +9 -9
  25. package/docs/recipes/memory-sync-conflicts.md +2 -2
  26. package/docs/recipes/ops-gotchas.md +24 -5
  27. package/docs/recipes/overnight-builder.md +3 -3
  28. package/docs/recipes/parallel-goal-waves.md +3 -3
  29. package/docs/recipes/reranking-a-goals-board.md +2 -2
  30. package/docs/recipes/search-index-ingestion.md +1 -1
  31. package/docs/recipes/ship-and-regrade.md +2 -2
  32. package/docs/recipes/staging-environment.md +1 -1
  33. package/docs/recipes/windows-builders.md +1 -1
  34. package/modules/hall-ui/public/deploy.html +10 -5
  35. package/modules/hall-ui/public/deploy.js +199 -110
  36. package/modules/hall-ui/public/project-settings.css +36 -0
  37. package/modules/provisioning/core-upgrade.js +61 -2
  38. package/modules/public-landing/public/contact.html +1 -1
  39. package/modules/public-landing/public/index.html +1 -1
  40. package/modules/public-landing/public/privacy.html +1 -1
  41. package/modules/public-landing/public/projects.html +1 -1
  42. package/modules/public-landing/public/terms.html +1 -1
  43. package/package-lock.json +2 -2
  44. package/package.json +1 -1
  45. package/scripts/gds/api-alias-caller-scan.js +47 -9
  46. package/scripts/gds/cloudbongos-token-isolation.conf +33 -0
  47. package/scripts/gds/provision-core-upgrade.js +132 -1
  48. package/scripts/gds/run-unit-tests.js +14 -9
  49. package/scripts/hall-preview/server.js +5 -0
  50. package/src/module-api.js +1 -1
  51. package/tests/api_alias_caller_ratchet.mjs +68 -2
  52. package/tests/core_upgrade_door.mjs +81 -0
  53. package/tests/core_upgrade_runner.mjs +132 -0
  54. package/tests/deploy_page_projects.mjs +234 -79
  55. package/tests/idea_spark_hall.mjs +101 -12
  56. package/tests/known_failing_ratchet.mjs +19 -9
  57. package/tests/landing_legal_pages.mjs +5 -1
  58. package/tests/projects_hub_pre_uat.mjs +12 -6
  59. package/tests/served_paths_shipped.mjs +36 -2
@@ -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>