@bongos/core 1.19.373 → 1.19.374

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 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.373",
6
- "core_contract": "1.19.373",
7
- "source_commit": "fbe2ff6cdfb5154f956f0a473085ad2f55a9e7e2",
5
+ "core_version": "1.19.374",
6
+ "core_contract": "1.19.374",
7
+ "source_commit": "ef487914ca1cc1439cbbe614d215ac0bcc4a7a11",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-25T22:26:30.155Z",
9
+ "built_at": "2026-08-25T22:31:01.224Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 334,
13
13
  "agent_docs_stubbed": 22,
14
- "functional_verbatim": 1545,
14
+ "functional_verbatim": 1549,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1901,
20
- "tree_sha256": "028f3591a335092742ce9ca6791ae17cc69d690bbe3895e09e10742a648eb04e",
19
+ "file_count": 1905,
20
+ "tree_sha256": "7a78e601f7e02412f566e605aed571ee145c0daa03911d7f8ad188e9369fd34f",
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": "fc26e581e9ba06ec9fd798e982539f0bd8dc4c27a5f6c248b395f62214daaf78"
350
+ "sha256": "4855d865dbe79d1d6b9d774c8cbf480c36c6a3eae28392845850efb0689a6997"
351
351
  },
352
352
  {
353
353
  "path": "clients/bongos-client/bongos-client.global.js",
354
354
  "mode": "0000644",
355
- "sha256": "e50696e5b7b9c1abab280665dd57ab6f8f8883d3bfd0f2701a45ee7d9a6fb728"
355
+ "sha256": "e5bde7c338fc07f45f8f58dec2fa8826c0ea11d85c33146c9b6e7bdcc7049ba2"
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": "b13e62181adbc47a9ba6d3349a4abcb63fca123f3b0202414a1f7a2ba5e7d088"
365
+ "sha256": "8f3092af15067a6ed54092a10b4d43cd64eda7ae8fec0e50fe43de91428c9157"
366
366
  },
367
367
  {
368
368
  "path": "clients/bongos-client/index.d.ts",
369
369
  "mode": "0000644",
370
- "sha256": "bebe9282f7f60c62dc554c24bbec1fb7c9a8453e98015d5207c3b70260bcbb56"
370
+ "sha256": "70cc0227b4003bf47a6942656c8ce652c9884022cee81006c0c73e552eec6361"
371
371
  },
372
372
  {
373
373
  "path": "clients/bongos-client/index.mjs",
374
374
  "mode": "0000644",
375
- "sha256": "d4f2e6c3b376c35679416efcd30bbe54bae396cbaa8ee1e4078c56b6175da3d4"
375
+ "sha256": "819100f31cb468e82a0f2684a0e16b5ae1252cf91ef30b1923398af968ee5170"
376
376
  },
377
377
  {
378
378
  "path": "clients/bongos-client/package.json",
@@ -1472,12 +1472,12 @@
1472
1472
  {
1473
1473
  "path": "docs/api-reference.md",
1474
1474
  "mode": "0000644",
1475
- "sha256": "544b25f700d89d7f51dd84cb4faa740bc1c9bb4bf52853470e7d645040215033"
1475
+ "sha256": "5972f14220674de9bcb4fbcdbe6859f208555bf0adc3de70ccefd64544545344"
1476
1476
  },
1477
1477
  {
1478
1478
  "path": "docs/api/openapi.json",
1479
1479
  "mode": "0000644",
1480
- "sha256": "dbc90270a1754caf816ce9fde9cde35af21e977da08309f56c4d96dbf7cad9bf"
1480
+ "sha256": "342d1060be381a873d917c47037097e9f2561546900c4ca16986aed10913c553"
1481
1481
  },
1482
1482
  {
1483
1483
  "path": "docs/architecture.md",
@@ -1502,12 +1502,12 @@
1502
1502
  {
1503
1503
  "path": "docs/copy-inventory.md",
1504
1504
  "mode": "0000644",
1505
- "sha256": "23502315ff7f5e758ad3171a7bd28f8861ccb6640be93bd6a3a3f681c1ff0606"
1505
+ "sha256": "5805bd8d2f443b028c4f6934ce1619254d3e6dc47f2ebc3f24ced87f5614066f"
1506
1506
  },
1507
1507
  {
1508
1508
  "path": "docs/copy-registry.json",
1509
1509
  "mode": "0000644",
1510
- "sha256": "85ead9ad5f556a449495e0e7f7af09a29fb9e54e663ef3fbf9f1fe1c390bc8d6"
1510
+ "sha256": "11839e3afac4b57f3a230f32bfa004a08ed34d0315ec94096cd0f83949b104a5"
1511
1511
  },
1512
1512
  {
1513
1513
  "path": "docs/design-contract.md",
@@ -2609,6 +2609,11 @@
2609
2609
  "mode": "0000644",
2610
2610
  "sha256": "5d79d4f6f0e24b0ae9731a31ebabadc43faa3b6a423d31800b664960dc16145c"
2611
2611
  },
2612
+ {
2613
+ "path": "modules/builder-settings/profile-nudge-prefs.js",
2614
+ "mode": "0000644",
2615
+ "sha256": "26935726b1b388fb90793145d22f95641b2222a7b8ee29b2ed418bb2725f9fba"
2616
+ },
2612
2617
  {
2613
2618
  "path": "modules/builder-settings/render-prefs.js",
2614
2619
  "mode": "0000644",
@@ -2617,7 +2622,7 @@
2617
2622
  {
2618
2623
  "path": "modules/builder-settings/routes/settings.js",
2619
2624
  "mode": "0000644",
2620
- "sha256": "c5198a5fb915be1015eec09ca0b614c96d820fdf491cb56d1c735b8839a441c0"
2625
+ "sha256": "e1de49abd66d5dece54d5a794bb1af28469028903b022f3d7b1e2c453ea0b851"
2621
2626
  },
2622
2627
  {
2623
2628
  "path": "modules/builder-settings/settings.js",
@@ -2907,7 +2912,7 @@
2907
2912
  {
2908
2913
  "path": "modules/dev-box/app/src/vendor/bongos-client.cjs",
2909
2914
  "mode": "0000644",
2910
- "sha256": "b13e62181adbc47a9ba6d3349a4abcb63fca123f3b0202414a1f7a2ba5e7d088"
2915
+ "sha256": "8f3092af15067a6ed54092a10b4d43cd64eda7ae8fec0e50fe43de91428c9157"
2911
2916
  },
2912
2917
  {
2913
2918
  "path": "modules/dev-box/app/test/unit.mjs",
@@ -3557,7 +3562,7 @@
3557
3562
  {
3558
3563
  "path": "modules/hall-ui/public/goal-inbox.js",
3559
3564
  "mode": "0000644",
3560
- "sha256": "ede756a5c11c8c6a9eca4c841f2110a5e05293b0fbd6d34a5c90a05dc02943c4"
3565
+ "sha256": "71836d70e751ea8c7363c3217931a2cdfaeea38b770b90e564baf69cfe75f40f"
3561
3566
  },
3562
3567
  {
3563
3568
  "path": "modules/hall-ui/public/goals-lib.js",
@@ -3667,7 +3672,7 @@
3667
3672
  {
3668
3673
  "path": "modules/hall-ui/public/index.html",
3669
3674
  "mode": "0000644",
3670
- "sha256": "6564fe67e5987fbc25a4dd84d0288302ddcd81050e6afa1e5ab1b1dea873c0d8"
3675
+ "sha256": "78cb8c70e92e70d996a8f1694b7b01bf893f6aa3f210bc297e48cdd961b971ae"
3671
3676
  },
3672
3677
  {
3673
3678
  "path": "modules/hall-ui/public/md-reader.js",
@@ -3754,6 +3759,11 @@
3754
3759
  "mode": "0000644",
3755
3760
  "sha256": "40f6f44283aa7dba25a2c983d77b6e6f53f40eefef3394122d34f7f42fc5f676"
3756
3761
  },
3762
+ {
3763
+ "path": "modules/hall-ui/public/profile-nudge.js",
3764
+ "mode": "0000644",
3765
+ "sha256": "b86a8a2b12e30671cc00a4495fbc9789967d38622ce803f1ffc2d95ae45b7d03"
3766
+ },
3757
3767
  {
3758
3768
  "path": "modules/hall-ui/public/profile.html",
3759
3769
  "mode": "0000644",
@@ -4902,12 +4912,12 @@
4902
4912
  {
4903
4913
  "path": "package-lock.json",
4904
4914
  "mode": "0000644",
4905
- "sha256": "01e0ec5418014e61e5a0bdf1781ea84ee46c4c8bfc1b63b0e55bb6397ec06498"
4915
+ "sha256": "eb5cb1b57d8467f5d70ced6de47f1963a802fe5ae00bbaf960befcf5000efa75"
4906
4916
  },
4907
4917
  {
4908
4918
  "path": "package.json",
4909
4919
  "mode": "0000644",
4910
- "sha256": "f38add7db0b96f5b469c5127f7e06336ef5626edc93d61f797d3439e0ca51760"
4920
+ "sha256": "61856d92a07e11bb5aa53a7ab2be103c7cf31e555a616086f603fdea79485344"
4911
4921
  },
4912
4922
  {
4913
4923
  "path": "public-docs/index.html",
@@ -5587,7 +5597,7 @@
5587
5597
  {
5588
5598
  "path": "scripts/gds/fitness-checks-write-validation.js",
5589
5599
  "mode": "0000644",
5590
- "sha256": "7ee041817c06a56d4801f4367a618384ad856235a5034d3abdafa9f9344bff0b"
5600
+ "sha256": "7501680e8b657a1002c185750951f16af1b2e7e30c04c72a3f03a94d286e02c4"
5591
5601
  },
5592
5602
  {
5593
5603
  "path": "scripts/gds/fitness-lib.js",
@@ -6257,7 +6267,7 @@
6257
6267
  {
6258
6268
  "path": "scripts/gds/ship-land.js",
6259
6269
  "mode": "0000644",
6260
- "sha256": "e091557b4cc29c081cb82f9cd650b4c110f1dede4e72b704893f8397cd7bbaf2"
6270
+ "sha256": "72efd4268060fb5ddf8a176d91baf01353dc1e82594d70feef029c45f748d8f9"
6261
6271
  },
6262
6272
  {
6263
6273
  "path": "scripts/gds/ship-memory-sync.js",
@@ -6792,7 +6802,7 @@
6792
6802
  {
6793
6803
  "path": "src/module-api.js",
6794
6804
  "mode": "0000644",
6795
- "sha256": "88ea06ed6759765c78ff0fc67ddd0a65041e7b8fda60e0f18b5809c44107ad7d"
6805
+ "sha256": "abace193194919c4b75a9c461890b24be8e8c06093311c956c91894305980725"
6796
6806
  },
6797
6807
  {
6798
6808
  "path": "src/module-loader/catalog.js",
@@ -8704,6 +8714,11 @@
8704
8714
  "mode": "0000644",
8705
8715
  "sha256": "ff73579dd2416a81648d85dcd5d1a77cb776c32047bd0cc7897dff132cbae5b7"
8706
8716
  },
8717
+ {
8718
+ "path": "tests/profile_nudge_prefs.mjs",
8719
+ "mode": "0000644",
8720
+ "sha256": "a7f70fa612494593438565f4bd1e3d454a3dbca862c8396a7dcb252d38e3e489"
8721
+ },
8707
8722
  {
8708
8723
  "path": "tests/profile_rollup_consent.mjs",
8709
8724
  "mode": "0000644",
@@ -9219,6 +9234,11 @@
9219
9234
  "mode": "0000644",
9220
9235
  "sha256": "5c0d5961408ac202eb53fe648c4c8b00e5552b231b35f39a39e006b705cb34f1"
9221
9236
  },
9237
+ {
9238
+ "path": "tests/ship_push_diagnosis.mjs",
9239
+ "mode": "0000644",
9240
+ "sha256": "d69bad6c8b3a01434b4fd5230f2b781bbe7fd9938df3d96b2a4ef34906287e54"
9241
+ },
9222
9242
  {
9223
9243
  "path": "tests/ship_resume.mjs",
9224
9244
  "mode": "0000644",
@@ -9292,7 +9312,7 @@
9292
9312
  {
9293
9313
  "path": "tests/sound_player.mjs",
9294
9314
  "mode": "0000644",
9295
- "sha256": "42398e730349be5f10e07eb3ec42aa47c9a024371f2f95f96802b990ff0e6f80"
9315
+ "sha256": "5b42ae4236e203c69feb3b8885ebec3a5f2b0d7f6880128536dfe5b8088fd4e8"
9296
9316
  },
9297
9317
  {
9298
9318
  "path": "tests/sound_prefs.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
- - 318 operations across 52 resource groups
8
+ - 320 operations across 52 resource groups
9
9
 
10
10
  ## Install
11
11
 
@@ -501,6 +501,10 @@ function createClient(opts = {}) {
501
501
  patchMeModelAllocation: (args) => request("PATCH", "/me/model-allocation", { hasBody: true }, args),
502
502
  // PATCH /me/profile — rank: any-builder — PATCH /me/profile
503
503
  patchMeProfile: (args) => request("PATCH", "/me/profile", { hasBody: true }, args),
504
+ // GET /me/profile-nudge — rank: any-builder — GET /me/profile-nudge
505
+ getMeProfileNudge: (args) => request("GET", "/me/profile-nudge", { hasBody: false }, args),
506
+ // PATCH /me/profile-nudge — rank: any-builder — PATCH /me/profile-nudge
507
+ patchMeProfileNudge: (args) => request("PATCH", "/me/profile-nudge", { hasBody: true }, args),
504
508
  // GET /me/recent-ships — rank: any-builder — GET /me/recent-ships
505
509
  getMeRecentShips: (args) => request("GET", "/me/recent-ships", { hasBody: false }, args),
506
510
  // GET /me/render — rank: any-builder — GET /me/render
@@ -500,6 +500,10 @@ function createClient(opts = {}) {
500
500
  patchMeModelAllocation: (args) => request("PATCH", "/me/model-allocation", { hasBody: true }, args),
501
501
  // PATCH /me/profile — rank: any-builder — PATCH /me/profile
502
502
  patchMeProfile: (args) => request("PATCH", "/me/profile", { hasBody: true }, args),
503
+ // GET /me/profile-nudge — rank: any-builder — GET /me/profile-nudge
504
+ getMeProfileNudge: (args) => request("GET", "/me/profile-nudge", { hasBody: false }, args),
505
+ // PATCH /me/profile-nudge — rank: any-builder — PATCH /me/profile-nudge
506
+ patchMeProfileNudge: (args) => request("PATCH", "/me/profile-nudge", { hasBody: true }, args),
503
507
  // GET /me/recent-ships — rank: any-builder — GET /me/recent-ships
504
508
  getMeRecentShips: (args) => request("GET", "/me/recent-ships", { hasBody: false }, args),
505
509
  // GET /me/render — rank: any-builder — GET /me/render
@@ -787,6 +787,10 @@ export interface BongosClient {
787
787
  patchMeModelAllocation(args?: RequestArgs): Promise<ApiResponse>;
788
788
  /** PATCH /me/profile — rank: any-builder */
789
789
  patchMeProfile(args?: RequestArgs & { body?: PatchMeProfileRequest }): Promise<PatchMeProfileResponse>;
790
+ /** GET /me/profile-nudge — rank: any-builder */
791
+ getMeProfileNudge(args?: RequestArgs): Promise<ApiResponse>;
792
+ /** PATCH /me/profile-nudge — rank: any-builder */
793
+ patchMeProfileNudge(args?: RequestArgs): Promise<ApiResponse>;
790
794
  /** GET /me/recent-ships — rank: any-builder */
791
795
  getMeRecentShips(args?: RequestArgs): Promise<GetMeRecentShipsResponse>;
792
796
  /** GET /me/render — rank: any-builder */
@@ -497,6 +497,10 @@ export function createClient(opts = {}) {
497
497
  patchMeModelAllocation: (args) => request("PATCH", "/me/model-allocation", { hasBody: true }, args),
498
498
  // PATCH /me/profile — rank: any-builder — PATCH /me/profile
499
499
  patchMeProfile: (args) => request("PATCH", "/me/profile", { hasBody: true }, args),
500
+ // GET /me/profile-nudge — rank: any-builder — GET /me/profile-nudge
501
+ getMeProfileNudge: (args) => request("GET", "/me/profile-nudge", { hasBody: false }, args),
502
+ // PATCH /me/profile-nudge — rank: any-builder — PATCH /me/profile-nudge
503
+ patchMeProfileNudge: (args) => request("PATCH", "/me/profile-nudge", { hasBody: true }, args),
500
504
  // GET /me/recent-ships — rank: any-builder — GET /me/recent-ships
501
505
  getMeRecentShips: (args) => request("GET", "/me/recent-ships", { hasBody: false }, args),
502
506
  // GET /me/render — rank: any-builder — GET /me/render
@@ -8204,6 +8204,79 @@
8204
8204
  ]
8205
8205
  }
8206
8206
  },
8207
+ "/me/profile-nudge": {
8208
+ "get": {
8209
+ "operationId": "get_me_profile_nudge",
8210
+ "tags": [
8211
+ "me"
8212
+ ],
8213
+ "summary": "GET /me/profile-nudge",
8214
+ "description": "The \"Complete your profile\" nudge dismissal (task 1002802 / P06). The nudge itself is a DISMISSIBLE line item in the hall's needs-attention inbox — the replacement for the profile-setup wizard Decision 2 rejected. These two routes own only the dismissal FLAG; whether anything is actually missing is composed by the caller, which already holds both inputs (preferred_disciplines from GET /me, bio from GET /me/cross-project — bio lives on the platform identity account, and this module must never reach across to that one). rank: any-builder — own profile config; same posture as /me/render.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8215
+ "x-rank": "any-builder",
8216
+ "x-source": "modules/builder-settings/routes/settings.js",
8217
+ "responses": {
8218
+ "200": {
8219
+ "description": "Success."
8220
+ },
8221
+ "400": {
8222
+ "$ref": "#/components/responses/BadRequest"
8223
+ },
8224
+ "401": {
8225
+ "$ref": "#/components/responses/Unauthorized"
8226
+ },
8227
+ "403": {
8228
+ "$ref": "#/components/responses/Forbidden"
8229
+ }
8230
+ },
8231
+ "security": [
8232
+ {
8233
+ "builderSession": []
8234
+ }
8235
+ ]
8236
+ },
8237
+ "patch": {
8238
+ "operationId": "patch_me_profile_nudge",
8239
+ "tags": [
8240
+ "me"
8241
+ ],
8242
+ "summary": "PATCH /me/profile-nudge",
8243
+ "description": "PATCH the dismissal. Body: { dismissed: <bool> | null } — null clears the override so the nudge can come back (the undismiss path). Sibling behavior keys are preserved by applyPatch.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8244
+ "x-rank": "any-builder",
8245
+ "x-source": "modules/builder-settings/routes/settings.js",
8246
+ "requestBody": {
8247
+ "required": false,
8248
+ "content": {
8249
+ "application/json": {
8250
+ "schema": {
8251
+ "type": "object",
8252
+ "additionalProperties": true
8253
+ }
8254
+ }
8255
+ },
8256
+ "x-validated": false,
8257
+ "x-body-undocumented": true
8258
+ },
8259
+ "responses": {
8260
+ "200": {
8261
+ "description": "Success."
8262
+ },
8263
+ "400": {
8264
+ "$ref": "#/components/responses/BadRequest"
8265
+ },
8266
+ "401": {
8267
+ "$ref": "#/components/responses/Unauthorized"
8268
+ },
8269
+ "403": {
8270
+ "$ref": "#/components/responses/Forbidden"
8271
+ }
8272
+ },
8273
+ "security": [
8274
+ {
8275
+ "builderSession": []
8276
+ }
8277
+ ]
8278
+ }
8279
+ },
8207
8280
  "/me/recent-ships": {
8208
8281
  "get": {
8209
8282
  "operationId": "get_me_recent_ships",
@@ -20079,9 +20152,9 @@
20079
20152
  "description": "A required dependency/feature is not configured or is temporarily down."
20080
20153
  }
20081
20154
  },
20082
- "x-endpoint-count": 318,
20155
+ "x-endpoint-count": 320,
20083
20156
  "x-schema-count": 353,
20084
- "x-undocumented-bodies": 8,
20157
+ "x-undocumented-bodies": 9,
20085
20158
  "x-response-schemas": 237,
20086
20159
  "x-generated-by": "scripts/gds/gen-api-docs.js"
20087
20160
  }
@@ -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. 318 endpoints across 52 route files.
5
+ > **Generated from the live route files** — the route file is authoritative. 320 endpoints across 52 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`.
@@ -327,7 +327,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
327
327
  | POST | `/api/bongos/llm-cache/lookup` | `metic+archon` | `cache_key`, `tokens_saved_est` | POST /llm-cache/lookup — hit/miss + atomic hit_count bump. |
328
328
  | POST | `/api/bongos/llm-cache/store` | `metic+archon` | `spec`, `result_stdout`, `result_cost_usd` | POST /llm-cache/store — upsert a freshly-produced result. |
329
329
 
330
- ## `me` (25)
330
+ ## `me` (27)
331
331
 
332
332
  | Method | Path | Rank | Body | Description |
333
333
  |---|---|---|---|---|
@@ -347,6 +347,8 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
347
347
  | GET | `/api/bongos/me/model-allocation` | `any-builder` | — | |
348
348
  | PATCH | `/api/bongos/me/model-allocation` | `any-builder` | _undocumented_ | PATCH the model allocation. |
349
349
  | PATCH | `/api/bongos/me/profile` | `any-builder` | `display_name`, `bio`, `links` | the caller's OWN profile fields (P02, task 1002798; spec Decision 4): bio, typed links, account display name. |
350
+ | GET | `/api/bongos/me/profile-nudge` | `any-builder` | — | The "Complete your profile" nudge dismissal (task 1002802 / P06). |
351
+ | PATCH | `/api/bongos/me/profile-nudge` | `any-builder` | _undocumented_ | PATCH the dismissal. |
350
352
  | GET | `/api/bongos/me/recent-ships` | `any-builder` | — | Tasks this builder has shipped, most recent first. |
351
353
  | GET | `/api/bongos/me/render` | `any-builder` | — | Per-builder "render widgets" knob (task 1279, shares the behavior_prefs jsonb / migration 119). |
352
354
  | PATCH | `/api/bongos/me/render` | `any-builder` | _undocumented_ | PATCH the render knob. |
@@ -587,5 +589,5 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
587
589
 
588
590
  ---
589
591
 
590
- > 8 write endpoint(s) take a JSON body with no declared validator schema — their fields are not yet machine-documented. Adding a `validateOrRespond` schema (`src/bongos/routes/_helpers.js`) surfaces them here automatically.
592
+ > 9 write endpoint(s) take a JSON body with no declared validator schema — their fields are not yet machine-documented. Adding a `validateOrRespond` schema (`src/bongos/routes/_helpers.js`) surfaces them here automatically.
591
593
 
@@ -4,13 +4,13 @@
4
4
  > Deterministic on a given tree — regenerate and commit rather than hand-editing.
5
5
  > Machine-readable twin: [`copy-registry.json`](copy-registry.json).
6
6
 
7
- **1400 strings** across 3 surfaces · 1241 distinct pieces of copy · 1400 certain, 0 heuristic · 129 shown in more than one place.
7
+ **1403 strings** across 3 surfaces · 1244 distinct pieces of copy · 1403 certain, 0 heuristic · 129 shown in more than one place.
8
8
 
9
9
  ## Surfaces
10
10
 
11
11
  | surface | lives at | files | strings |
12
12
  |---|---|---|---|
13
- | `builders-hall` | `modules/hall-ui/public` | 70 | 1110 |
13
+ | `builders-hall` | `modules/hall-ui/public` | 71 | 1113 |
14
14
  | `status-dashboard` | `modules/status-ui/public` | 4 | 98 |
15
15
  | `landing` | `public-landing` | 2 | 192 |
16
16
 
@@ -78,7 +78,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
78
78
  | `3837f6d586a3` | 2x | Copy the task prompt to your clipboard | modules/hall-ui/public/goals-render.js:266, modules/hall-ui/public/work.js:362 |
79
79
  | … | | _89 more in the JSON registry_ | |
80
80
 
81
- ## `builders-hall` — 1110 strings
81
+ ## `builders-hall` — 1113 strings
82
82
 
83
83
  | id | conf | text | where |
84
84
  |---|---|---|---|
@@ -136,6 +136,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
136
136
  | `303f8d90266f` | certain | Achievements could not be loaded. | `modules/hall-ui/public/builders.js:1136` |
137
137
  | `6ca31dd5eb91` | certain | Acknowledge the primer | `modules/hall-ui/public/primer.html:65` |
138
138
  | `4461b13f95c2` | certain | Actions apply at once. Closing stops and deletes the box; you can start a fresh one anytime. | `modules/hall-ui/public/settings.html:162` |
139
+ | `62eded408097` | certain | Add {…} | `modules/hall-ui/public/profile-nudge.js:68` |
139
140
  | `7cf0163d4cde` | certain | Aggregate totals across every recorded work session. | `modules/hall-ui/public/sessions.html:35` |
140
141
  | `8b736e365191` | certain | All builders | `modules/hall-ui/public/hall-render.js:998` |
141
142
  | `b5e03617b319` | certain | All goals | `modules/hall-ui/public/goals.html:41` |
@@ -238,6 +239,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
238
239
  | `530d83153e17` | certain | Codebase detail | `modules/hall-ui/public/atlas.html:41` |
239
240
  | `c72c97dc33fe` | certain | Collab — {{worldName}} | `modules/hall-ui/public/collab.html:6` |
240
241
  | `c72c97dc33fe` | certain | Collab — {{worldName}} | `modules/hall-ui/public/collab.html:6` |
242
+ | `cfc287c42263` | certain | Complete your profile | `modules/hall-ui/public/profile-nudge.js:62` |
241
243
  | `2867c85d196d` | certain | Confirm that verification for #{…} actually passed? | `modules/hall-ui/public/task.js:327` |
242
244
  | `9b16a22bf24c` | certain | Connect Discord &amp; join the server | `modules/hall-ui/public/hall-render.js:814` |
243
245
  | `9b16a22bf24c` | certain | Connect Discord &amp; join the server | `modules/hall-ui/public/settings.js:826` |
@@ -770,6 +772,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
770
772
  | `14d32a35c15f` | certain | Show my cross-project record | `modules/hall-ui/public/settings.js:579` |
771
773
  | `dc0f9fe50a65` | certain | Show the setup guide again | `modules/hall-ui/public/index.html:55` |
772
774
  | `2af4c0f2b4b9` | certain | Showing {…} of {…}. | `modules/hall-ui/public/ideas.js:369` |
775
+ | `838b17fa8d93` | certain | Shown on your public profile — it helps others know what you work on. | `modules/hall-ui/public/profile-nudge.js:69` |
773
776
  | `2f12d46f96ca` | certain | Sign in from the app. | `modules/hall-ui/public/hall-render.js:609` |
774
777
  | `19ab56cc3932` | certain | Sign in to filter to your goals | `modules/hall-ui/public/goals-page.js:821` |
775
778
  | `b057dfbf49c5` | certain | Sign in when Claude prompts you. | `modules/hall-ui/public/hall-render.js:609` |
@@ -3,17 +3,17 @@
3
3
  "generator": "scripts/gds/copy-inventory.js",
4
4
  "goal": 1000074,
5
5
  "counts": {
6
- "strings": 1400,
7
- "unique_ids": 1241,
8
- "certain": 1400,
6
+ "strings": 1403,
7
+ "unique_ids": 1244,
8
+ "certain": 1403,
9
9
  "heuristic": 0,
10
10
  "duplicate_ids": 129
11
11
  },
12
12
  "surfaces": {
13
13
  "builders-hall": {
14
14
  "at": "modules/hall-ui/public",
15
- "files": 70,
16
- "strings": 1110
15
+ "files": 71,
16
+ "strings": 1113
17
17
  },
18
18
  "status-dashboard": {
19
19
  "at": "modules/status-ui/public",
@@ -1846,6 +1846,15 @@
1846
1846
  "origin": "html-text",
1847
1847
  "confidence": "certain"
1848
1848
  },
1849
+ {
1850
+ "id": "62eded408097",
1851
+ "surface": "builders-hall",
1852
+ "text": "Add {…}",
1853
+ "file": "modules/hall-ui/public/profile-nudge.js",
1854
+ "line": 68,
1855
+ "origin": "js-markup",
1856
+ "confidence": "certain"
1857
+ },
1849
1858
  {
1850
1859
  "id": "7cf0163d4cde",
1851
1860
  "surface": "builders-hall",
@@ -2764,6 +2773,15 @@
2764
2773
  "origin": "html-title",
2765
2774
  "confidence": "certain"
2766
2775
  },
2776
+ {
2777
+ "id": "cfc287c42263",
2778
+ "surface": "builders-hall",
2779
+ "text": "Complete your profile",
2780
+ "file": "modules/hall-ui/public/profile-nudge.js",
2781
+ "line": 62,
2782
+ "origin": "js-markup",
2783
+ "confidence": "certain"
2784
+ },
2767
2785
  {
2768
2786
  "id": "2867c85d196d",
2769
2787
  "surface": "builders-hall",
@@ -7552,6 +7570,15 @@
7552
7570
  "origin": "js-markup",
7553
7571
  "confidence": "certain"
7554
7572
  },
7573
+ {
7574
+ "id": "838b17fa8d93",
7575
+ "surface": "builders-hall",
7576
+ "text": "Shown on your public profile — it helps others know what you work on.",
7577
+ "file": "modules/hall-ui/public/profile-nudge.js",
7578
+ "line": 69,
7579
+ "origin": "js-markup",
7580
+ "confidence": "certain"
7581
+ },
7555
7582
  {
7556
7583
  "id": "2f12d46f96ca",
7557
7584
  "surface": "builders-hall",
@@ -0,0 +1,125 @@
1
+ // modules/builder-settings/profile-nudge-prefs.js — the "Complete your profile"
2
+ // nudge dismissal (task 1002802 / P06, goal 1000045).
3
+ //
4
+ // WHY A KNOB AND NOT A WIZARD. Decision 2 of the builder-profile spec
5
+ // (docs/specs/bongos-v1-builder-profile-page.md) rejected a profile-setup
6
+ // wizard: profiles are auto-created at first sign-in and enriched
7
+ // progressively, and an interstitial between sign-in and the hall adds friction
8
+ // at exactly the drop-off point. The replacement is a DISMISSIBLE line item in
9
+ // the needs-attention inbox — the hall's placement rule (modules/hall-ui/
10
+ // CLAUDE.md → Placement: an action awaiting the builder is an inbox line, never
11
+ // a new Home card).
12
+ //
13
+ // Dismissible has to mean dismissed-for-good, across devices: a builder who has
14
+ // decided not to write a bio should not be asked again on their laptop. So the
15
+ // flag is SERVER-side, not localStorage.
16
+ //
17
+ // Storage: one reserved key ('profile_nudge_dismissed') inside the
18
+ // `behavior_prefs` jsonb on `builders` (migration 119 — the same blob the
19
+ // wandering/render/interaction knobs use), so this adds NO migration. Value is
20
+ // a scalar boolean; a missing key means "not dismissed", which keeps the column
21
+ // sparse. applyPatch leaves sibling keys alone.
22
+ //
23
+ // This module is the single source of truth: GET/PATCH /api/gds/me/profile-nudge
24
+ // read and write it, and the hall's attention-card slice reads the resolved
25
+ // boolean to decide whether to render the line at all.
26
+
27
+ const RESERVED_KEY = 'profile_nudge_dismissed';
28
+ const DEFAULT_DISMISSED = false; // never dismissed until the builder says so
29
+
30
+ // ---- pure helpers ----
31
+
32
+ // The raw override a builder has stored, or null when unset/invalid. Kept
33
+ // separate from the effective value so a caller can tell "chose to keep it" from
34
+ // "never answered" (mirrors render-prefs' overrideEnabled).
35
+ function overrideDismissed(behaviorPrefs) {
36
+ const raw = behaviorPrefs && behaviorPrefs[RESERVED_KEY];
37
+ return typeof raw === 'boolean' ? raw : null;
38
+ }
39
+
40
+ // The resolved boolean — the value the hall acts on.
41
+ function nudgeDismissed(behaviorPrefs) {
42
+ const o = overrideDismissed(behaviorPrefs);
43
+ return o === null ? DEFAULT_DISMISSED : o;
44
+ }
45
+
46
+ // Which profile fields are still empty. PURE over the two inputs the nudge
47
+ // cares about, so the decision is testable without a DB or an HTTP round-trip.
48
+ // `bio` lives on the platform-identity account (hub-side), `preferred_disciplines`
49
+ // on `builders` — the caller passes both; this file never reaches for either.
50
+ // Whitespace-only bio counts as empty: a space is not a bio.
51
+ function missingProfileFields({ bio, preferredDisciplines } = {}) {
52
+ const missing = [];
53
+ if (typeof bio !== 'string' || bio.trim() === '') missing.push('bio');
54
+ if (!Array.isArray(preferredDisciplines) || preferredDisciplines.length === 0) missing.push('disciplines');
55
+ return missing;
56
+ }
57
+
58
+ // Should the nudge render? Only when something is actually missing AND the
59
+ // builder has not dismissed it. The two halves are deliberately separate above
60
+ // so a caller can say WHAT is missing even when the nudge is dismissed.
61
+ function shouldNudge({ behaviorPrefs, bio, preferredDisciplines } = {}) {
62
+ if (nudgeDismissed(behaviorPrefs)) return false;
63
+ return missingProfileFields({ bio, preferredDisciplines }).length > 0;
64
+ }
65
+
66
+ // Everything the API + the hall slice need in one object.
67
+ function describeForApi(behaviorPrefs, { bio, preferredDisciplines } = {}) {
68
+ const missing = missingProfileFields({ bio, preferredDisciplines });
69
+ return {
70
+ dismissed: nudgeDismissed(behaviorPrefs),
71
+ default_dismissed: DEFAULT_DISMISSED,
72
+ override: overrideDismissed(behaviorPrefs),
73
+ missing,
74
+ show: !nudgeDismissed(behaviorPrefs) && missing.length > 0,
75
+ };
76
+ }
77
+
78
+ // Validate a PATCH body: { dismissed: <bool> | null }. `null` clears the
79
+ // override (back to not-dismissed, so the nudge can return — the "undismiss"
80
+ // path a Settings toggle would use). Unknown keys / non-booleans → 422. An empty
81
+ // body is a valid no-op.
82
+ function validatePatch(body) {
83
+ if (body == null || typeof body !== 'object' || Array.isArray(body)) {
84
+ return { ok: false, errors: [{ error: 'body_must_be_object' }] };
85
+ }
86
+ const errors = [];
87
+ const patch = {};
88
+ for (const [key, value] of Object.entries(body)) {
89
+ if (key !== 'dismissed') {
90
+ errors.push({ key, error: 'unknown_key', allowed: ['dismissed'] });
91
+ continue;
92
+ }
93
+ if (value === null || typeof value === 'boolean') {
94
+ patch[RESERVED_KEY] = value; // null = clear → default (not dismissed)
95
+ continue;
96
+ }
97
+ errors.push({ key, error: 'invalid_value', value, allowed: 'boolean or null' });
98
+ }
99
+ if (errors.length > 0) return { ok: false, errors };
100
+ return { ok: true, patch };
101
+ }
102
+
103
+ // Apply a validated patch to a builder's existing behavior_prefs. Pure. null
104
+ // deletes the key; a boolean overwrites. Sibling keys (wandering, widgets,
105
+ // interaction) are left untouched.
106
+ function applyPatch(existing, patch) {
107
+ const next = { ...(existing || {}) };
108
+ for (const [key, value] of Object.entries(patch)) {
109
+ if (value === null) delete next[key];
110
+ else next[key] = value;
111
+ }
112
+ return next;
113
+ }
114
+
115
+ module.exports = {
116
+ RESERVED_KEY,
117
+ DEFAULT_DISMISSED,
118
+ overrideDismissed,
119
+ nudgeDismissed,
120
+ missingProfileFields,
121
+ shouldNudge,
122
+ describeForApi,
123
+ validatePatch,
124
+ applyPatch,
125
+ };