@bongos/core 1.19.249 → 1.19.250
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 +52 -27
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +10 -0
- package/clients/bongos-client/index.cjs +10 -0
- package/clients/bongos-client/index.d.ts +14 -0
- package/clients/bongos-client/index.mjs +10 -0
- package/docs/adr/0178-the-copy-desk-flag-and-queue.md +131 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +257 -3
- package/docs/api-reference.md +10 -1
- package/docs/file-map.md +1 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +10 -0
- package/modules/hall-ui/public/copy-desk.css +119 -0
- package/modules/hall-ui/public/copy-desk.html +139 -0
- package/modules/hall-ui/public/copy-desk.js +352 -0
- package/modules/hall-ui/public/shell.js +9 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/run-unit-tests.js +11 -1
- package/src/bongos/module-scope-map.js +17 -0
- package/src/bongos/serve-internal.js +11 -1
- package/src/module-api.js +3 -1
- package/tests/copy_desk_flags_db.mjs +250 -0
- package/tests/hall_nav.mjs +7 -1
- package/tests/module_loader.mjs +5 -2
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.250",
|
|
6
|
+
"core_contract": "1.19.250",
|
|
7
|
+
"source_commit": "f59cb8a3e16573333ac81a1b37976b46909d7f49",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-
|
|
9
|
+
"built_at": "2026-08-19T01:00:55.130Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 313,
|
|
13
13
|
"agent_docs_stubbed": 21,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1334,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1668,
|
|
20
|
+
"tree_sha256": "dff668aafc72a07d98fd1370bf3ad744a895c6336703607bbab6944e967e9518",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -347,12 +347,12 @@
|
|
|
347
347
|
{
|
|
348
348
|
"path": "clients/bongos-client/README.md",
|
|
349
349
|
"mode": "0000644",
|
|
350
|
-
"sha256": "
|
|
350
|
+
"sha256": "687cc799954164a882f70587922fa313a836eb90aae32f99a4e0c3a0e7333e9c"
|
|
351
351
|
},
|
|
352
352
|
{
|
|
353
353
|
"path": "clients/bongos-client/bongos-client.global.js",
|
|
354
354
|
"mode": "0000644",
|
|
355
|
-
"sha256": "
|
|
355
|
+
"sha256": "2985a6dfb923b6b974aed3da0d6fe0915ae1d439a48c4ee7607062e4fbdc182f"
|
|
356
356
|
},
|
|
357
357
|
{
|
|
358
358
|
"path": "clients/bongos-client/examples/hello-world.mjs",
|
|
@@ -362,17 +362,17 @@
|
|
|
362
362
|
{
|
|
363
363
|
"path": "clients/bongos-client/index.cjs",
|
|
364
364
|
"mode": "0000644",
|
|
365
|
-
"sha256": "
|
|
365
|
+
"sha256": "9ec1876369ac9d53ebf9f742b276314c8731cad32b17e8ed068dc180074098c9"
|
|
366
366
|
},
|
|
367
367
|
{
|
|
368
368
|
"path": "clients/bongos-client/index.d.ts",
|
|
369
369
|
"mode": "0000644",
|
|
370
|
-
"sha256": "
|
|
370
|
+
"sha256": "14023727fa9eea325fb6ae784d9edea291c228d19e4966618a3d953191a8ef2d"
|
|
371
371
|
},
|
|
372
372
|
{
|
|
373
373
|
"path": "clients/bongos-client/index.mjs",
|
|
374
374
|
"mode": "0000644",
|
|
375
|
-
"sha256": "
|
|
375
|
+
"sha256": "c6d64e8c7efbad95471f0d68a07e5ee4b83d8122ca1d68cf3ea6a614f3fa396f"
|
|
376
376
|
},
|
|
377
377
|
{
|
|
378
378
|
"path": "clients/bongos-client/package.json",
|
|
@@ -1369,20 +1369,25 @@
|
|
|
1369
1369
|
"mode": "0000644",
|
|
1370
1370
|
"sha256": "922dc12e699d3631907292e0a5550806ac88a160394c65c8223f65e58a46bc6f"
|
|
1371
1371
|
},
|
|
1372
|
+
{
|
|
1373
|
+
"path": "docs/adr/0178-the-copy-desk-flag-and-queue.md",
|
|
1374
|
+
"mode": "0000644",
|
|
1375
|
+
"sha256": "36f95e2bd77c310e1be3632e9b9d84bf014d4d51ae73b450365ed353fefa8923"
|
|
1376
|
+
},
|
|
1372
1377
|
{
|
|
1373
1378
|
"path": "docs/adr/README.md",
|
|
1374
1379
|
"mode": "0000644",
|
|
1375
|
-
"sha256": "
|
|
1380
|
+
"sha256": "745dc09cea4db07614901e6744eeb11c12a7f45711a40cf35c777315452baf2e"
|
|
1376
1381
|
},
|
|
1377
1382
|
{
|
|
1378
1383
|
"path": "docs/api-reference.md",
|
|
1379
1384
|
"mode": "0000644",
|
|
1380
|
-
"sha256": "
|
|
1385
|
+
"sha256": "b99be08b4c6f2e81b12e3ddf80f4cfaa39423e0c87ff2790857ae16ad755fdc7"
|
|
1381
1386
|
},
|
|
1382
1387
|
{
|
|
1383
1388
|
"path": "docs/api/openapi.json",
|
|
1384
1389
|
"mode": "0000644",
|
|
1385
|
-
"sha256": "
|
|
1390
|
+
"sha256": "1c59dad7ce9c5d20bf2ce8671308b5e66a48516c30dc7b48677b0f0382c39e4b"
|
|
1386
1391
|
},
|
|
1387
1392
|
{
|
|
1388
1393
|
"path": "docs/architecture.md",
|
|
@@ -1507,7 +1512,7 @@
|
|
|
1507
1512
|
{
|
|
1508
1513
|
"path": "docs/file-map.md",
|
|
1509
1514
|
"mode": "0000644",
|
|
1510
|
-
"sha256": "
|
|
1515
|
+
"sha256": "fcba56f057bbfa22c5586075fab70ae5559137880ab6886e5f9c990f4801edd7"
|
|
1511
1516
|
},
|
|
1512
1517
|
{
|
|
1513
1518
|
"path": "docs/handoff-template.md",
|
|
@@ -2697,7 +2702,7 @@
|
|
|
2697
2702
|
{
|
|
2698
2703
|
"path": "modules/dev-box/app/src/vendor/bongos-client.cjs",
|
|
2699
2704
|
"mode": "0000644",
|
|
2700
|
-
"sha256": "
|
|
2705
|
+
"sha256": "9ec1876369ac9d53ebf9f742b276314c8731cad32b17e8ed068dc180074098c9"
|
|
2701
2706
|
},
|
|
2702
2707
|
{
|
|
2703
2708
|
"path": "modules/dev-box/app/test/unit.mjs",
|
|
@@ -3189,6 +3194,21 @@
|
|
|
3189
3194
|
"mode": "0000644",
|
|
3190
3195
|
"sha256": "daeaef3932c592d2f9eb7fcd97e99b5eb761d7af9115718fc432360c7d7de0e1"
|
|
3191
3196
|
},
|
|
3197
|
+
{
|
|
3198
|
+
"path": "modules/hall-ui/public/copy-desk.css",
|
|
3199
|
+
"mode": "0000644",
|
|
3200
|
+
"sha256": "b8b7c26156fba8662068b1c393229aac58e35daba1589be708876aa3edc133c6"
|
|
3201
|
+
},
|
|
3202
|
+
{
|
|
3203
|
+
"path": "modules/hall-ui/public/copy-desk.html",
|
|
3204
|
+
"mode": "0000644",
|
|
3205
|
+
"sha256": "54863e10419f1fdf0e0aa9c22bd3144d8015e736e177adb33f016be502f17e85"
|
|
3206
|
+
},
|
|
3207
|
+
{
|
|
3208
|
+
"path": "modules/hall-ui/public/copy-desk.js",
|
|
3209
|
+
"mode": "0000644",
|
|
3210
|
+
"sha256": "f14378218081172f37a2dec8bcc879832686138bbf191d1d8fec5b9db30e0543"
|
|
3211
|
+
},
|
|
3192
3212
|
{
|
|
3193
3213
|
"path": "modules/hall-ui/public/copy-on-click.js",
|
|
3194
3214
|
"mode": "0000644",
|
|
@@ -3492,7 +3512,7 @@
|
|
|
3492
3512
|
{
|
|
3493
3513
|
"path": "modules/hall-ui/public/shell.js",
|
|
3494
3514
|
"mode": "0000644",
|
|
3495
|
-
"sha256": "
|
|
3515
|
+
"sha256": "bfaa74784ee0578fe8e950f55585522a39c892c58f663330a3b724f34a8a0603"
|
|
3496
3516
|
},
|
|
3497
3517
|
{
|
|
3498
3518
|
"path": "modules/hall-ui/public/style.css",
|
|
@@ -4347,12 +4367,12 @@
|
|
|
4347
4367
|
{
|
|
4348
4368
|
"path": "package-lock.json",
|
|
4349
4369
|
"mode": "0000644",
|
|
4350
|
-
"sha256": "
|
|
4370
|
+
"sha256": "d42566d923e50fa60869beed4e57bdf9549f87f83101c54558a29ca9bd848aab"
|
|
4351
4371
|
},
|
|
4352
4372
|
{
|
|
4353
4373
|
"path": "package.json",
|
|
4354
4374
|
"mode": "0000644",
|
|
4355
|
-
"sha256": "
|
|
4375
|
+
"sha256": "4fea4f30c5b2be65786e08dd645d614bb198bcac4c21c7d11190457a4aa1f566"
|
|
4356
4376
|
},
|
|
4357
4377
|
{
|
|
4358
4378
|
"path": "public-docs/index.html",
|
|
@@ -5342,7 +5362,7 @@
|
|
|
5342
5362
|
{
|
|
5343
5363
|
"path": "scripts/gds/run-unit-tests.js",
|
|
5344
5364
|
"mode": "0000644",
|
|
5345
|
-
"sha256": "
|
|
5365
|
+
"sha256": "dd83af498409b0891ed6cd4de5906a182215040733aae677cee6f26e39d81177"
|
|
5346
5366
|
},
|
|
5347
5367
|
{
|
|
5348
5368
|
"path": "scripts/gds/sandbox-stage.js",
|
|
@@ -5802,7 +5822,7 @@
|
|
|
5802
5822
|
{
|
|
5803
5823
|
"path": "src/bongos/module-scope-map.js",
|
|
5804
5824
|
"mode": "0000644",
|
|
5805
|
-
"sha256": "
|
|
5825
|
+
"sha256": "39fe7b2e08a64d038a2f4b5b0807a6803bf19196fe8b43d0c3ec4989fe9cbb6b"
|
|
5806
5826
|
},
|
|
5807
5827
|
{
|
|
5808
5828
|
"path": "src/bongos/module-submissions.js",
|
|
@@ -5937,7 +5957,7 @@
|
|
|
5937
5957
|
{
|
|
5938
5958
|
"path": "src/bongos/serve-internal.js",
|
|
5939
5959
|
"mode": "0000644",
|
|
5940
|
-
"sha256": "
|
|
5960
|
+
"sha256": "785c83fa3bb4a12ea33b528d70e030cffd44fb3ffaecbdb90f7c558621118f3e"
|
|
5941
5961
|
},
|
|
5942
5962
|
{
|
|
5943
5963
|
"path": "src/bongos/stealth-gate.js",
|
|
@@ -5972,7 +5992,7 @@
|
|
|
5972
5992
|
{
|
|
5973
5993
|
"path": "src/module-api.js",
|
|
5974
5994
|
"mode": "0000644",
|
|
5975
|
-
"sha256": "
|
|
5995
|
+
"sha256": "817ba3170d6f4b3132d26c50b25ccb769c8b99b9d436f886bae1ca4bddec6a3d"
|
|
5976
5996
|
},
|
|
5977
5997
|
{
|
|
5978
5998
|
"path": "src/module-loader/catalog.js",
|
|
@@ -6494,6 +6514,11 @@
|
|
|
6494
6514
|
"mode": "0000644",
|
|
6495
6515
|
"sha256": "55bd70efd8067e39d6220d14f78c743ce80d8a8ff37988acf00136b5108dc004"
|
|
6496
6516
|
},
|
|
6517
|
+
{
|
|
6518
|
+
"path": "tests/copy_desk_flags_db.mjs",
|
|
6519
|
+
"mode": "0000644",
|
|
6520
|
+
"sha256": "5ffe8f556f30f00f8489ca0922dc45fef4695a022b0ccee379fe6bd11e7870d5"
|
|
6521
|
+
},
|
|
6497
6522
|
{
|
|
6498
6523
|
"path": "tests/copy_inventory.mjs",
|
|
6499
6524
|
"mode": "0000644",
|
|
@@ -7127,7 +7152,7 @@
|
|
|
7127
7152
|
{
|
|
7128
7153
|
"path": "tests/hall_nav.mjs",
|
|
7129
7154
|
"mode": "0000644",
|
|
7130
|
-
"sha256": "
|
|
7155
|
+
"sha256": "7ab365a19909a0a73b446339d5109f8f1f818dd6fdc49c8792a42bde5757e5ed"
|
|
7131
7156
|
},
|
|
7132
7157
|
{
|
|
7133
7158
|
"path": "tests/hall_page_gate_map.mjs",
|
|
@@ -7467,7 +7492,7 @@
|
|
|
7467
7492
|
{
|
|
7468
7493
|
"path": "tests/module_loader.mjs",
|
|
7469
7494
|
"mode": "0000644",
|
|
7470
|
-
"sha256": "
|
|
7495
|
+
"sha256": "c768beda710cc8446023d95cf5736cb70364403ed6412c41eef7c3c5039c1604"
|
|
7471
7496
|
},
|
|
7472
7497
|
{
|
|
7473
7498
|
"path": "tests/module_manifest.mjs",
|
|
@@ -5,7 +5,7 @@ A **generated**, zero-dependency typed client for the Bongos API — produced fr
|
|
|
5
5
|
by hand; it regenerates when the spec changes, so it can never drift from the routes.
|
|
6
6
|
|
|
7
7
|
- API version: **v1** (served at `/api/bongos/v1`)
|
|
8
|
-
-
|
|
8
|
+
- 289 operations across 47 resource groups
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -252,6 +252,16 @@ function createClient(opts = {}) {
|
|
|
252
252
|
// POST /claims/batch/validate — rank: any-builder — POST /claims/batch/validate
|
|
253
253
|
postClaimsBatchValidate: (args) => request("POST", "/claims/batch/validate", { hasBody: true }, args),
|
|
254
254
|
},
|
|
255
|
+
"copyDesk": {
|
|
256
|
+
// POST /copy-desk/flags — rank: any-builder — POST /copy-desk/flags
|
|
257
|
+
postCopyDeskFlags: (args) => request("POST", "/copy-desk/flags", { hasBody: true }, args),
|
|
258
|
+
// POST /copy-desk/flags/{id}/close — rank: any-builder — POST /copy-desk/flags/:id/close
|
|
259
|
+
postCopyDeskFlagsIdClose: (args) => request("POST", "/copy-desk/flags/{id}/close", { hasBody: true }, args),
|
|
260
|
+
// GET /copy-desk/queue — rank: any-builder — GET /copy-desk/queue
|
|
261
|
+
getCopyDeskQueue: (args) => request("GET", "/copy-desk/queue", { hasBody: false }, args),
|
|
262
|
+
// GET /copy-desk/strings — rank: any-builder — GET /copy-desk/strings
|
|
263
|
+
getCopyDeskStrings: (args) => request("GET", "/copy-desk/strings", { hasBody: false }, args),
|
|
264
|
+
},
|
|
255
265
|
"cost": {
|
|
256
266
|
// POST /cost — rank: any-builder — POST /cost
|
|
257
267
|
postCost: (args) => request("POST", "/cost", { hasBody: true }, args),
|
|
@@ -251,6 +251,16 @@ function createClient(opts = {}) {
|
|
|
251
251
|
// POST /claims/batch/validate — rank: any-builder — POST /claims/batch/validate
|
|
252
252
|
postClaimsBatchValidate: (args) => request("POST", "/claims/batch/validate", { hasBody: true }, args),
|
|
253
253
|
},
|
|
254
|
+
"copyDesk": {
|
|
255
|
+
// POST /copy-desk/flags — rank: any-builder — POST /copy-desk/flags
|
|
256
|
+
postCopyDeskFlags: (args) => request("POST", "/copy-desk/flags", { hasBody: true }, args),
|
|
257
|
+
// POST /copy-desk/flags/{id}/close — rank: any-builder — POST /copy-desk/flags/:id/close
|
|
258
|
+
postCopyDeskFlagsIdClose: (args) => request("POST", "/copy-desk/flags/{id}/close", { hasBody: true }, args),
|
|
259
|
+
// GET /copy-desk/queue — rank: any-builder — GET /copy-desk/queue
|
|
260
|
+
getCopyDeskQueue: (args) => request("GET", "/copy-desk/queue", { hasBody: false }, args),
|
|
261
|
+
// GET /copy-desk/strings — rank: any-builder — GET /copy-desk/strings
|
|
262
|
+
getCopyDeskStrings: (args) => request("GET", "/copy-desk/strings", { hasBody: false }, args),
|
|
263
|
+
},
|
|
254
264
|
"cost": {
|
|
255
265
|
// POST /cost — rank: any-builder — POST /cost
|
|
256
266
|
postCost: (args) => request("POST", "/cost", { hasBody: true }, args),
|
|
@@ -192,6 +192,10 @@ export interface PostClaimsIdReleaseOnBehalfRequest { reason: string }
|
|
|
192
192
|
export interface PostClaimsIdResolveRequest { outcome: "shipped" | "abandoned" | "partial"; notes_md?: string; value_summary?: string; verification?: Record<string, unknown>; force?: boolean; committed_files?: string[]; sandbox_review?: Record<string, unknown> }
|
|
193
193
|
export interface PostClaimsRequest { task_id: StringifiableId; worktree_name?: string; head_sha?: string; bind_session?: boolean; enforce_touches?: boolean }
|
|
194
194
|
export interface PostClaimsResponse { claim: unknown; task: unknown; just_unlocked: unknown; overlap_warning: unknown; reward: unknown }
|
|
195
|
+
export interface PostCopyDeskFlagsIdCloseRequest { status: string; resolution_note: string; resolved_task_id?: number }
|
|
196
|
+
export interface PostCopyDeskFlagsIdCloseResponse { ok: boolean; flag: unknown }
|
|
197
|
+
export interface PostCopyDeskFlagsRequest { scope?: string; surface: string; string_id?: string; reason: string; kind?: string }
|
|
198
|
+
export interface PostCopyDeskFlagsResponse { ok: boolean; flag: unknown }
|
|
195
199
|
export interface PostCostRequest { task_id?: number; amount_usd: number; category: string; description?: string; source?: string; source_ref?: string; recorded_at?: string; fresh_input_usd?: number; cache_read_usd?: number; cache_write_usd?: number; output_usd?: number; builder_id?: number; skill_name?: string }
|
|
196
200
|
export interface PostCostResponse { skipped?: boolean; reason?: unknown; id?: unknown; recorded_at?: unknown }
|
|
197
201
|
export interface PostDependenciesResponse { ok: boolean; dependency: unknown }
|
|
@@ -506,6 +510,16 @@ export interface BongosClient {
|
|
|
506
510
|
/** POST /claims/batch/validate — rank: any-builder */
|
|
507
511
|
postClaimsBatchValidate(args: RequestArgs & { body: PostClaimsBatchValidateRequest }): Promise<ApiResponse>;
|
|
508
512
|
};
|
|
513
|
+
"copyDesk": {
|
|
514
|
+
/** POST /copy-desk/flags — rank: any-builder */
|
|
515
|
+
postCopyDeskFlags(args: RequestArgs & { body: PostCopyDeskFlagsRequest }): Promise<PostCopyDeskFlagsResponse>;
|
|
516
|
+
/** POST /copy-desk/flags/{id}/close — rank: any-builder */
|
|
517
|
+
postCopyDeskFlagsIdClose(args: RequestArgs & { body: PostCopyDeskFlagsIdCloseRequest }): Promise<PostCopyDeskFlagsIdCloseResponse>;
|
|
518
|
+
/** GET /copy-desk/queue — rank: any-builder */
|
|
519
|
+
getCopyDeskQueue(args?: RequestArgs): Promise<ApiResponse>;
|
|
520
|
+
/** GET /copy-desk/strings — rank: any-builder */
|
|
521
|
+
getCopyDeskStrings(args?: RequestArgs): Promise<ApiResponse>;
|
|
522
|
+
};
|
|
509
523
|
"cost": {
|
|
510
524
|
/** POST /cost — rank: any-builder */
|
|
511
525
|
postCost(args: RequestArgs & { body: PostCostRequest }): Promise<PostCostResponse>;
|
|
@@ -248,6 +248,16 @@ export function createClient(opts = {}) {
|
|
|
248
248
|
// POST /claims/batch/validate — rank: any-builder — POST /claims/batch/validate
|
|
249
249
|
postClaimsBatchValidate: (args) => request("POST", "/claims/batch/validate", { hasBody: true }, args),
|
|
250
250
|
},
|
|
251
|
+
"copyDesk": {
|
|
252
|
+
// POST /copy-desk/flags — rank: any-builder — POST /copy-desk/flags
|
|
253
|
+
postCopyDeskFlags: (args) => request("POST", "/copy-desk/flags", { hasBody: true }, args),
|
|
254
|
+
// POST /copy-desk/flags/{id}/close — rank: any-builder — POST /copy-desk/flags/:id/close
|
|
255
|
+
postCopyDeskFlagsIdClose: (args) => request("POST", "/copy-desk/flags/{id}/close", { hasBody: true }, args),
|
|
256
|
+
// GET /copy-desk/queue — rank: any-builder — GET /copy-desk/queue
|
|
257
|
+
getCopyDeskQueue: (args) => request("GET", "/copy-desk/queue", { hasBody: false }, args),
|
|
258
|
+
// GET /copy-desk/strings — rank: any-builder — GET /copy-desk/strings
|
|
259
|
+
getCopyDeskStrings: (args) => request("GET", "/copy-desk/strings", { hasBody: false }, args),
|
|
260
|
+
},
|
|
251
261
|
"cost": {
|
|
252
262
|
// POST /cost — rank: any-builder — POST /cost
|
|
253
263
|
postCost: (args) => request("POST", "/cost", { hasBody: true }, args),
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# 0178 — The copy desk: a flag carries a judgement, and no column here could ever hold copy
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-08-19
|
|
5
|
+
- **Tasks:** [#1003113](https://cloudbongos.com/builders#/task/1003113) (R02 of goal 1000074, the artist's loop) — builds on [#1003112](https://cloudbongos.com/builders#/task/1003112) (R01, the copy registry)
|
|
6
|
+
- **Extends:** [ADR 0081](<redacted>.md) — the repo is the source of truth for design, with swappable tool adapters. This is the same claim for TEXT.
|
|
7
|
+
- **Related:** ADR 0083 (module system, module-owned migrations), 0086 (goals + scope walls), 0016 (authority resolved server-side), 0162 (grader verdicts are advisory, never self-executing), 0175 (an objection requires a reason)
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Goal 1000074 gives the artist real authority over what ships, and the owner's ABI brief gives them the
|
|
12
|
+
project's **text**: no slop, story and ideology communicated, purpose and gravitas, re-text-writing.
|
|
13
|
+
None of that was exercisable. Copy was scattered across 57 hall files, the status dashboard and a
|
|
14
|
+
server-rendered landing template, and nothing routed work to an artist.
|
|
15
|
+
|
|
16
|
+
R01 built the inventory: `scripts/gds/copy-inventory.js` emits `docs/copy-registry.json` — every
|
|
17
|
+
user-facing string with a stable id and the render path that shows it. An inventory answers *what does
|
|
18
|
+
the product say*. It cannot answer *what is wrong with what the product says*, which is a judgement a
|
|
19
|
+
person has to make and record. R02 is that record, plus the queue an artist reads it from.
|
|
20
|
+
|
|
21
|
+
Two constraints came from the goal itself, and they pull against each other. **Blocking strictness is
|
|
22
|
+
per project, not global** (R05's problem, not this one). And **NO LIVE CMS** — every copy change is a
|
|
23
|
+
claimed, graded, shipped task, because the GDS is the complete ledger of what happened to this project.
|
|
24
|
+
The tension: the goal also asks for "an editing affordance". An affordance that edits, and a ledger
|
|
25
|
+
that must record every edit, are one bad column apart from being incompatible.
|
|
26
|
+
|
|
27
|
+
## Decision
|
|
28
|
+
|
|
29
|
+
### 1. Copy is its own module — `copy-desk`, not `copy`
|
|
30
|
+
|
|
31
|
+
The domain has no home today. `ideas` owns intake queues and would have been the cheap answer, but its
|
|
32
|
+
module-owned migrations must be `ideas_`-prefixed, so the table would have been `ideas_copy_flags` — a
|
|
33
|
+
name that lies about the domain. `hall-ui` contributes no routes. `lifecycle` is the build state
|
|
34
|
+
machine. So: a new default-on core-domain module, which the loader discovers with no core edits.
|
|
35
|
+
|
|
36
|
+
**The key is `copy-desk` and not `copy` for a concrete reason, not a stylistic one.** The branding
|
|
37
|
+
contract already owns a `copy` block (`branding.copy.tagline` / `hallName` / `notFound`). The
|
|
38
|
+
`core carries no module code` fitness check greps kernel files for every discovered module key, so a
|
|
39
|
+
module keyed `copy` reds the build on the English word appearing in `src/branding.js`. `copy-desk` is
|
|
40
|
+
also the more accurate name: the copy desk is the editorial station copy passes over before it is
|
|
41
|
+
published, which is exactly the job. SQL namespace `copy_desk_`, table `copy_desk_flags`.
|
|
42
|
+
|
|
43
|
+
The generator stays at `scripts/gds/copy-inventory.js` and is listed in the module's scope-map globs —
|
|
44
|
+
a command travels with its domain (the `memory`/`ideas` precedent), it does not move.
|
|
45
|
+
|
|
46
|
+
### 2. A flag carries a JUDGEMENT, and the floor is in the schema
|
|
47
|
+
|
|
48
|
+
A flag with no reason hands an artist a pointer and no judgement. That is strictly **worse** than not
|
|
49
|
+
flagging, because it still costs them a look. So `reason` is required in the route (a named
|
|
50
|
+
`reason_too_thin` whose payload explains why, since the person hitting it is trying to help) **and** in
|
|
51
|
+
a CHECK constraint (`length(btrim(reason)) >= 10`).
|
|
52
|
+
|
|
53
|
+
Both, deliberately. The route gives a person a usable error; the constraint means no future write path
|
|
54
|
+
— a backfill, a CLI, a later module — can quietly create the reasonless flag the queue exists to not
|
|
55
|
+
contain. Closing a flag requires a note for the same reason, and **the symmetry is the point**: an
|
|
56
|
+
unexplained close is indistinguishable from a queue being cleared to make a number go down. ADR 0175
|
|
57
|
+
made the same call for a board objection.
|
|
58
|
+
|
|
59
|
+
### 3. NO LIVE CMS is enforced by the schema's SHAPE, and made testable
|
|
60
|
+
|
|
61
|
+
This is the load-bearing decision. A non-goal that lives only in prose erodes — and it does not erode
|
|
62
|
+
by someone committing "add a CMS". It erodes by one plausible column ("just store the suggested wording
|
|
63
|
+
so the artist doesn't lose it"), then one plausible read of that column, and the ledger rule is dead
|
|
64
|
+
with nobody having decided to kill it.
|
|
65
|
+
|
|
66
|
+
So **`copy_desk_flags` has no column that could hold replacement text, and must never gain one.** A
|
|
67
|
+
flag carries a judgement (`reason`, `kind`) and a pointer (`surface`, `string_id`). The editing
|
|
68
|
+
affordance R03 builds produces a **task with a diff attached**, not a row here; `resolved_task_id` is
|
|
69
|
+
the seam it lands on.
|
|
70
|
+
|
|
71
|
+
`modules/copy-desk/tests/copy_no_cms.mjs` pins the shape: the write surface is exactly the two verbs
|
|
72
|
+
R02 defines, no `new_text`-shaped column exists, this module performs no filesystem write, and the
|
|
73
|
+
manifest provides no port another module could write copy through. When R03 adds its proposal path the
|
|
74
|
+
expected route list changes **in a diff a reviewer can see**, rather than drifting.
|
|
75
|
+
|
|
76
|
+
### 4. Filing is open to every rank; closing is Metic+ or the author
|
|
77
|
+
|
|
78
|
+
Asymmetric on purpose. **"Sourced by everyone else" is the task's own framing** — the engineer who just
|
|
79
|
+
wrote a toast, the newcomer who could not understand a button, and the ideator reading the landing page
|
|
80
|
+
are the three people most likely to notice bad copy, and none of them is the artist. A rank floor on
|
|
81
|
+
filing leaves the queue empty and the goal unmet. The reason floor is the quality gate; rank is not.
|
|
82
|
+
|
|
83
|
+
Closing someone else's flag is a triage act at the same floor as idea triage and blocker resolution
|
|
84
|
+
(Metic+). An author withdrawing their own flag is a correction, not triage, and should not require a
|
|
85
|
+
promotion. **An artist is a craft, not a rank** (`builders.preferred_disciplines`), so nothing here
|
|
86
|
+
gates on craft — that would hide the artist's own queue from a Xenos artist.
|
|
87
|
+
|
|
88
|
+
### 5. Duplicates are the only automatic signal, and the count subtracts settled work
|
|
89
|
+
|
|
90
|
+
The task asked the queue to surface copy that is **stale, duplicated or off-voice**. Only duplication
|
|
91
|
+
needs no judgement layer, and R01 already computes it deterministically. This module does not recompute
|
|
92
|
+
it — it **subtracts what has already been flagged or dismissed**, because a count that never moves
|
|
93
|
+
however much work an artist does teaches them the queue is decorative. Stale and off-voice need either
|
|
94
|
+
a person (the flag) or a judge, which is not built.
|
|
95
|
+
|
|
96
|
+
### 6. A vanished target is reported, never auto-closed
|
|
97
|
+
|
|
98
|
+
The registry id is a content hash over (surface, text, render path): stable across a file **move**, not
|
|
99
|
+
across an **edit**. So a flag routinely outlives the sentence it names, and `annotateFlag` reports
|
|
100
|
+
`target_state: 'gone'`.
|
|
101
|
+
|
|
102
|
+
That is a **hint for a person**, not a verdict. Auto-closing on it would be the system deciding a human
|
|
103
|
+
judgement is finished because a hash moved — the same family of error ADR 0162 rules out for grader
|
|
104
|
+
verdicts. Every flag also stores a snapshot (text, file, line at flag time), taken server-side from the
|
|
105
|
+
registry and never from the request body: a caller-supplied snapshot is a caller-supplied claim, and
|
|
106
|
+
the snapshot's entire value is that it can be trusted later.
|
|
107
|
+
|
|
108
|
+
## Consequences
|
|
109
|
+
|
|
110
|
+
- **A new default-on module ships to every instance.** Justified: every instance shows a person words.
|
|
111
|
+
- **The queue describes the committed registry** — the deployed tree, not a working copy. It therefore
|
|
112
|
+
names what it read and hands over `--check`, and **deliberately reports no `generated_at`**: R01 is in
|
|
113
|
+
the `gen-*` family and its contract is determinism, so a clock is the one field it cannot carry. The
|
|
114
|
+
file's mtime is the checkout's, and showing it as "generated" would be a plausible lie.
|
|
115
|
+
- **The inventory is partial and says so.** R01's documented blind spots — server-side messages, skill
|
|
116
|
+
prose, runtime-composed strings — travel with the queue payload and render on the page, so "1332
|
|
117
|
+
strings" is never read as "every word the project says".
|
|
118
|
+
- **Adding a hall page adds copy**, so `docs/copy-registry.json` must be regenerated when a surface file
|
|
119
|
+
changes. `tests/copy_inventory.mjs` already enforces this; this task tripped it, which is the system
|
|
120
|
+
working.
|
|
121
|
+
- **Goal 1000074's `scope_modules` must gain `copy-desk`**, or R03–R06 cannot declare `touches[]` in the
|
|
122
|
+
module their work lives in. The mechanism is `POST /goals/:id/scope` with `{"add":["copy-desk"]}` —
|
|
123
|
+
the ADR 0086 §4 "ask to widen" route, Metic+ and member-or-Archon, Archon-only for a protected module.
|
|
124
|
+
**It cannot run until this change is deployed.** The route validates the key against `isModuleKey()`,
|
|
125
|
+
which reads the *running server's* `module-scope-map.js`; until the instance pin moves, the live
|
|
126
|
+
roster has no `copy-desk` and the call answers `400 unknown_module`. So this is an ordering
|
|
127
|
+
dependency, not a missing capability, and it is the one follow-up that must happen after the pin
|
|
128
|
+
moves rather than before.
|
|
129
|
+
- **R05 still owns the hard question.** Nothing here blocks a ship. Whether an artist's flag can ever
|
|
130
|
+
gate a ship, and how that coexists with ADR 0162, is R05's to decide — this ADR deliberately does not
|
|
131
|
+
pre-empt it, and the flag is advisory by construction until it does.
|
package/docs/adr/README.md
CHANGED
|
@@ -251,5 +251,6 @@ This keeps the decision history honest and traceable.
|
|
|
251
251
|
| 0175 | [**The Board Room: Full Idea ratification is the first upward-pointing gate** ([task 1003014](https://cloudbongos.com/builders#/task/1003014), R01 of goal 1000069). Every existing gate points DOWN the rank ladder; the owner asked for one that could slow THEM down — *"otherwise I can just ship and build as I want, finding loopholes."* The board's subjects are exactly **two**, both rare by construction: a **Full Idea** and a **change to the constitution itself** (`subject_type` from day one). Because the vote gates no build path, the **ADR 0162 tension dissolves rather than being compromised** — nothing self-executes because there is nothing downstream to execute; ratification confers legitimacy and does NOT auto-create the goal. A vote is a **timed sitting, not a quorum**: absent members are *nulled* (dropped from the denominator, never counted as a no), so the clock is the deadlock exit and no shotgun/fork-with-treasury clause is needed at this scale — default 90 minutes, closing early on full turnout, and yes, a short window means whoever is awake governs (the owner's explicit intent). **Consent, not majority**: silence is consent, one reasoned objection returns the idea, and a zero-vote close PASSES. A rejection must carry free text **plus one of the five Full Idea sections** — which is what **forces the hall to be the voting surface**, since a Discord reaction could only ever be feedback-free; Discord is therefore an outbound notification mirror that casts nothing, and its own mobile app is the mobile push (no mobile surface is built). The completeness grade is the **floor** (a window opens automatically on pass), the vote is the **merit**; concurrent windows are normal. Who governs is **configuration** — day one is monarchy expressed as config on the real machinery, never a separate code path — and **amendments are decided under whatever rule is currently in force**, so flipping out of monarchy is a legal monarchy act and no bootstrap exception is needed. Open ballot; an author may never vote on their own item. Franchise derives from **rank, not role**, and that is forced: `PATCH /me/disciplines` is deliberately ungated, so a craft-scoped chamber would be one-request privilege escalation. Unblocks goal 1000067 / IC5, which consumes the pass event and *"invents no parallel vote"*. Rejected: quadratic vote weighting and the attribution-economy knobs (γ, τ, the 30% stack cap — v1's franchise is not credit-weighted at all), eternity clauses, and any legal-entity integration.](<redacted>.md) | government / ideas / governance |
|
|
252
252
|
| 0176 | [**Private-repo standup: a per-repo read-only deploy key, minted with the owner's token** ([task 1002735](https://cloudbongos.com/builders#/task/1002735), goal 1000032; walk finding N4, seen live on `mercury` 2026-08-17). A standalone instance's repo is cloned/pushed once with the owner's `repo`-scoped token (ADR 0155), but the durable `origin` rides the shared SSH deploy alias — which GitHub answers "Repository not found" for a private repo it isn't registered on, so every private standup failed at the recurring `git pull --ff-only origin main`, **token or not**. Fix: when the target repo answers *private* to the owner's stored token, provisioning mints the instance its OWN ed25519 deploy key beside the checkout (`.git/cloudbongos-deploy-key` — untracked, survives `git reset --hard`, dies with the checkout), registers it on the repo **read-only** as `cloudbongos-deploy-<slug>` (idempotent by title; a stale same-title key is replaced, never duplicated), then wires the CHECKOUT: `origin` re-pointed at plain `git@github.com` and `core.sshCommand` pinned to the key with `-F /dev/null` so no `~/.ssh/config` identity can shadow it. Public repos take the unchanged shared-alias path; pushes keep preferring the owner-token HTTPS URL (task 1002317), which is why read-only suffices; teardown best-effort revokes by title. Rejected: **GitHub App installation tokens** (hourly expiry means every unattended pull needs a minting sidecar or a hub round-trip) and **registering the shared platform key on the repo** (one shared secret granting the whole platform durable access to private code; per-repo keys keep the blast radius to one repo and are visible to the owner in their own repo settings). Follow-ups: the manage page's flip-to-private stays refused until already-provisioned instances get the same wiring, and builder dev boxes of a private-repo project need their own leg.](<redacted>.md) | provisioning / security |
|
|
253
253
|
| 0177 | [**A goal membership is a `membership_kind`, and the rename ships expand-then-contract** ([task 1003013](https://cloudbongos.com/builders#/task/1003013), goal 1000068). ADR 0174 reserved `role` for the three crafts and enforced it on the GOVERNMENT surface only — by design, since `builders.system_role` and Discord's own noun are legitimate. That left a **fourth** use uncatalogued: `goal_members.role` (`'lead' | 'member'`), the MANAGER predicate behind goal authorization. Renamed to **`membership_kind`** — values unchanged, because the noun was the problem; `rank` is wrong (not a ladder position) and `position` is worse (ADR 0174 uses it as the prose word for a rank, recreating the "rank-role" qualifier collision). A boolean `is_lead` was seriously considered and rejected: the text rename keeps the copied CHECK and stays a reversible change on a live authz column, where `ADD boolean; UPDATE; DROP text` is data-destructive. The migration is **expand-then-contract, never `RENAME COLUMN`** — auto-rollback reverts the code pin and NOT the DB, so `core_220` ADDs the column and keeps the pair in lockstep with a BEFORE trigger (direction decided by which side changed), and a later task DROPs the old column with the `-- forward-unsafe` pragma, a backup, and its own window; no `SET NOT NULL` in step 1, because a static NOT NULL on a column the previous release never heard of is what the forward-safety gate rightly flags. The API field renames with it and carries **no `role` alias** (ADR 0174's Lineage lesson: a time-boxed shim outlives its release), pinned by the test of its absence. The reservation gains a **second narrow wall** rather than a widened first one — line-scoped inside the 5k-line `db.js` to the lines that name `goal_members`, so no allowlist is needed. A **fifth** use (`project_memberships.role`) is catalogued and filed, with this as its pattern.](<redacted>.md) | naming / lifecycle |
|
|
254
|
+
| 0178 | [**The copy desk: a flag carries a judgement, and no column here could ever hold copy** ([task 1003113](https://cloudbongos.com/builders#/task/1003113), R02 of goal 1000074). [ADR 0081](<redacted>.md) made the repo the source of truth for design TOKENS; user-facing TEXT had no equivalent. R01 built the inventory (`copy-inventory.js` → `docs/copy-registry.json`), which answers *what does the product say* but cannot answer *what is wrong with it* — a judgement a person must record. This is that record plus the artist's queue. A new default-on module keyed **`copy-desk`, not `copy`**, for a concrete reason: the branding contract already owns a `copy` block, so the bare word reds the `core carries no module code` fitness check on the English word in `src/branding.js` — and the editorial "copy desk" is the more accurate name anyway. **The reason is required in the route AND in a CHECK constraint**: the route gives a person a usable error, the constraint means no future write path can create the reasonless flag the queue exists to not contain (a pointer with no judgement is *worse* than no flag — it still costs the artist a look). Closing requires a note by the same symmetry, since an unexplained close is indistinguishable from clearing a queue to move a number. The load-bearing decision is **NO LIVE CMS enforced by the schema's SHAPE**: `copy_desk_flags` has no column that could hold replacement text and must never gain one, because that non-goal does not erode via an "add a CMS" commit but via one plausible column, then one plausible read of it — `copy_no_cms.mjs` pins the write surface at exactly two verbs so R03's proposal path changes it in a reviewable diff. Rank is asymmetric: **filing is open to every rank** (the engineer who wrote the toast and the newcomer who could not read the button are the likeliest noticers, and neither is the artist — the reason floor is the quality gate, rank is not), closing is Metic+ **or the author** (withdrawing your own flag is a correction, not triage); nothing gates on craft, since an artist is a craft and not a rank. Duplicates are the only automatic signal (the one of stale/duplicated/off-voice needing no judgement layer) and the count **subtracts settled work**, or it never moves and teaches that the queue is decorative. A vanished target is **reported, never auto-closed** — the id is a hash over the text, so an edit routinely orphans a flag, and closing a human judgement because a hash moved is the family of error ADR 0162 rules out. Deliberately reports **no `generated_at`**: R01's contract is determinism, so a clock is the one field it cannot carry.](<redacted>.md) | design / artist loop |
|
|
254
255
|
|
|
255
256
|
> ⚠️ **Numbering collision (known):** two ADRs share `0008`. The numbers were assigned in parallel sessions before the lock could be enforced. The files are kept under their existing filenames to preserve git history, but the table disambiguates them as `0008-a` / `0008-b`. New ADRs should resume at `0040` to avoid further collisions. (`0024` is also a real on-disk duplicate: the cloneable-repo/local-first-memory ADR and the multi-agent-system-architecture ADR were both numbered `0024` in parallel; both keep their filenames, disambiguated as `0024-a` / `0024-b` like `0008`. Likewise `0025` ended up with two on-disk files — the offsite-backup ADR and the structured-criterion↔task-link ADR — both kept under their existing filenames and disambiguated as `0025-a` / `0025-b`; separately, the BFG ADR had also claimed `0025` on 2026-05-30 and yielded to `0026`. A third happened 2026-06-06: `0036` was claimed in parallel by the builders-subdomain ADR ([#726](https://example.com/builders#/task/726)) and this Discord-channels ADR ([#727](https://example.com/builders#/task/727)); the Discord-channels ADR yielded to `0037`. A fourth landed 2026-06-10: `0044` was claimed in parallel by the palette-replacement ADR and the per-box game-preview ADR ([#897](https://example.com/builders#/task/897)); both keep their filenames, disambiguated as `0044-a` / `0044-b` like `0008`. A fifth was caught pre-land 2026-06-13: `0050` was claimed in parallel by the device-flow-admission ADR ([#1000](https://example.com/builders#/task/1000)) and the sandbox-for-everyone ADR ([#1001](https://example.com/builders#/task/1001)); the latter yielded to `0052` before merging, so there is no `0050` duplicate. A sixth was caught pre-land 2026-06-21: the tool-agnostic-design-layer ADR ([#1375](https://example.com/builders#/task/1375)) first collided at `0079` with the value-gated-autonomous-engagement ADR, then at `0080` with the llm-cache-api-transport ADR (both part of the 0075–0080 LLM-cost-reduction series, landed on `main` in parallel); it yielded to `0081` before merging, so there is no `0079`/`0080` design-layer duplicate. A seventh landed 2026-06-23: `0084` was claimed in parallel by the auto-derived-per-task-rank-floor ADR and the remove-default-spend-cap ADR (task 1499); the rank-floor ADR landed on `main` first, so the spend-cap ADR yielded to `0085`, leaving no `0084` duplicate. An eighth landed 2026-06-27: `0095` was claimed in parallel by the borrowed-memory-and-retrieval-concepts ADR (PR #459, landed first) and the cross-agent-context-management ADR ([#1651](https://example.com/builders#/task/1651), PR #460); both keep their filenames, disambiguated as `0095-a` / `0095-b` like `0008`. These two are companions — `0095-a` adopts the memory/retrieval concepts and explicitly defers the grep-vs-semantic question to `0095-b`.)
|