@bongos/core 1.19.169 → 1.19.170
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 +29 -24
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +7 -0
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/api/openapi.json +126 -3
- package/docs/api-reference.md +4 -2
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +4 -0
- package/modules/hall-ui/public/settings.html +14 -0
- package/modules/hall-ui/public/settings.js +84 -0
- package/modules/platform-identity/module.json +1 -1
- package/modules/platform-identity/platform-identity.js +21 -0
- package/modules/platform-identity/routes/profile.js +77 -0
- package/modules/platform-identity/tests/platform-identity.mjs +22 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +3 -1
- package/tests/profile_rollup_consent.mjs +25 -0
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.170",
|
|
6
|
+
"core_contract": "1.19.170",
|
|
7
|
+
"source_commit": "2fd8009b960bd910fe95b3169d504adc7fe309d1",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-16T18:
|
|
9
|
+
"built_at": "2026-08-16T18:45:10.150Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 305,
|
|
13
13
|
"agent_docs_stubbed": 21,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1268,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1594,
|
|
20
|
+
"tree_sha256": "938326e2e51b3c2bf0a7ab516c8444005ec95028f247df10cc55bedae67b099f",
|
|
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": "d6d6163164f98c96a8e25dfe84825669bf8324f1f1b98adb0674781ad235ad10"
|
|
351
351
|
},
|
|
352
352
|
{
|
|
353
353
|
"path": "clients/bongos-client/bongos-client.global.js",
|
|
354
354
|
"mode": "0000644",
|
|
355
|
-
"sha256": "
|
|
355
|
+
"sha256": "386706d0ef3d09c5e074bdc45bfc0de869d732eb1cb2a2280472f02f797f2b74"
|
|
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": "7d462b5b80c9db9159981406398fc62c5ba71043c6a3b9433852d06d612bf2b0"
|
|
366
366
|
},
|
|
367
367
|
{
|
|
368
368
|
"path": "clients/bongos-client/index.d.ts",
|
|
369
369
|
"mode": "0000644",
|
|
370
|
-
"sha256": "
|
|
370
|
+
"sha256": "4acb1dde6f894ba37cff7e27e0110f12ae30c7cb6e4d972d6e85d2877489b6d0"
|
|
371
371
|
},
|
|
372
372
|
{
|
|
373
373
|
"path": "clients/bongos-client/index.mjs",
|
|
374
374
|
"mode": "0000644",
|
|
375
|
-
"sha256": "
|
|
375
|
+
"sha256": "4c7679ef258644ab12db1b8fe412b388f96f4296c6aae45d6ab637a8f3b9a037"
|
|
376
376
|
},
|
|
377
377
|
{
|
|
378
378
|
"path": "clients/bongos-client/package.json",
|
|
@@ -1347,12 +1347,12 @@
|
|
|
1347
1347
|
{
|
|
1348
1348
|
"path": "docs/api-reference.md",
|
|
1349
1349
|
"mode": "0000644",
|
|
1350
|
-
"sha256": "
|
|
1350
|
+
"sha256": "ccf59372e0456f36d83154d6dff4dbf5382e8d69023bee82b318b8e9cfb2c50b"
|
|
1351
1351
|
},
|
|
1352
1352
|
{
|
|
1353
1353
|
"path": "docs/api/openapi.json",
|
|
1354
1354
|
"mode": "0000644",
|
|
1355
|
-
"sha256": "
|
|
1355
|
+
"sha256": "4deac83a1d7c12e1a24109e856d9a6fef6b539afafdca55a525ab6786fe4e235"
|
|
1356
1356
|
},
|
|
1357
1357
|
{
|
|
1358
1358
|
"path": "docs/architecture.md",
|
|
@@ -2662,7 +2662,7 @@
|
|
|
2662
2662
|
{
|
|
2663
2663
|
"path": "modules/dev-box/app/src/vendor/bongos-client.cjs",
|
|
2664
2664
|
"mode": "0000644",
|
|
2665
|
-
"sha256": "
|
|
2665
|
+
"sha256": "7d462b5b80c9db9159981406398fc62c5ba71043c6a3b9433852d06d612bf2b0"
|
|
2666
2666
|
},
|
|
2667
2667
|
{
|
|
2668
2668
|
"path": "modules/dev-box/app/test/unit.mjs",
|
|
@@ -3382,12 +3382,12 @@
|
|
|
3382
3382
|
{
|
|
3383
3383
|
"path": "modules/hall-ui/public/settings.html",
|
|
3384
3384
|
"mode": "0000644",
|
|
3385
|
-
"sha256": "
|
|
3385
|
+
"sha256": "ae68a26d5d37ac75968ac11335b7062a448798cb3af0e0a4f81963f3382f204d"
|
|
3386
3386
|
},
|
|
3387
3387
|
{
|
|
3388
3388
|
"path": "modules/hall-ui/public/settings.js",
|
|
3389
3389
|
"mode": "0000644",
|
|
3390
|
-
"sha256": "
|
|
3390
|
+
"sha256": "2b69d258218e92b9fe2922496632e43d4bdb81f641f09469f5f6df85494a9fca"
|
|
3391
3391
|
},
|
|
3392
3392
|
{
|
|
3393
3393
|
"path": "modules/hall-ui/public/shell.js",
|
|
@@ -3792,12 +3792,12 @@
|
|
|
3792
3792
|
{
|
|
3793
3793
|
"path": "modules/platform-identity/module.json",
|
|
3794
3794
|
"mode": "0000644",
|
|
3795
|
-
"sha256": "
|
|
3795
|
+
"sha256": "e993fb26e61249bf89c0b0a5977c60e9d974810ffe1b67e94d909e8b52e00d5e"
|
|
3796
3796
|
},
|
|
3797
3797
|
{
|
|
3798
3798
|
"path": "modules/platform-identity/platform-identity.js",
|
|
3799
3799
|
"mode": "0000644",
|
|
3800
|
-
"sha256": "
|
|
3800
|
+
"sha256": "96907af1af78c472118ecf5ed9f269d6d2ce940b09535aae3b3df4de4d2337c1"
|
|
3801
3801
|
},
|
|
3802
3802
|
{
|
|
3803
3803
|
"path": "modules/platform-identity/pollers/code-sweep.js",
|
|
@@ -3814,6 +3814,11 @@
|
|
|
3814
3814
|
"mode": "0000644",
|
|
3815
3815
|
"sha256": "7fdb5a107e1ad84214c0d07507a7fd14b9234c9907224fddc0f6a70c850f460a"
|
|
3816
3816
|
},
|
|
3817
|
+
{
|
|
3818
|
+
"path": "modules/platform-identity/routes/profile.js",
|
|
3819
|
+
"mode": "0000644",
|
|
3820
|
+
"sha256": "6ea29b4f07417fa925b7987dd7fbe8a0262b7fec83ceffade9fccbc220289bba"
|
|
3821
|
+
},
|
|
3817
3822
|
{
|
|
3818
3823
|
"path": "modules/platform-identity/routes/projects.js",
|
|
3819
3824
|
"mode": "0000644",
|
|
@@ -3832,7 +3837,7 @@
|
|
|
3832
3837
|
{
|
|
3833
3838
|
"path": "modules/platform-identity/tests/platform-identity.mjs",
|
|
3834
3839
|
"mode": "0000644",
|
|
3835
|
-
"sha256": "
|
|
3840
|
+
"sha256": "18bf8d0cb42d76bc5413c760b13837fe439a6e719b88a84db96f143b829e67d5"
|
|
3836
3841
|
},
|
|
3837
3842
|
{
|
|
3838
3843
|
"path": "modules/platform-identity/ui/.gitkeep",
|
|
@@ -4207,12 +4212,12 @@
|
|
|
4207
4212
|
{
|
|
4208
4213
|
"path": "package-lock.json",
|
|
4209
4214
|
"mode": "0000644",
|
|
4210
|
-
"sha256": "
|
|
4215
|
+
"sha256": "0b596f97bbd28b6ddfda4a9265effcb0ea8711a4bb57948c35b80d7cf79c23d9"
|
|
4211
4216
|
},
|
|
4212
4217
|
{
|
|
4213
4218
|
"path": "package.json",
|
|
4214
4219
|
"mode": "0000644",
|
|
4215
|
-
"sha256": "
|
|
4220
|
+
"sha256": "ae9625129bd03cb29845f65e1be3abd91c48259d85a9c23095370e43a0a0e8aa"
|
|
4216
4221
|
},
|
|
4217
4222
|
{
|
|
4218
4223
|
"path": "public-docs/index.html",
|
|
@@ -5817,7 +5822,7 @@
|
|
|
5817
5822
|
{
|
|
5818
5823
|
"path": "src/module-api.js",
|
|
5819
5824
|
"mode": "0000644",
|
|
5820
|
-
"sha256": "
|
|
5825
|
+
"sha256": "28605499539ca63e000ac2fc52af1a3d75dbb2fd947846426619a189a2afd92b"
|
|
5821
5826
|
},
|
|
5822
5827
|
{
|
|
5823
5828
|
"path": "src/module-loader/catalog.js",
|
|
@@ -7377,7 +7382,7 @@
|
|
|
7377
7382
|
{
|
|
7378
7383
|
"path": "tests/profile_rollup_consent.mjs",
|
|
7379
7384
|
"mode": "0000644",
|
|
7380
|
-
"sha256": "
|
|
7385
|
+
"sha256": "b926ffa5d4e96ef223e4057ecc3e64d678ca1e16ec4afe79cb90cbf3e35e65e0"
|
|
7381
7386
|
},
|
|
7382
7387
|
{
|
|
7383
7388
|
"path": "tests/profile_route.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
|
+
- 273 operations across 45 resource groups
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -409,6 +409,10 @@ function createClient(opts = {}) {
|
|
|
409
409
|
getMeAchievements: (args) => request("GET", "/me/achievements", { hasBody: false }, args),
|
|
410
410
|
// POST /me/anthropic-link — rank: any-builder — POST /me/anthropic-link
|
|
411
411
|
postMeAnthropicLink: (args) => request("POST", "/me/anthropic-link", { hasBody: true }, args),
|
|
412
|
+
// GET /me/cross-project — rank: any-builder — GET /me/cross-project
|
|
413
|
+
getMeCrossProject: (args) => request("GET", "/me/cross-project", { hasBody: false }, args),
|
|
414
|
+
// PATCH /me/cross-project — rank: any-builder — PATCH /me/cross-project
|
|
415
|
+
patchMeCrossProject: (args) => request("PATCH", "/me/cross-project", { hasBody: true }, args),
|
|
412
416
|
// PATCH /me/disciplines — rank: any-builder — PATCH /me/disciplines
|
|
413
417
|
patchMeDisciplines: (args) => request("PATCH", "/me/disciplines", { hasBody: true }, args),
|
|
414
418
|
// GET /me/display-name — rank: any-builder — GET /me/display-name
|
|
@@ -408,6 +408,10 @@ function createClient(opts = {}) {
|
|
|
408
408
|
getMeAchievements: (args) => request("GET", "/me/achievements", { hasBody: false }, args),
|
|
409
409
|
// POST /me/anthropic-link — rank: any-builder — POST /me/anthropic-link
|
|
410
410
|
postMeAnthropicLink: (args) => request("POST", "/me/anthropic-link", { hasBody: true }, args),
|
|
411
|
+
// GET /me/cross-project — rank: any-builder — GET /me/cross-project
|
|
412
|
+
getMeCrossProject: (args) => request("GET", "/me/cross-project", { hasBody: false }, args),
|
|
413
|
+
// PATCH /me/cross-project — rank: any-builder — PATCH /me/cross-project
|
|
414
|
+
patchMeCrossProject: (args) => request("PATCH", "/me/cross-project", { hasBody: true }, args),
|
|
411
415
|
// PATCH /me/disciplines — rank: any-builder — PATCH /me/disciplines
|
|
412
416
|
patchMeDisciplines: (args) => request("PATCH", "/me/disciplines", { hasBody: true }, args),
|
|
413
417
|
// GET /me/display-name — rank: any-builder — GET /me/display-name
|
|
@@ -51,6 +51,7 @@ export interface GetInboxResponse { ideas: unknown; open_count: unknown }
|
|
|
51
51
|
export interface GetLeaderboardResponse { leaderboard: unknown; page: unknown }
|
|
52
52
|
export interface GetLearningsResponse { learnings: unknown }
|
|
53
53
|
export interface GetMeAchievementsResponse { unlocked: unknown }
|
|
54
|
+
export interface GetMeCrossProjectResponse { hide_stats: boolean; profile_state: unknown; cross_project: unknown }
|
|
54
55
|
export interface GetMeDisplayNameResponse { display_name: unknown; max_length: unknown }
|
|
55
56
|
export interface GetMeEventSoundPrefsResponse { prefs: unknown; effective: unknown; effective_wav: unknown; events: unknown }
|
|
56
57
|
export interface GetMeRecentShipsResponse { ships: unknown }
|
|
@@ -124,6 +125,8 @@ export interface PatchGoalsIdVisibilityRequest { visibility: string }
|
|
|
124
125
|
export interface PatchGoalsIdVisibilityResponse { ok: boolean; goal: unknown }
|
|
125
126
|
export interface PatchInboxIdRequest { status?: "open" | "promoted" | "discarded" | "merged"; kind?: string; promoted_to_task_id?: number; reviewed_at?: unknown; merged_into_idea_id?: number; triage_note?: string }
|
|
126
127
|
export interface PatchInboxIdResponse { idea: unknown }
|
|
128
|
+
export interface PatchMeCrossProjectRequest { hide_stats: boolean }
|
|
129
|
+
export interface PatchMeCrossProjectResponse { hide_stats: boolean }
|
|
127
130
|
export interface PatchMeDisciplinesRequest { preferred_disciplines: string[] }
|
|
128
131
|
export interface PatchMeDisciplinesResponse { builder: unknown; onboarding: unknown }
|
|
129
132
|
export interface PatchMeDisplayNameResponse { display_name: unknown; max_length: unknown }
|
|
@@ -645,6 +648,10 @@ export interface BongosClient {
|
|
|
645
648
|
getMeAchievements(args?: RequestArgs): Promise<GetMeAchievementsResponse>;
|
|
646
649
|
/** POST /me/anthropic-link — rank: any-builder */
|
|
647
650
|
postMeAnthropicLink(args: RequestArgs & { body: PostMeAnthropicLinkRequest }): Promise<PostMeAnthropicLinkResponse>;
|
|
651
|
+
/** GET /me/cross-project — rank: any-builder */
|
|
652
|
+
getMeCrossProject(args?: RequestArgs): Promise<GetMeCrossProjectResponse>;
|
|
653
|
+
/** PATCH /me/cross-project — rank: any-builder */
|
|
654
|
+
patchMeCrossProject(args: RequestArgs & { body: PatchMeCrossProjectRequest }): Promise<PatchMeCrossProjectResponse>;
|
|
648
655
|
/** PATCH /me/disciplines — rank: any-builder */
|
|
649
656
|
patchMeDisciplines(args: RequestArgs & { body: PatchMeDisciplinesRequest }): Promise<PatchMeDisciplinesResponse>;
|
|
650
657
|
/** GET /me/display-name — rank: any-builder */
|
|
@@ -405,6 +405,10 @@ export function createClient(opts = {}) {
|
|
|
405
405
|
getMeAchievements: (args) => request("GET", "/me/achievements", { hasBody: false }, args),
|
|
406
406
|
// POST /me/anthropic-link — rank: any-builder — POST /me/anthropic-link
|
|
407
407
|
postMeAnthropicLink: (args) => request("POST", "/me/anthropic-link", { hasBody: true }, args),
|
|
408
|
+
// GET /me/cross-project — rank: any-builder — GET /me/cross-project
|
|
409
|
+
getMeCrossProject: (args) => request("GET", "/me/cross-project", { hasBody: false }, args),
|
|
410
|
+
// PATCH /me/cross-project — rank: any-builder — PATCH /me/cross-project
|
|
411
|
+
patchMeCrossProject: (args) => request("PATCH", "/me/cross-project", { hasBody: true }, args),
|
|
408
412
|
// PATCH /me/disciplines — rank: any-builder — PATCH /me/disciplines
|
|
409
413
|
patchMeDisciplines: (args) => request("PATCH", "/me/disciplines", { hasBody: true }, args),
|
|
410
414
|
// GET /me/display-name — rank: any-builder — GET /me/display-name
|
package/docs/api/openapi.json
CHANGED
|
@@ -6267,6 +6267,91 @@
|
|
|
6267
6267
|
]
|
|
6268
6268
|
}
|
|
6269
6269
|
},
|
|
6270
|
+
"/me/cross-project": {
|
|
6271
|
+
"get": {
|
|
6272
|
+
"operationId": "get_me_cross_project",
|
|
6273
|
+
"tags": [
|
|
6274
|
+
"me"
|
|
6275
|
+
],
|
|
6276
|
+
"summary": "GET /me/cross-project",
|
|
6277
|
+
"description": "the caller's OWN cross-project record + hide flag (own data).\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
6278
|
+
"x-rank": "any-builder",
|
|
6279
|
+
"x-source": "modules/platform-identity/routes/profile.js",
|
|
6280
|
+
"responses": {
|
|
6281
|
+
"200": {
|
|
6282
|
+
"description": "Success.",
|
|
6283
|
+
"content": {
|
|
6284
|
+
"application/json": {
|
|
6285
|
+
"schema": {
|
|
6286
|
+
"$ref": "#/components/schemas/GetMeCrossProjectResponse"
|
|
6287
|
+
}
|
|
6288
|
+
}
|
|
6289
|
+
}
|
|
6290
|
+
},
|
|
6291
|
+
"400": {
|
|
6292
|
+
"$ref": "#/components/responses/BadRequest"
|
|
6293
|
+
},
|
|
6294
|
+
"401": {
|
|
6295
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
6296
|
+
},
|
|
6297
|
+
"403": {
|
|
6298
|
+
"$ref": "#/components/responses/Forbidden"
|
|
6299
|
+
}
|
|
6300
|
+
},
|
|
6301
|
+
"security": [
|
|
6302
|
+
{
|
|
6303
|
+
"builderSession": []
|
|
6304
|
+
}
|
|
6305
|
+
]
|
|
6306
|
+
},
|
|
6307
|
+
"patch": {
|
|
6308
|
+
"operationId": "patch_me_cross_project",
|
|
6309
|
+
"tags": [
|
|
6310
|
+
"me"
|
|
6311
|
+
],
|
|
6312
|
+
"summary": "PATCH /me/cross-project",
|
|
6313
|
+
"description": "flip the caller's OWN hide flag (C2's self-serve opt-out; own data, keyed on the authenticated github_id).\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
6314
|
+
"x-rank": "any-builder",
|
|
6315
|
+
"x-source": "modules/platform-identity/routes/profile.js",
|
|
6316
|
+
"requestBody": {
|
|
6317
|
+
"required": true,
|
|
6318
|
+
"content": {
|
|
6319
|
+
"application/json": {
|
|
6320
|
+
"schema": {
|
|
6321
|
+
"$ref": "#/components/schemas/PatchMeCrossProjectRequest"
|
|
6322
|
+
}
|
|
6323
|
+
}
|
|
6324
|
+
},
|
|
6325
|
+
"x-validated": true
|
|
6326
|
+
},
|
|
6327
|
+
"responses": {
|
|
6328
|
+
"200": {
|
|
6329
|
+
"description": "Success.",
|
|
6330
|
+
"content": {
|
|
6331
|
+
"application/json": {
|
|
6332
|
+
"schema": {
|
|
6333
|
+
"$ref": "#/components/schemas/PatchMeCrossProjectResponse"
|
|
6334
|
+
}
|
|
6335
|
+
}
|
|
6336
|
+
}
|
|
6337
|
+
},
|
|
6338
|
+
"400": {
|
|
6339
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
6340
|
+
},
|
|
6341
|
+
"401": {
|
|
6342
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
6343
|
+
},
|
|
6344
|
+
"403": {
|
|
6345
|
+
"$ref": "#/components/responses/Forbidden"
|
|
6346
|
+
}
|
|
6347
|
+
},
|
|
6348
|
+
"security": [
|
|
6349
|
+
{
|
|
6350
|
+
"builderSession": []
|
|
6351
|
+
}
|
|
6352
|
+
]
|
|
6353
|
+
}
|
|
6354
|
+
},
|
|
6270
6355
|
"/me/disciplines": {
|
|
6271
6356
|
"patch": {
|
|
6272
6357
|
"operationId": "patch_me_disciplines",
|
|
@@ -12850,6 +12935,21 @@
|
|
|
12850
12935
|
"unlocked"
|
|
12851
12936
|
]
|
|
12852
12937
|
},
|
|
12938
|
+
"GetMeCrossProjectResponse": {
|
|
12939
|
+
"type": "object",
|
|
12940
|
+
"properties": {
|
|
12941
|
+
"hide_stats": {
|
|
12942
|
+
"type": "boolean"
|
|
12943
|
+
},
|
|
12944
|
+
"profile_state": {},
|
|
12945
|
+
"cross_project": {}
|
|
12946
|
+
},
|
|
12947
|
+
"required": [
|
|
12948
|
+
"hide_stats",
|
|
12949
|
+
"profile_state",
|
|
12950
|
+
"cross_project"
|
|
12951
|
+
]
|
|
12952
|
+
},
|
|
12853
12953
|
"GetMeDisplayNameResponse": {
|
|
12854
12954
|
"type": "object",
|
|
12855
12955
|
"properties": {
|
|
@@ -13808,6 +13908,29 @@
|
|
|
13808
13908
|
"idea"
|
|
13809
13909
|
]
|
|
13810
13910
|
},
|
|
13911
|
+
"PatchMeCrossProjectRequest": {
|
|
13912
|
+
"type": "object",
|
|
13913
|
+
"properties": {
|
|
13914
|
+
"hide_stats": {
|
|
13915
|
+
"type": "boolean"
|
|
13916
|
+
}
|
|
13917
|
+
},
|
|
13918
|
+
"required": [
|
|
13919
|
+
"hide_stats"
|
|
13920
|
+
],
|
|
13921
|
+
"additionalProperties": false
|
|
13922
|
+
},
|
|
13923
|
+
"PatchMeCrossProjectResponse": {
|
|
13924
|
+
"type": "object",
|
|
13925
|
+
"properties": {
|
|
13926
|
+
"hide_stats": {
|
|
13927
|
+
"type": "boolean"
|
|
13928
|
+
}
|
|
13929
|
+
},
|
|
13930
|
+
"required": [
|
|
13931
|
+
"hide_stats"
|
|
13932
|
+
]
|
|
13933
|
+
},
|
|
13811
13934
|
"PatchMeDisciplinesRequest": {
|
|
13812
13935
|
"type": "object",
|
|
13813
13936
|
"properties": {
|
|
@@ -17099,9 +17222,9 @@
|
|
|
17099
17222
|
"description": "A required dependency/feature is not configured or is temporarily down."
|
|
17100
17223
|
}
|
|
17101
17224
|
},
|
|
17102
|
-
"x-endpoint-count":
|
|
17103
|
-
"x-schema-count":
|
|
17225
|
+
"x-endpoint-count": 273,
|
|
17226
|
+
"x-schema-count": 306,
|
|
17104
17227
|
"x-undocumented-bodies": 8,
|
|
17105
|
-
"x-response-schemas":
|
|
17228
|
+
"x-response-schemas": 207,
|
|
17106
17229
|
"x-generated-by": "scripts/gds/gen-api-docs.js"
|
|
17107
17230
|
}
|
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. 273 endpoints across 45 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`.
|
|
@@ -282,13 +282,15 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
282
282
|
| POST | `/api/bongos/llm-cache/lookup` | `metic+archon` | `cache_key`, `tokens_saved_est` | POST /llm-cache/lookup — hit/miss + atomic hit_count bump. |
|
|
283
283
|
| POST | `/api/bongos/llm-cache/store` | `metic+archon` | `spec`, `result_stdout`, `result_cost_usd` | POST /llm-cache/store — upsert a freshly-produced result. |
|
|
284
284
|
|
|
285
|
-
## `me` (
|
|
285
|
+
## `me` (23)
|
|
286
286
|
|
|
287
287
|
| Method | Path | Rank | Body | Description |
|
|
288
288
|
|---|---|---|---|---|
|
|
289
289
|
| GET | `/api/bongos/me` | `any-builder` | — | PMS-V2 task 188: response now also carries `unlocked` (server-joined achievement metadata) + `locked_with_progress` (server-computed huma… |
|
|
290
290
|
| GET | `/api/bongos/me/achievements` | `any-builder` | — | The builder's unlocked achievements (joined to achievement metadata). |
|
|
291
291
|
| POST | `/api/bongos/me/anthropic-link` | `any-builder` | `anthropic_email`, `anthropic_user_id` | V3.R10 (task 235), criterion C3 — link the authed builder's Anthropic account. |
|
|
292
|
+
| GET | `/api/bongos/me/cross-project` | `any-builder` | — | the caller's OWN cross-project record + hide flag (own data). |
|
|
293
|
+
| PATCH | `/api/bongos/me/cross-project` | `any-builder` | `hide_stats` | flip the caller's OWN hide flag (C2's self-serve opt-out; own data, keyed on the authenticated github_id). |
|
|
292
294
|
| PATCH | `/api/bongos/me/disciplines` | `any-builder` | `preferred_disciplines` | Self-service edit of the authenticated builder's preferred disciplines (migrations 027 + 086 / V3.R80). |
|
|
293
295
|
| GET | `/api/bongos/me/display-name` | `any-builder` | — | Per-builder DISPLAY NAME (task 1669). |
|
|
294
296
|
| PATCH | `/api/bongos/me/display-name` | `any-builder` | _undocumented_ | PATCH the display name. |
|
|
@@ -408,6 +408,10 @@ function createClient(opts = {}) {
|
|
|
408
408
|
getMeAchievements: (args) => request("GET", "/me/achievements", { hasBody: false }, args),
|
|
409
409
|
// POST /me/anthropic-link — rank: any-builder — POST /me/anthropic-link
|
|
410
410
|
postMeAnthropicLink: (args) => request("POST", "/me/anthropic-link", { hasBody: true }, args),
|
|
411
|
+
// GET /me/cross-project — rank: any-builder — GET /me/cross-project
|
|
412
|
+
getMeCrossProject: (args) => request("GET", "/me/cross-project", { hasBody: false }, args),
|
|
413
|
+
// PATCH /me/cross-project — rank: any-builder — PATCH /me/cross-project
|
|
414
|
+
patchMeCrossProject: (args) => request("PATCH", "/me/cross-project", { hasBody: true }, args),
|
|
411
415
|
// PATCH /me/disciplines — rank: any-builder — PATCH /me/disciplines
|
|
412
416
|
patchMeDisciplines: (args) => request("PATCH", "/me/disciplines", { hasBody: true }, args),
|
|
413
417
|
// GET /me/display-name — rank: any-builder — GET /me/display-name
|
|
@@ -79,6 +79,20 @@
|
|
|
79
79
|
<p class="section-note">Saves when you press Save; shows everywhere at once.</p>
|
|
80
80
|
</section>
|
|
81
81
|
|
|
82
|
+
<!-- Cross-project record (platform-identity module, ADR 0171 / task 1002279).
|
|
83
|
+
Revealed only when the module's own endpoint answers; hidden on a
|
|
84
|
+
vanilla instance. P04 later moves this into the Profile rail group. -->
|
|
85
|
+
<section class="scroll" id="cross-project-scroll" aria-labelledby="cross-project-h" data-group="account" hidden>
|
|
86
|
+
<h2 id="cross-project-h" class="scroll__h">Cross-project record</h2>
|
|
87
|
+
<p class="scroll__lede">
|
|
88
|
+
Your track record across every project you build on, shown on your
|
|
89
|
+
public profile. Hiding it takes effect immediately everywhere —
|
|
90
|
+
your profile, the People directory, and recruiting views.
|
|
91
|
+
</p>
|
|
92
|
+
<div id="cross-project-rows"></div>
|
|
93
|
+
<p class="settings-status" id="cross-project-status" role="status" aria-live="polite"></p>
|
|
94
|
+
</section>
|
|
95
|
+
|
|
82
96
|
<!-- CRAFT — discipline preferences (GET /me, PATCH /me/disciplines). -->
|
|
83
97
|
<section class="scroll" id="craft-scroll" aria-labelledby="craft-h" data-group="account" hidden>
|
|
84
98
|
<h2 id="craft-h" class="scroll__h">Preferred disciplines</h2>
|
|
@@ -550,6 +550,86 @@
|
|
|
550
550
|
}
|
|
551
551
|
}
|
|
552
552
|
|
|
553
|
+
// ---- section: CROSS-PROJECT RECORD (hide flag — ADR 0171 D4, task 1002279)
|
|
554
|
+
//
|
|
555
|
+
// GET/PATCH /api/bongos/me/cross-project (platform-identity module — hub only).
|
|
556
|
+
// One on/off toggle: "show my cross-project record on my public profile".
|
|
557
|
+
// Enforcement is server-side in the module's read functions, so flipping it
|
|
558
|
+
// takes effect immediately on the profile, /people, and /scouting. The section
|
|
559
|
+
// reveals ONLY when the module's endpoint answers (404 on a vanilla instance →
|
|
560
|
+
// stays hidden, no error state). P04 later re-homes this in the Profile rail
|
|
561
|
+
// group; storage is account-global on the hub (not behavior_prefs).
|
|
562
|
+
function renderCrossProject(data) {
|
|
563
|
+
const wrap = $('#cross-project-rows');
|
|
564
|
+
if (!wrap) return;
|
|
565
|
+
const hidden = !!(data && data.hide_stats);
|
|
566
|
+
const on = !hidden; // the toggle reads as "shown"
|
|
567
|
+
const cp = data && data.cross_project;
|
|
568
|
+
const totals = cp && cp.totals ? cp.totals : null;
|
|
569
|
+
const totalsLine = totals
|
|
570
|
+
? `Your record: ${totals.projects} project${totals.projects === 1 ? '' : 's'} · ` +
|
|
571
|
+
`${totals.credits} credits · ${totals.works_shipped} works shipped · ${totals.karma} karma.`
|
|
572
|
+
: 'No cross-project activity reported yet.';
|
|
573
|
+
const consentNote = data && data.profile_state !== 'active'
|
|
574
|
+
? ' Your profile publishes after your next sign-in at the hub.'
|
|
575
|
+
: '';
|
|
576
|
+
wrap.innerHTML =
|
|
577
|
+
'<div class="sound-row" data-cross-project="hide">' +
|
|
578
|
+
'<div class="sound-row__text">' +
|
|
579
|
+
'<div class="sound-row__label">Show my cross-project record</div>' +
|
|
580
|
+
`<div class="sound-row__desc">${escapeHtml(totalsLine + consentNote)} You always see your own numbers here.</div>` +
|
|
581
|
+
'</div>' +
|
|
582
|
+
`<button type="button" class="sound-toggle" data-on="${on ? '1' : '0'}" aria-pressed="${on ? 'true' : 'false'}">${on ? 'Shown' : 'Hidden'}</button>` +
|
|
583
|
+
'</div>';
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// PATCH the hide flag. Body: { hide_stats: <bool> } (routes/profile.js).
|
|
587
|
+
async function saveCrossProject(hide, btn) {
|
|
588
|
+
const status = $('#cross-project-status');
|
|
589
|
+
if (status) status.textContent = 'Saving…';
|
|
590
|
+
if (btn) btn.disabled = true;
|
|
591
|
+
try {
|
|
592
|
+
const res = await api.request('PATCH', `${API}/me/cross-project`, { body: { hide_stats: hide } });
|
|
593
|
+
if (res.status === 401) { toAuth(); return; }
|
|
594
|
+
if (!res.ok) throw new Error(`cross-project PATCH → ${res.status}`);
|
|
595
|
+
await loadCrossProject(); // re-render from the server's truth (flag + record)
|
|
596
|
+
const msg = hide
|
|
597
|
+
? 'Cross-project record hidden — removed from your profile and directories immediately.'
|
|
598
|
+
: 'Cross-project record shown on your public profile.';
|
|
599
|
+
if (status) status.textContent = msg;
|
|
600
|
+
toast(msg);
|
|
601
|
+
} catch (err) {
|
|
602
|
+
console.error('[settings] save cross-project failed', err);
|
|
603
|
+
if (status) status.textContent = 'Could not save. Try again.';
|
|
604
|
+
toast('Could not save your cross-project preference. Try again.');
|
|
605
|
+
if (btn) btn.disabled = false;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
let crossProjectWired = false;
|
|
610
|
+
async function loadCrossProject() {
|
|
611
|
+
const section = $('#cross-project-scroll');
|
|
612
|
+
if (!section) return;
|
|
613
|
+
try {
|
|
614
|
+
const res = await api.request('GET', `${API}/me/cross-project`);
|
|
615
|
+
if (res.status === 401) { toAuth(); return; }
|
|
616
|
+
if (!res.ok) return; // module off / route absent → the section stays hidden
|
|
617
|
+
renderCrossProject(res.data);
|
|
618
|
+
section.hidden = false;
|
|
619
|
+
if (!crossProjectWired) {
|
|
620
|
+
crossProjectWired = true;
|
|
621
|
+
$('#cross-project-rows').addEventListener('click', (e) => {
|
|
622
|
+
const b = e.target.closest('.sound-toggle');
|
|
623
|
+
if (!b || b.disabled) return;
|
|
624
|
+
saveCrossProject(b.dataset.on === '1', b); // shown → hide; hidden → show
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
} catch (err) {
|
|
628
|
+
// Network/parse failure on an optional module section — stay hidden, log only.
|
|
629
|
+
console.error('[settings] failed to load cross-project', err);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
553
633
|
// ---- section 2: CRAFT (discipline preferences) ---------------------------
|
|
554
634
|
|
|
555
635
|
// Valid disciplines are limited (see me.js + db.updateBuilderDisciplines):
|
|
@@ -1098,6 +1178,7 @@
|
|
|
1098
1178
|
const PANEL_ANCHORS = {
|
|
1099
1179
|
'connections': 'connections-scroll',
|
|
1100
1180
|
'display-name': 'display-name-scroll',
|
|
1181
|
+
'cross-project': 'cross-project-scroll',
|
|
1101
1182
|
'disciplines': 'craft-scroll',
|
|
1102
1183
|
'cli': 'cli-scroll',
|
|
1103
1184
|
'art-key': 'art-key',
|
|
@@ -1250,6 +1331,9 @@
|
|
|
1250
1331
|
await loadWandering();
|
|
1251
1332
|
// Rendering loads independently too (rich in-feed cards on/off, task 1279).
|
|
1252
1333
|
await loadRender();
|
|
1334
|
+
// Cross-project record (hide flag) — reveals only where the platform-identity
|
|
1335
|
+
// module answers (the hub); silent no-op elsewhere (task 1002279).
|
|
1336
|
+
await loadCrossProject();
|
|
1253
1337
|
// Image key (per-builder Gemini key, task 1013) — own try/catch; `me` carries
|
|
1254
1338
|
// the `needs` art_key item that drives the covered/action-needed banner.
|
|
1255
1339
|
await loadArtKey(me);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"maintainer": "Cloud Bongos maintainers",
|
|
11
11
|
"maintenance": { "status": "core-maintained" },
|
|
12
12
|
"contributes": {
|
|
13
|
-
"routes": ["sso", "projects", "my-projects", "scouting"],
|
|
13
|
+
"routes": ["sso", "projects", "my-projects", "scouting", "profile"],
|
|
14
14
|
"migrations": true,
|
|
15
15
|
"pollers": ["code-sweep"]
|
|
16
16
|
},
|
|
@@ -601,6 +601,26 @@ async function getOwnProfileExtras(githubId, { pool = defaultPool } = {}) {
|
|
|
601
601
|
return getRollupForBuilder(githubId, { pool });
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
+
// The C2 hide-stats WRITE (ADR 0171 D4, task 1002279): own-scoped — callers key
|
|
605
|
+
// this by the AUTHENTICATED session's github_id. Upserts so a hub builder who
|
|
606
|
+
// never federated (no account row yet) can still hide ahead of time: the insert
|
|
607
|
+
// creates a fail-closed provisional account already carrying the flag; the
|
|
608
|
+
// UPDATE branch touches ONLY hide_stats (never identity fields, never
|
|
609
|
+
// profile_state — activation stays /sso/authorize's alone, D3).
|
|
610
|
+
async function setAccountHideStats({ githubId, githubLogin, displayName, avatarUrl, hide }, { pool = defaultPool } = {}) {
|
|
611
|
+
if (!isNumericGithubId(githubId)) return null;
|
|
612
|
+
const { rows } = await pool.query(
|
|
613
|
+
`INSERT INTO platform_identity_accounts (github_id, github_login, display_name, avatar_url, hide_stats)
|
|
614
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
615
|
+
ON CONFLICT (github_id) DO UPDATE SET
|
|
616
|
+
hide_stats = EXCLUDED.hide_stats,
|
|
617
|
+
updated_at = now()
|
|
618
|
+
RETURNING github_id, github_login, profile_state, hide_stats`,
|
|
619
|
+
[githubId, githubLogin, displayName ?? null, avatarUrl ?? null, !!hide],
|
|
620
|
+
);
|
|
621
|
+
return rows[0];
|
|
622
|
+
}
|
|
623
|
+
|
|
604
624
|
// -------------------------------------------------------------------------
|
|
605
625
|
// Authorization codes (single-use, short-lived, bound)
|
|
606
626
|
// -------------------------------------------------------------------------
|
|
@@ -700,6 +720,7 @@ module.exports = {
|
|
|
700
720
|
getRollupForBuilder,
|
|
701
721
|
getPublicProfileExtras,
|
|
702
722
|
getOwnProfileExtras,
|
|
723
|
+
setAccountHideStats,
|
|
703
724
|
// codes
|
|
704
725
|
mintAuthCode,
|
|
705
726
|
redeemAuthCode,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// modules/platform-identity/routes/profile.js — the builder's OWN cross-project
|
|
2
|
+
// profile controls (ADR 0171 D4, task 1002279 / goal 1000045). Two own-scoped
|
|
3
|
+
// endpoints behind a live session; both key on req.builder.github_id — never a
|
|
4
|
+
// caller-named id — so a builder can only ever read or flip their own flag.
|
|
5
|
+
//
|
|
6
|
+
// GET /me/cross-project — the Settings self view: the hide flag + consent
|
|
7
|
+
// state + the builder's own rollup, served regardless
|
|
8
|
+
// of flags (getOwnProfileExtras — a hidden builder
|
|
9
|
+
// inspects their numbers HERE, not on the public
|
|
10
|
+
// route, which stays viewer-independent).
|
|
11
|
+
// PATCH /me/cross-project — flip hide_stats. Takes effect immediately on every
|
|
12
|
+
// public surface (profile cross_project, /people,
|
|
13
|
+
// /scouting): enforcement lives in the module's read
|
|
14
|
+
// functions (D2) and the hub profile response is
|
|
15
|
+
// no-store (D5), so there is no cache to outwait.
|
|
16
|
+
//
|
|
17
|
+
// Reaches core only through the doorway (ADR 0083).
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const express = require('express');
|
|
21
|
+
const api = require('../../../src/module-api');
|
|
22
|
+
const pi = require('../platform-identity');
|
|
23
|
+
|
|
24
|
+
const { pool, requireBuilder, validateOrRespond } = api;
|
|
25
|
+
|
|
26
|
+
module.exports = function profileRoutes() {
|
|
27
|
+
const router = express.Router();
|
|
28
|
+
|
|
29
|
+
// rank: any-builder — the caller's OWN cross-project record + hide flag (own data).
|
|
30
|
+
router.get('/me/cross-project', requireBuilder, async (req, res) => {
|
|
31
|
+
try {
|
|
32
|
+
// Core builders include system:* principals whose github_id is not numeric;
|
|
33
|
+
// they have no platform identity — answer the empty shape, not a 500.
|
|
34
|
+
if (!pi.isNumericGithubId(req.builder.github_id)) {
|
|
35
|
+
return res.json({ hide_stats: false, profile_state: null, cross_project: null });
|
|
36
|
+
}
|
|
37
|
+
const [acct, extras] = await Promise.all([
|
|
38
|
+
pi.getAccount(req.builder.github_id, { pool }),
|
|
39
|
+
pi.getOwnProfileExtras(req.builder.github_id, { pool }),
|
|
40
|
+
]);
|
|
41
|
+
res.json({
|
|
42
|
+
hide_stats: !!(acct && acct.hide_stats),
|
|
43
|
+
profile_state: acct ? acct.profile_state : null,
|
|
44
|
+
cross_project: extras,
|
|
45
|
+
});
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error('[platform-identity] GET /me/cross-project', err);
|
|
48
|
+
res.fail('cross_project_read_failed', { status: 500, message: 'internal error' });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// rank: any-builder — flip the caller's OWN hide flag (C2's self-serve opt-out;
|
|
53
|
+
// own data, keyed on the authenticated github_id).
|
|
54
|
+
router.patch('/me/cross-project', requireBuilder, async (req, res) => {
|
|
55
|
+
if (validateOrRespond(req, res, {
|
|
56
|
+
hide_stats: { required: true, type: 'boolean' },
|
|
57
|
+
})) return;
|
|
58
|
+
try {
|
|
59
|
+
if (!pi.isNumericGithubId(req.builder.github_id)) {
|
|
60
|
+
return res.fail('no_platform_identity', 400);
|
|
61
|
+
}
|
|
62
|
+
const row = await pi.setAccountHideStats({
|
|
63
|
+
githubId: req.builder.github_id,
|
|
64
|
+
githubLogin: req.builder.github_login,
|
|
65
|
+
displayName: req.builder.display_name,
|
|
66
|
+
avatarUrl: req.builder.avatar_url,
|
|
67
|
+
hide: req.body.hide_stats,
|
|
68
|
+
}, { pool });
|
|
69
|
+
res.json({ hide_stats: !!(row && row.hide_stats) });
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error('[platform-identity] PATCH /me/cross-project', err);
|
|
72
|
+
res.fail('cross_project_write_failed', { status: 500, message: 'internal error' });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return router;
|
|
77
|
+
};
|
|
@@ -486,6 +486,28 @@ await ta('getPublicProfileExtras returns the rollup for an active, visible accou
|
|
|
486
486
|
assert.equal(out.more_projects, 0);
|
|
487
487
|
});
|
|
488
488
|
|
|
489
|
+
await ta('setAccountHideStats flips ONLY the hide flag (never identity, never profile_state)', async () => {
|
|
490
|
+
const pool = mockPool([{ rows: [{ github_id: '42', hide_stats: true, profile_state: 'provisional' }] }]);
|
|
491
|
+
const out = await P.setAccountHideStats(
|
|
492
|
+
{ githubId: 42, githubLogin: 'octo', displayName: 'Octo', avatarUrl: 'a', hide: true }, { pool });
|
|
493
|
+
assert.equal(out.hide_stats, true);
|
|
494
|
+
const c = pool.calls[0];
|
|
495
|
+
assert.match(c.text, /ON CONFLICT \(github_id\) DO UPDATE/);
|
|
496
|
+
// The UPDATE branch may set hide_stats + updated_at and NOTHING else — a
|
|
497
|
+
// settings toggle is not an identity refresh and must never touch consent.
|
|
498
|
+
const updateBranch = c.text.slice(c.text.indexOf('DO UPDATE'), c.text.indexOf('RETURNING'));
|
|
499
|
+
assert.match(updateBranch, /hide_stats = EXCLUDED\.hide_stats/);
|
|
500
|
+
assert.doesNotMatch(updateBranch, /github_login|display_name|avatar_url|profile_state/,
|
|
501
|
+
'the update branch must not clobber identity or consent');
|
|
502
|
+
assert.equal(c.params[4], true, 'hide passed as a boolean');
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
await ta('setAccountHideStats guards non-numeric github_id (system:* principals) → null, no query', async () => {
|
|
506
|
+
const pool = mockPool([]);
|
|
507
|
+
assert.equal(await P.setAccountHideStats({ githubId: 'system:bfg', hide: true }, { pool }), null);
|
|
508
|
+
assert.equal(pool.calls.length, 0);
|
|
509
|
+
});
|
|
510
|
+
|
|
489
511
|
await ta('getOwnProfileExtras serves the self view regardless of flags (no flags lookup)', async () => {
|
|
490
512
|
const pool = mockPool([
|
|
491
513
|
{ rows: [{ projects: 1, credits: 10, works_shipped: 1, karma: 0, last_active: 'T', disciplines: null }] },
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.170",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.170",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.170",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/src/module-api.js
CHANGED
|
@@ -765,8 +765,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
765
765
|
// landed since 1.19.167 with no explicit bump. run 31962256201. (task 1002620)
|
|
766
766
|
// 1.19.169 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
767
767
|
// landed since 1.19.168 with no explicit bump. run 31964702865. (task 1002620)
|
|
768
|
+
// 1.19.170 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
769
|
+
// landed since 1.19.169 with no explicit bump. run 31965483417. (task 1002620)
|
|
768
770
|
// ---------------------------------------------------------------------------
|
|
769
|
-
const CORE_VERSION = '1.19.
|
|
771
|
+
const CORE_VERSION = '1.19.170'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
770
772
|
|
|
771
773
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
772
774
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -142,6 +142,31 @@ try {
|
|
|
142
142
|
acct = await pi.getAccount(LAZY, { pool });
|
|
143
143
|
ok(acct.profile_state === 'active', 'a later plain upsert never demotes an active account');
|
|
144
144
|
|
|
145
|
+
// 4b — the live hide toggle (R03, task 1002279): flipping hide_stats takes
|
|
146
|
+
// effect IMMEDIATELY on every public read — the profile port and the
|
|
147
|
+
// /people + /scouting rollup — while the self view keeps serving.
|
|
148
|
+
await pi.setAccountHideStats({ githubId: ACTIVE, githubLogin: 'active-b', hide: true }, { pool });
|
|
149
|
+
ok(await pi.getPublicProfileExtras(ACTIVE, { pool }) === null, 'hide ON → public read drops to null immediately');
|
|
150
|
+
const hiddenListed = (await pi.listScoutingRollup({ limit: 500, pool })).map((r) => Number(r.github_id));
|
|
151
|
+
ok(!hiddenListed.includes(ACTIVE), 'hide ON → the builder drops out of /people + /scouting immediately');
|
|
152
|
+
const stillOwn = await pi.getOwnProfileExtras(ACTIVE, { pool });
|
|
153
|
+
ok(stillOwn && stillOwn.totals.credits === 175, 'hide ON → the self view still serves their own numbers');
|
|
154
|
+
let hiddenAcct = await pi.getAccount(ACTIVE, { pool });
|
|
155
|
+
ok(hiddenAcct.profile_state === 'active', 'the toggle never touches profile_state');
|
|
156
|
+
ok(hiddenAcct.github_login === 'active-b', 'the toggle never clobbers identity fields');
|
|
157
|
+
await pi.setAccountHideStats({ githubId: ACTIVE, githubLogin: 'active-b', hide: false }, { pool });
|
|
158
|
+
const backPub = await pi.getPublicProfileExtras(ACTIVE, { pool });
|
|
159
|
+
ok(backPub && backPub.totals.credits === 175, 'hide OFF → the public read returns immediately');
|
|
160
|
+
|
|
161
|
+
// 4c — hide-before-federating: a builder with NO account row can still hide;
|
|
162
|
+
// the insert creates a fail-closed provisional account carrying the flag.
|
|
163
|
+
const EARLY = 90002282;
|
|
164
|
+
await q(`DELETE FROM platform_identity_accounts WHERE github_id = $1`, [EARLY]).catch(() => {});
|
|
165
|
+
const early = await pi.setAccountHideStats({ githubId: EARLY, githubLogin: 'early-b', hide: true }, { pool });
|
|
166
|
+
ok(early.hide_stats === true && early.profile_state === 'provisional',
|
|
167
|
+
'hide with no account row creates a provisional, hidden account');
|
|
168
|
+
await q(`DELETE FROM platform_identity_accounts WHERE github_id = $1`, [EARLY]).catch(() => {});
|
|
169
|
+
|
|
145
170
|
// 5 — rollup payload sanitization against the real columns (D7).
|
|
146
171
|
await pi.upsertActivity({
|
|
147
172
|
clientId: PUB_CLIENT, githubId: LAZY, credits: 1,
|