@bongos/core 1.19.349 → 1.19.351
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +82 -42
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +8 -0
- package/clients/bongos-client/index.cjs +8 -0
- package/clients/bongos-client/index.d.ts +11 -0
- package/clients/bongos-client/index.mjs +8 -0
- package/docs/adr/0184-a-task-visual-inherits-its-summary-audience.md +49 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +208 -3
- package/docs/api-reference.md +10 -2
- package/docs/architecture.md +6 -1
- package/docs/copy-inventory.md +39 -39
- package/docs/copy-registry.json +49 -49
- package/docs/file-map.md +2 -1
- package/migrations/core_223_task_visual.sql +33 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +8 -0
- package/modules/hall-ui/public/hall-kit.js +33 -0
- package/modules/hall-ui/public/hall-render.js +1 -0
- package/modules/hall-ui/public/profile.html +4 -1
- package/modules/hall-ui/public/profile.js +7 -1
- package/modules/hall-ui/public/style.css +29 -0
- package/modules/hall-ui/public/task.js +30 -0
- package/modules/lifecycle/db-analytics.js +3 -1
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/routes/visuals.js +216 -0
- package/modules/lifecycle/task-visual-db.js +46 -0
- package/modules/lifecycle/task-visuals.js +270 -0
- package/modules/status-ui/public/status.js +33 -0
- package/modules/status-ui/public/style.css +14 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/projects.html +30 -10
- package/scripts/gds/ship-flow.js +11 -1
- package/scripts/gds/ship-visual.js +68 -0
- package/scripts/gds/task.js +62 -0
- package/src/bongos/routes/public.js +8 -0
- package/src/bongos/routes.js +5 -1
- package/src/module-api.js +5 -1
- package/tests/projects_hub.mjs +5 -3
- package/tests/projects_hub_pre_uat.mjs +41 -0
- package/tests/public_landing_projects.mjs +1 -1
- package/tests/task_visual_route_gate.mjs +151 -0
- package/tests/task_visuals.mjs +200 -0
package/docs/api/openapi.json
CHANGED
|
@@ -205,6 +205,10 @@
|
|
|
205
205
|
"name": "sso",
|
|
206
206
|
"description": "Endpoints under `/api/bongos/sso`."
|
|
207
207
|
},
|
|
208
|
+
{
|
|
209
|
+
"name": "task-visuals",
|
|
210
|
+
"description": "Endpoints under `/api/bongos/task-visuals`."
|
|
211
|
+
},
|
|
208
212
|
{
|
|
209
213
|
"name": "tasks",
|
|
210
214
|
"description": "Endpoints under `/api/bongos/tasks`."
|
|
@@ -12088,6 +12092,41 @@
|
|
|
12088
12092
|
"security": []
|
|
12089
12093
|
}
|
|
12090
12094
|
},
|
|
12095
|
+
"/task-visuals/{name}": {
|
|
12096
|
+
"get": {
|
|
12097
|
+
"operationId": "get_task_visuals_name",
|
|
12098
|
+
"tags": [
|
|
12099
|
+
"task-visuals"
|
|
12100
|
+
],
|
|
12101
|
+
"summary": "GET /task-visuals/:name",
|
|
12102
|
+
"description": "the WIDEST path this route can take, declared honestly. It is reached only for already-public work (see isPubliclyViewable); every other request falls through to auth.requireBuilder INSIDE the handler, because the decision needs the task the filename names. Read the file header before changing this — a mounted middleware cannot express a per-row audience. GET /api/bongos/task-visuals/:name — stream one stored visual.\n\n**Rank:** `public` — No authentication — any caller.",
|
|
12103
|
+
"x-rank": "public",
|
|
12104
|
+
"x-source": "modules/lifecycle/routes/visuals.js",
|
|
12105
|
+
"parameters": [
|
|
12106
|
+
{
|
|
12107
|
+
"name": "name",
|
|
12108
|
+
"in": "path",
|
|
12109
|
+
"required": true,
|
|
12110
|
+
"schema": {
|
|
12111
|
+
"type": "string"
|
|
12112
|
+
},
|
|
12113
|
+
"description": "Path parameter `name`."
|
|
12114
|
+
}
|
|
12115
|
+
],
|
|
12116
|
+
"responses": {
|
|
12117
|
+
"200": {
|
|
12118
|
+
"description": "Success."
|
|
12119
|
+
},
|
|
12120
|
+
"400": {
|
|
12121
|
+
"$ref": "#/components/responses/BadRequest"
|
|
12122
|
+
},
|
|
12123
|
+
"404": {
|
|
12124
|
+
"$ref": "#/components/responses/NotFound"
|
|
12125
|
+
}
|
|
12126
|
+
},
|
|
12127
|
+
"security": []
|
|
12128
|
+
}
|
|
12129
|
+
},
|
|
12091
12130
|
"/tasks": {
|
|
12092
12131
|
"get": {
|
|
12093
12132
|
"operationId": "get_tasks",
|
|
@@ -13456,6 +13495,119 @@
|
|
|
13456
13495
|
]
|
|
13457
13496
|
}
|
|
13458
13497
|
},
|
|
13498
|
+
"/tasks/{id}/visual": {
|
|
13499
|
+
"post": {
|
|
13500
|
+
"operationId": "post_tasks_id_visual",
|
|
13501
|
+
"tags": [
|
|
13502
|
+
"tasks"
|
|
13503
|
+
],
|
|
13504
|
+
"summary": "POST /tasks/:id/visual",
|
|
13505
|
+
"description": "POST /tasks/:id/visual — attach (or replace) this task's ship-time visual.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
13506
|
+
"x-rank": "any-builder",
|
|
13507
|
+
"x-source": "modules/lifecycle/routes/visuals.js",
|
|
13508
|
+
"parameters": [
|
|
13509
|
+
{
|
|
13510
|
+
"name": "id",
|
|
13511
|
+
"in": "path",
|
|
13512
|
+
"required": true,
|
|
13513
|
+
"schema": {
|
|
13514
|
+
"type": "string"
|
|
13515
|
+
},
|
|
13516
|
+
"description": "Path parameter `id`."
|
|
13517
|
+
}
|
|
13518
|
+
],
|
|
13519
|
+
"requestBody": {
|
|
13520
|
+
"required": true,
|
|
13521
|
+
"content": {
|
|
13522
|
+
"application/json": {
|
|
13523
|
+
"schema": {
|
|
13524
|
+
"$ref": "#/components/schemas/PostTasksIdVisualRequest"
|
|
13525
|
+
}
|
|
13526
|
+
}
|
|
13527
|
+
},
|
|
13528
|
+
"x-validated": true
|
|
13529
|
+
},
|
|
13530
|
+
"responses": {
|
|
13531
|
+
"200": {
|
|
13532
|
+
"description": "Success.",
|
|
13533
|
+
"content": {
|
|
13534
|
+
"application/json": {
|
|
13535
|
+
"schema": {
|
|
13536
|
+
"$ref": "#/components/schemas/PostTasksIdVisualResponse"
|
|
13537
|
+
}
|
|
13538
|
+
}
|
|
13539
|
+
}
|
|
13540
|
+
},
|
|
13541
|
+
"400": {
|
|
13542
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
13543
|
+
},
|
|
13544
|
+
"401": {
|
|
13545
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
13546
|
+
},
|
|
13547
|
+
"403": {
|
|
13548
|
+
"$ref": "#/components/responses/Forbidden"
|
|
13549
|
+
},
|
|
13550
|
+
"404": {
|
|
13551
|
+
"$ref": "#/components/responses/NotFound"
|
|
13552
|
+
}
|
|
13553
|
+
},
|
|
13554
|
+
"security": [
|
|
13555
|
+
{
|
|
13556
|
+
"builderSession": []
|
|
13557
|
+
}
|
|
13558
|
+
]
|
|
13559
|
+
},
|
|
13560
|
+
"delete": {
|
|
13561
|
+
"operationId": "delete_tasks_id_visual",
|
|
13562
|
+
"tags": [
|
|
13563
|
+
"tasks"
|
|
13564
|
+
],
|
|
13565
|
+
"summary": "DELETE /tasks/:id/visual",
|
|
13566
|
+
"description": "DELETE /tasks/:id/visual — remove it again. The counterpart to \"always optional\": a builder who attached the wrong screenshot must be able to take it back, and an image is not prose (the write-once rule of task 1003102 guards a written CLAIM, not the picture beside it). Same ownership gate as the POST. Idempotent — removing an absent visual is a success.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
13567
|
+
"x-rank": "any-builder",
|
|
13568
|
+
"x-source": "modules/lifecycle/routes/visuals.js",
|
|
13569
|
+
"parameters": [
|
|
13570
|
+
{
|
|
13571
|
+
"name": "id",
|
|
13572
|
+
"in": "path",
|
|
13573
|
+
"required": true,
|
|
13574
|
+
"schema": {
|
|
13575
|
+
"type": "string"
|
|
13576
|
+
},
|
|
13577
|
+
"description": "Path parameter `id`."
|
|
13578
|
+
}
|
|
13579
|
+
],
|
|
13580
|
+
"responses": {
|
|
13581
|
+
"200": {
|
|
13582
|
+
"description": "Success.",
|
|
13583
|
+
"content": {
|
|
13584
|
+
"application/json": {
|
|
13585
|
+
"schema": {
|
|
13586
|
+
"$ref": "#/components/schemas/DeleteTasksIdVisualResponse"
|
|
13587
|
+
}
|
|
13588
|
+
}
|
|
13589
|
+
}
|
|
13590
|
+
},
|
|
13591
|
+
"400": {
|
|
13592
|
+
"$ref": "#/components/responses/BadRequest"
|
|
13593
|
+
},
|
|
13594
|
+
"401": {
|
|
13595
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
13596
|
+
},
|
|
13597
|
+
"403": {
|
|
13598
|
+
"$ref": "#/components/responses/Forbidden"
|
|
13599
|
+
},
|
|
13600
|
+
"404": {
|
|
13601
|
+
"$ref": "#/components/responses/NotFound"
|
|
13602
|
+
}
|
|
13603
|
+
},
|
|
13604
|
+
"security": [
|
|
13605
|
+
{
|
|
13606
|
+
"builderSession": []
|
|
13607
|
+
}
|
|
13608
|
+
]
|
|
13609
|
+
}
|
|
13610
|
+
},
|
|
13459
13611
|
"/tasks/{id}/vote": {
|
|
13460
13612
|
"post": {
|
|
13461
13613
|
"operationId": "post_tasks_id_vote",
|
|
@@ -13945,6 +14097,19 @@
|
|
|
13945
14097
|
"removed"
|
|
13946
14098
|
]
|
|
13947
14099
|
},
|
|
14100
|
+
"DeleteTasksIdVisualResponse": {
|
|
14101
|
+
"type": "object",
|
|
14102
|
+
"properties": {
|
|
14103
|
+
"ok": {
|
|
14104
|
+
"type": "boolean"
|
|
14105
|
+
},
|
|
14106
|
+
"visual_url": {}
|
|
14107
|
+
},
|
|
14108
|
+
"required": [
|
|
14109
|
+
"ok",
|
|
14110
|
+
"visual_url"
|
|
14111
|
+
]
|
|
14112
|
+
},
|
|
13948
14113
|
"ErrorEnvelope": {
|
|
13949
14114
|
"type": "object",
|
|
13950
14115
|
"required": [
|
|
@@ -18828,6 +18993,46 @@
|
|
|
18828
18993
|
"task"
|
|
18829
18994
|
]
|
|
18830
18995
|
},
|
|
18996
|
+
"PostTasksIdVisualRequest": {
|
|
18997
|
+
"type": "object",
|
|
18998
|
+
"properties": {
|
|
18999
|
+
"image_b64": {
|
|
19000
|
+
"type": "string",
|
|
19001
|
+
"minLength": 1,
|
|
19002
|
+
"maxLength": 7340032
|
|
19003
|
+
},
|
|
19004
|
+
"content_type": {
|
|
19005
|
+
"type": "string",
|
|
19006
|
+
"minLength": 1,
|
|
19007
|
+
"maxLength": 100
|
|
19008
|
+
},
|
|
19009
|
+
"alt": {
|
|
19010
|
+
"type": "string"
|
|
19011
|
+
}
|
|
19012
|
+
},
|
|
19013
|
+
"required": [
|
|
19014
|
+
"image_b64",
|
|
19015
|
+
"content_type"
|
|
19016
|
+
],
|
|
19017
|
+
"additionalProperties": false
|
|
19018
|
+
},
|
|
19019
|
+
"PostTasksIdVisualResponse": {
|
|
19020
|
+
"type": "object",
|
|
19021
|
+
"properties": {
|
|
19022
|
+
"ok": {
|
|
19023
|
+
"type": "boolean"
|
|
19024
|
+
},
|
|
19025
|
+
"visual_url": {},
|
|
19026
|
+
"visual_alt": {},
|
|
19027
|
+
"bytes": {}
|
|
19028
|
+
},
|
|
19029
|
+
"required": [
|
|
19030
|
+
"ok",
|
|
19031
|
+
"visual_url",
|
|
19032
|
+
"visual_alt",
|
|
19033
|
+
"bytes"
|
|
19034
|
+
]
|
|
19035
|
+
},
|
|
18831
19036
|
"PostTasksIdVoteRequest": {
|
|
18832
19037
|
"type": "object",
|
|
18833
19038
|
"properties": {
|
|
@@ -19243,9 +19448,9 @@
|
|
|
19243
19448
|
"description": "A required dependency/feature is not configured or is temporarily down."
|
|
19244
19449
|
}
|
|
19245
19450
|
},
|
|
19246
|
-
"x-endpoint-count":
|
|
19247
|
-
"x-schema-count":
|
|
19451
|
+
"x-endpoint-count": 307,
|
|
19452
|
+
"x-schema-count": 343,
|
|
19248
19453
|
"x-undocumented-bodies": 8,
|
|
19249
|
-
"x-response-schemas":
|
|
19454
|
+
"x-response-schemas": 229,
|
|
19250
19455
|
"x-generated-by": "scripts/gds/gen-api-docs.js"
|
|
19251
19456
|
}
|
package/docs/api-reference.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Bongos API reference
|
|
4
4
|
|
|
5
|
-
> **Generated from the live route files** — the route file is authoritative.
|
|
5
|
+
> **Generated from the live route files** — the route file is authoritative. 307 endpoints across 50 route files.
|
|
6
6
|
> Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).
|
|
7
7
|
|
|
8
8
|
Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>.md)): `public` < `any-builder` < `metic+archon` < `archon`.
|
|
@@ -507,7 +507,13 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
507
507
|
| GET | `/api/bongos/sso/pubkey` | `public` | — | GET /sso/pubkey — the hub's Ed25519 assertion-verification key (public). |
|
|
508
508
|
| POST | `/api/bongos/sso/token` | `public` | `client_id`, `client_secret`, `code`, `redirect_uri` | POST /sso/token — server-to-server code redemption. |
|
|
509
509
|
|
|
510
|
-
## `
|
|
510
|
+
## `task-visuals` (1)
|
|
511
|
+
|
|
512
|
+
| Method | Path | Rank | Body | Description |
|
|
513
|
+
|---|---|---|---|---|
|
|
514
|
+
| GET | `/api/bongos/task-visuals/:name` | `public` | — | the WIDEST path this route can take, declared honestly. |
|
|
515
|
+
|
|
516
|
+
## `tasks` (29)
|
|
511
517
|
|
|
512
518
|
| Method | Path | Rank | Body | Description |
|
|
513
519
|
|---|---|---|---|---|
|
|
@@ -532,6 +538,8 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
532
538
|
| POST | `/api/bongos/tasks/:id/publish-branch` | `any-builder` | `branch`, `head_sha`, `bundle_b64`, `value_summary` | |
|
|
533
539
|
| GET | `/api/bongos/tasks/:id/publish-status` | `any-builder` | — | GET /tasks/:id/publish-status?branch=<branch> — poll the PR + prod-deploy state for a server-mediated publish so ship.js can confirm the … |
|
|
534
540
|
| POST | `/api/bongos/tasks/:id/ship` | `any-builder` | validated | confirmed → shipped. |
|
|
541
|
+
| POST | `/api/bongos/tasks/:id/visual` | `any-builder` | `image_b64`, `content_type`, `alt` | POST /tasks/:id/visual — attach (or replace) this task's ship-time visual. |
|
|
542
|
+
| DELETE | `/api/bongos/tasks/:id/visual` | `any-builder` | — | DELETE /tasks/:id/visual — remove it again. |
|
|
535
543
|
| POST | `/api/bongos/tasks/:id/vote` | `metic+archon` | `vote` | POST /tasks/:id/vote { vote: 1 \| -1 \| 0 } — cast/change/clear a peer vote on a confirmed/shipped task. |
|
|
536
544
|
| GET | `/api/bongos/tasks/claimable` | `any-builder` | — | NOTE: /tasks/claimable must come BEFORE /tasks/:id or Express will route 'claimable' to the :id param. |
|
|
537
545
|
| GET | `/api/bongos/tasks/newcomer-floor` | `any-builder` | — | GET /tasks/newcomer-floor — supply gauge for the newcomer queue (task 614). |
|
package/docs/architecture.md
CHANGED
|
@@ -106,6 +106,11 @@ versions(id PK 'V1'|'PMS-V1'..., name, track, status, scope_doc_path, ...) -- t
|
|
|
106
106
|
tasks(id, version_id, title, description, status, touches text[], est_minutes, est_cost_usd,
|
|
107
107
|
manual_degree, priority, automation_tag, credits_reward, source, source_ref,
|
|
108
108
|
promoted_at, shipped_at, shipped_by, value_summary, created_at, updated_at)
|
|
109
|
+
+ visual_url, visual_alt (mig core_223, task 1003109) -- the OPTIONAL ship-time VISUAL: a
|
|
110
|
+
screenshot / before-after / diagram, stored on disk under var/task-visuals/ and served by
|
|
111
|
+
GET /api/bongos/task-visuals/:name. It is value_summary's claim in another medium, so it
|
|
112
|
+
inherits that summary's audience (public only for shipped|confirmed, and never for a
|
|
113
|
+
security_sensitive task); never required, and nothing on the ship path blocks on it.
|
|
109
114
|
status ∈ {backlog, ready, active, blocked, completed, confirmed, shipped, abandoned}
|
|
110
115
|
completed = builder declared done (Phase 5 / 2026-05-10)
|
|
111
116
|
confirmed = scanner+smoke passed AND quality grader >= threshold; verified, NOT yet paid
|
|
@@ -222,7 +227,7 @@ Bongos views: `claimable_tasks` (rebuilt in 006 to use SELECT t.*; rebuilt again
|
|
|
222
227
|
- Per-builder "needs" + own Gemini key ([ADR 0073](adr/<redacted>.md), [#1013](https://example.com/builders#/task/1013)): `GET /me` now also carries `needs` ({items, action_needed_count} — the consistent "the system needs an input from you" signal; `modules/builder-settings/builder-needs.js` is the SSOT — carved out in BV1.R80, resolved by `GET /me` via the `builder-settings` kernel port — rendered by the hall Standing card + the CLI `printNeedsNudge`). The own-key (pragmatic BYOK) endpoints, all own-scoped: `GET /me/art-key/own` (masked meta — last4 only), `PUT /me/art-key/own` (validate-on-save via a Google list-models call → encrypt with `src/bongos/secret-box.js` → store; `503 storage_not_configured` until `BUILDER_SECRET_KEY` is set, `422 invalid_key` on a bad key), `DELETE /me/art-key/own`. The existing `GET /me/art-key` (shared-key box-sync delivery) is extended to also deliver the decrypted own key, which `scripts/gds/fetch-art-key.js` syncs into the local `gemini_api_key` slot (own key wins in the `gen_api.py` cascade).
|
|
223
228
|
- Builder memory (ADR 0024 cloneable memory + ADR 0026 BFG). Self-scoped (owner is always `req.builder.id`, never input): `POST /memory/sync` (push own memory; own 64 MB parser, vs the global 64 KB), `GET /memory/files`, `GET /memory/file`, `DELETE /memory/file` (the BFG delete affordance, 6C.2). Cross-builder (input-owner) endpoints — all privileged + audit-logged fail-closed: `GET /memory/builders/:id/files`+`/file` (**archon forensic** read), `GET /memory/bfg/builders/:id/files`+`/file` (**BFG-principal only**, 6B.1 read), and the one cross-builder WRITE `POST /memory/builders/:id/bfg-write` (**BFG-principal only**, kill-switched `BFG_WRITE_ENABLED` default-OFF, namespaced `bfg/`, attributed `author: BFG`, write+audit in one tx — dreams + transparent corrections, 6C.1/6C.2).
|
|
224
229
|
- Session corpus (BFG evaluator — 6D, ADR 0027): `POST /sessions` (upload own session digest; own **4 MB** json parser — capped, vs the global 64 KB — and the digest is per-turn metadata + short scrubbed snippets, never raw transcript content), `GET /sessions/mine` (self-serve), `GET /sessions/search` (cross-builder, **archon-gated** — the BFG-principal stand-in; the principal now exists (6C.1) and the *memory* cross-reads moved to it (6B.1), but these *session* reads still use the archon stand-in pending a retrofit, + audit_log row per read), `GET /sessions/:id` (own self-serve; cross-builder needs archon + audit; non-owner → 404).
|
|
225
|
-
- Public (no auth, `Cache-Control: max-age=60`): `/public/versions`, `/public/progress`, `/public/cost-summary`, `/public/recent-shipped`, `/public/leaderboard`, `/public/grades[?days=N]`, `/public/tasks/:id` ([#604](https://example.com/builders#/task/604) — single-task resolver for `#NNN` doc deep-links; `title`/`value_summary` returned **only** for `shipped`|`confirmed` work and withheld for in-flight tasks so unshipped/security task names can't leak; the status dashboard renders it at `#/task/:id`), `/public/ref-ids` ([#653](https://example.com/builders#/task/653) — `{tasks:[ids]}`, ids only, token-free; the doc-ref linkifier + its CI gate consult it to decide which bare `#NNN` are real task refs). These power `status.example.com`.
|
|
230
|
+
- Public (no auth, `Cache-Control: max-age=60`): `/public/versions`, `/public/progress`, `/public/cost-summary`, `/public/recent-shipped`, `/public/leaderboard`, `/public/grades[?days=N]`, `/public/tasks/:id` ([#604](https://example.com/builders#/task/604) — single-task resolver for `#NNN` doc deep-links; `title`/`value_summary`/`visual_url` returned **only** for `shipped`|`confirmed` work and withheld for in-flight tasks so unshipped/security task names can't leak; the status dashboard renders it at `#/task/:id`), `/public/ref-ids` ([#653](https://example.com/builders#/task/653) — `{tasks:[ids]}`, ids only, token-free; the doc-ref linkifier + its CI gate consult it to decide which bare `#NNN` are real task refs). These power `status.example.com`.
|
|
226
231
|
- Live updates (Server-Sent Events — [#569](https://example.com/builders#/task/569), [ADR 0030](adr/<redacted>.md) signal layer, [ADR 0069](adr/<redacted>.md) transport): an SSE stream at `GET /api/gds/live` ([`src/bongos/routes/live.js`](../src/bongos/routes/live.js)), mounted in both entrypoints (`server.js` + `src/platform-server.js`) so the hall stays live on a game-less boot. This **superseded** the original broadcast-only Colyseus `builders_hall` room (R55 / task 1194 — the room and `src/rooms/BuildersHallRoom.js` were deleted because a Bongos-only instance has no Colyseus in the process). The change-signal layer is **unchanged**: a statement-level Postgres `NOTIFY` trigger (migration 066) on `tasks`/`claims`/`builders`/`builder_onboarding_state`/`credit_log`/`karma_log` fires a `gds_live` notification on every write (incl. direct `psql`); one persistent `LISTEN` connection ([`src/bongos/live-channel.js`](../src/bongos/live-channel.js)) relays each notification, and the SSE endpoint emits an **opaque empty nudge** to open hall tabs (the `{table, op}` is kept server-side so internal table names can't leak); the hall re-runs its `loadAll()` (debounced) on any nudge. No row data crosses the channel — access control stays on the per-panel re-fetches.
|
|
227
232
|
|
|
228
233
|
**Builder ranks & the trust boundary:** moved to its own canonical reference —
|