@bongos/core 1.19.346 → 1.19.347

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.346",
6
- "core_contract": "1.19.346",
7
- "source_commit": "17c6cd8e45c6d2f44d50888c8bcd9c5daa0889f3",
5
+ "core_version": "1.19.347",
6
+ "core_contract": "1.19.347",
7
+ "source_commit": "e27bff09b16adf42c58b54ead68924751dbc2351",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-25T02:22:50.262Z",
9
+ "built_at": "2026-08-25T02:40:24.281Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 328,
13
13
  "agent_docs_stubbed": 22,
14
- "functional_verbatim": 1492,
14
+ "functional_verbatim": 1497,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1842,
20
- "tree_sha256": "88d683cf1954503b8c2d4555474808840900d1b16471c26c626c53f076927df5",
19
+ "file_count": 1847,
20
+ "tree_sha256": "2e117a040ff4372ef27c7c654e2e827f99f84acd347da0b416682cc5a66a1eb2",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -1472,12 +1472,12 @@
1472
1472
  {
1473
1473
  "path": "docs/copy-inventory.md",
1474
1474
  "mode": "0000644",
1475
- "sha256": "a47b42bd38656c5aa947d278ecf4b82cdf7113d94ac6c6796f8b7ecada6030e5"
1475
+ "sha256": "360d2e6fd65c436cc402f94fede50e2ab59ff275c1bd894c99f599576a804984"
1476
1476
  },
1477
1477
  {
1478
1478
  "path": "docs/copy-registry.json",
1479
1479
  "mode": "0000644",
1480
- "sha256": "11d91419272c08d96af33fc4a9df963b3f4c249874e2bfebd32396a40ac275e5"
1480
+ "sha256": "9c6c9b7271759fff6f4a76857f7d98ebef55785c474158276d28d7f6db9092d9"
1481
1481
  },
1482
1482
  {
1483
1483
  "path": "docs/design-contract.md",
@@ -3379,6 +3379,26 @@
3379
3379
  "mode": "0000644",
3380
3380
  "sha256": "daeaef3932c592d2f9eb7fcd97e99b5eb761d7af9115718fc432360c7d7de0e1"
3381
3381
  },
3382
+ {
3383
+ "path": "modules/hall-ui/public/collab-lib.js",
3384
+ "mode": "0000644",
3385
+ "sha256": "e68240885e57ae11d8d4364ad68d69daaa67cf82b79751f82a5f25de715b97ed"
3386
+ },
3387
+ {
3388
+ "path": "modules/hall-ui/public/collab.css",
3389
+ "mode": "0000644",
3390
+ "sha256": "cf5b07dd293c2e97584deca4856d24ffeca27a9f1aa9f80b93a1e1698f14977f"
3391
+ },
3392
+ {
3393
+ "path": "modules/hall-ui/public/collab.html",
3394
+ "mode": "0000644",
3395
+ "sha256": "f5f31cb1353493ff3f8f2a00882ae71a6a5d7c2553eef37571e8dc9dc12684ae"
3396
+ },
3397
+ {
3398
+ "path": "modules/hall-ui/public/collab.js",
3399
+ "mode": "0000644",
3400
+ "sha256": "3db1cc1fe472dabad978396726091bf4abde69383c9a5cd51b89f711796dec5c"
3401
+ },
3382
3402
  {
3383
3403
  "path": "modules/hall-ui/public/copy-desk.css",
3384
3404
  "mode": "0000644",
@@ -3742,7 +3762,7 @@
3742
3762
  {
3743
3763
  "path": "modules/hall-ui/public/shell.js",
3744
3764
  "mode": "0000644",
3745
- "sha256": "cc9fd3033b528ca2e5761054219653926341a6c10ee27ab10b42779cfc3b46a3"
3765
+ "sha256": "8d07f24b287a54fdff6d340972d8cc80e37f8fffb4169834fe051a40e6a196ac"
3746
3766
  },
3747
3767
  {
3748
3768
  "path": "modules/hall-ui/public/style.css",
@@ -4742,12 +4762,12 @@
4742
4762
  {
4743
4763
  "path": "package-lock.json",
4744
4764
  "mode": "0000644",
4745
- "sha256": "4f0ae4d95143c43bf5761820758130da48c40d0a2fde082d898fb6a6175ef853"
4765
+ "sha256": "ab72d709096f258ac4fb7df05ba0e32127d2e470b9bba8d724894ea883e14094"
4746
4766
  },
4747
4767
  {
4748
4768
  "path": "package.json",
4749
4769
  "mode": "0000644",
4750
- "sha256": "9ecdcc1f7c105d33b249c6953d18a6c8769d6fa767b996333a456f9b32ad141c"
4770
+ "sha256": "3f40232829ee5093d6b7a64808013abb68ac04fe25b38d3fa174fc5fecff11a2"
4751
4771
  },
4752
4772
  {
4753
4773
  "path": "public-docs/index.html",
@@ -6562,7 +6582,7 @@
6562
6582
  {
6563
6583
  "path": "src/bongos/serve-internal.js",
6564
6584
  "mode": "0000644",
6565
- "sha256": "d960192ab5e0f6880f94cd45e33c81e5913810a907d16fdec7f48d166240e267"
6585
+ "sha256": "bf37dbda6a10fe8b21f6df01e8bdd62aea3f539523464b37666ffd535ceb7e00"
6566
6586
  },
6567
6587
  {
6568
6588
  "path": "src/bongos/stealth-gate.js",
@@ -6597,7 +6617,7 @@
6597
6617
  {
6598
6618
  "path": "src/module-api.js",
6599
6619
  "mode": "0000644",
6600
- "sha256": "ac7a592af3eb6de39e0a7f095d4a17bb89934aaa7064107fe0d462a0aaee4e2b"
6620
+ "sha256": "0caf921877b91ef138e5ad71215e7a2b05a5ad837dc1fa6cb8d8d2b4ccbb63f2"
6601
6621
  },
6602
6622
  {
6603
6623
  "path": "src/module-loader/catalog.js",
@@ -7114,6 +7134,11 @@
7114
7134
  "mode": "0000644",
7115
7135
  "sha256": "9f225b5d4db7e73fbd35bea68ab6c8c64b72bac79daa7be0219e796dc758376a"
7116
7136
  },
7137
+ {
7138
+ "path": "tests/collab_page.mjs",
7139
+ "mode": "0000644",
7140
+ "sha256": "f3a238160d2f9ad363013ac6f6c47333778184930692d3d4dfdf504cc0aa047e"
7141
+ },
7117
7142
  {
7118
7143
  "path": "tests/conductor.mjs",
7119
7144
  "mode": "0000644",
@@ -7847,7 +7872,7 @@
7847
7872
  {
7848
7873
  "path": "tests/hall_nav.mjs",
7849
7874
  "mode": "0000644",
7850
- "sha256": "59de791cd5497e7d73e3d34841f5e816efe36bc34d1580f3e9afbae3055f2f5c"
7875
+ "sha256": "3754e9c4880f96da1c0b77e4d81c1652b94c878872b663b3c421f5aef1bf0712"
7851
7876
  },
7852
7877
  {
7853
7878
  "path": "tests/hall_page_gate_map.mjs",
@@ -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
- **1385 strings** across 3 surfaces · 1228 distinct pieces of copy · 1385 certain, 0 heuristic · 127 shown in more than one place.
7
+ **1397 strings** across 3 surfaces · 1238 distinct pieces of copy · 1397 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` | 67 | 1096 |
13
+ | `builders-hall` | `modules/hall-ui/public` | 70 | 1108 |
14
14
  | `status-dashboard` | `modules/status-ui/public` | 4 | 98 |
15
15
  | `landing` | `public-landing` | 2 | 191 |
16
16
 
@@ -48,7 +48,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
48
48
  | `c661548d5140` | 3x | Next → | public-landing/projects.html:786, public-landing/projects.html:855, public-landing/projects.html:895 |
49
49
  | `f6f68ba322be` | 3x | Open {…} | modules/hall-ui/public/goal-inbox.js:53, modules/hall-ui/public/goal-inbox.js:69, modules/hall-ui/public/goals.js:67 |
50
50
  | `aa4db91b5732` | 3x | Released #{…}. It returns to the queue. | modules/hall-ui/public/goals-page.js:487, modules/hall-ui/public/work.js:792, modules/hall-ui/public/work.js:845 |
51
- | `e57ec24e13ef` | 3x | Sign in with GitHub | modules/hall-ui/public/hall-render.js:252, modules/hall-ui/public/hall-render.js:355, modules/hall-ui/public/shell.js:273 |
51
+ | `e57ec24e13ef` | 3x | Sign in with GitHub | modules/hall-ui/public/hall-render.js:252, modules/hall-ui/public/hall-render.js:355, modules/hall-ui/public/shell.js:277 |
52
52
  | `df38941b25a6` | 3x | Sign in with GitHub | public-landing/projects.html:549, public-landing/projects.html:607, public-landing/projects.html:725 |
53
53
  | `b2b399fdd6a4` | 3x | in progress | modules/hall-ui/public/goals-render.js:188, modules/hall-ui/public/roadmap.js:108, modules/hall-ui/public/roadmap.js:194 |
54
54
  | `cc5106af4b35` | 3x | {…} of {…} criteria satisfied | modules/hall-ui/public/goals-lib.js:30, modules/hall-ui/public/goals-render.js:540, modules/hall-ui/public/goals.js:45 |
@@ -63,22 +63,22 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
63
63
  | `a072071b893c` | 2x | All time | modules/status-ui/public/index.html:286, modules/status-ui/public/index.html:313 |
64
64
  | `490b73a0c6e8` | 2x | Approve your Dev Box app — {{worldName}} | modules/hall-ui/public/pair.html:6, modules/hall-ui/public/pair.html:6 |
65
65
  | `b71a26e6fb37` | 2x | Archons only | modules/hall-ui/public/sessions.html:28, modules/hall-ui/public/watch.html:31 |
66
+ | `b2656d0b2402` | 2x | Asked {…} | modules/hall-ui/public/collab.js:36, modules/hall-ui/public/watch.js:205 |
66
67
  | `08afb1257b71` | 2x | Back to home | modules/hall-ui/public/primer.html:68, modules/hall-ui/public/primer.js:516 |
67
68
  | `563d29ed86d8` | 2x | Bulk actions | modules/hall-ui/public/goals.html:148, modules/hall-ui/public/work.html:118 |
68
69
  | `46b33550d52b` | 2x | Bulk claim | modules/hall-ui/public/goals.html:151, modules/hall-ui/public/work.html:121 |
69
70
  | `c10806b50c61` | 2x | Claim task {…} | modules/hall-ui/public/goals-render.js:254, modules/hall-ui/public/work.js:356 |
70
71
  | `a832f32b5597` | 2x | Claimed {…} tasks. Open a terminal to start work. | modules/hall-ui/public/goals-page.js:370, modules/hall-ui/public/work.js:754 |
71
72
  | `cfe0f4c4fcc2` | 2x | Closing your box… (a few seconds) | modules/hall-ui/public/box-panel.js:265, modules/hall-ui/public/box-panel.js:370 |
73
+ | `c72c97dc33fe` | 2x | Collab — {{worldName}} | modules/hall-ui/public/collab.html:6, modules/hall-ui/public/collab.html:6 |
72
74
  | `9b16a22bf24c` | 2x | Connect Discord & join the server | modules/hall-ui/public/hall-render.js:814, modules/hall-ui/public/settings.js:826 |
73
75
  | `12ee050d0180` | 2x | Copy desk | modules/hall-ui/public/copy-desk.html:15, modules/hall-ui/public/copy-desk.html:15 |
74
76
  | `1214ea8e0d1b` | 2x | Copy desk — {{worldName}} | modules/hall-ui/public/copy-desk.html:6, modules/hall-ui/public/copy-desk.html:6 |
75
77
  | `8e2046777710` | 2x | Copy prompt for task {…} | modules/hall-ui/public/goals-render.js:255, modules/hall-ui/public/work.js:356 |
76
78
  | `3837f6d586a3` | 2x | Copy the task prompt to your clipboard | modules/hall-ui/public/goals-render.js:255, modules/hall-ui/public/work.js:356 |
77
- | `e3fd1febf63d` | 2x | Could not copy to the clipboard. Try again. | modules/hall-ui/public/goals-page.js:323, modules/hall-ui/public/work.js:461 |
78
- | `7775fec0fa85` | 2x | Could not load sessions. | modules/hall-ui/public/sessions.js:168, modules/hall-ui/public/sessions.js:207 |
79
- | … | | _87 more in the JSON registry_ | |
79
+ | … | | _89 more in the JSON registry_ | |
80
80
 
81
- ## `builders-hall` — 1096 strings
81
+ ## `builders-hall` — 1108 strings
82
82
 
83
83
  | id | conf | text | where |
84
84
  |---|---|---|---|
@@ -166,6 +166,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
166
166
  | `4195d0d114e4` | certain | Archons only. | `modules/hall-ui/public/watch.js:838` |
167
167
  | `5fcfdf8975cf` | certain | As configuration: | `modules/hall-ui/public/government.js:633` |
168
168
  | `01a0f29ff406` | certain | As first filed | `modules/hall-ui/public/idea.js:229` |
169
+ | `688cc6dd38ee` | certain | Asked of you | `modules/hall-ui/public/collab.html:32` |
170
+ | `b2656d0b2402` | certain | Asked {…} | `modules/hall-ui/public/collab.js:36` |
169
171
  | `b2656d0b2402` | certain | Asked {…} | `modules/hall-ui/public/watch.js:205` |
170
172
  | `8c8ec2597036` | certain | Assigned {…} to builder {…}. | `modules/hall-ui/public/government.js:401` |
171
173
  | `46714066f2da` | certain | Audit log | `modules/hall-ui/public/watch.html:127` |
@@ -234,6 +236,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
234
236
  | `cfe0f4c4fcc2` | certain | Closing your box… (a few seconds) | `modules/hall-ui/public/box-panel.js:370` |
235
237
  | `92ca0a0e5924` | certain | Code → Environment → “{…} Dev Box” | `modules/hall-ui/public/hall-render.js:609` |
236
238
  | `530d83153e17` | certain | Codebase detail | `modules/hall-ui/public/atlas.html:41` |
239
+ | `c72c97dc33fe` | certain | Collab — {{worldName}} | `modules/hall-ui/public/collab.html:6` |
240
+ | `c72c97dc33fe` | certain | Collab — {{worldName}} | `modules/hall-ui/public/collab.html:6` |
237
241
  | `2867c85d196d` | certain | Confirm that verification for #{…} actually passed? | `modules/hall-ui/public/task.js:290` |
238
242
  | `9b16a22bf24c` | certain | Connect Discord & join the server | `modules/hall-ui/public/hall-render.js:814` |
239
243
  | `9b16a22bf24c` | certain | Connect Discord & join the server | `modules/hall-ui/public/settings.js:826` |
@@ -279,6 +283,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
279
283
  | `589a4cfc5442` | certain | Could not load idea {…}. | `modules/hall-ui/public/ideas.js:444` |
280
284
  | `192c893fc64e` | certain | Could not load modules. | `modules/hall-ui/public/modules.js:226` |
281
285
  | `4e09b612c6b7` | certain | Could not load modules. Return to the hall and try again. | `modules/hall-ui/public/modules.js:225` |
286
+ | `8a4b590fa1a0` | certain | Could not load requests. | `modules/hall-ui/public/collab.js:137` |
282
287
  | `7775fec0fa85` | certain | Could not load sessions. | `modules/hall-ui/public/sessions.js:168` |
283
288
  | `7775fec0fa85` | certain | Could not load sessions. | `modules/hall-ui/public/sessions.js:207` |
284
289
  | `5420bb0b8475` | certain | Could not load the Board Room just now. | `modules/hall-ui/public/government.js:804` |
@@ -475,8 +480,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
475
480
  | `7ee76a958445` | certain | Join requests | `modules/hall-ui/public/goals-render.js:983` |
476
481
  | `b7ab5922126b` | certain | Join-requests to approve | `modules/hall-ui/public/goal-inbox.js:117` |
477
482
  | `d305f0df6f25` | certain | Jump to | `modules/hall-ui/public/palette.js:223` |
478
- | `2bfc5a50ce88` | certain | Jump to a page or record | `modules/hall-ui/public/shell.js:312` |
479
- | `eba610d25523` | certain | Jump to… | `modules/hall-ui/public/shell.js:314` |
483
+ | `2bfc5a50ce88` | certain | Jump to a page or record | `modules/hall-ui/public/shell.js:316` |
484
+ | `eba610d25523` | certain | Jump to… | `modules/hall-ui/public/shell.js:318` |
480
485
  | `bc779353aa0c` | certain | Karma — peer recognition and approvals, a separate counter from {…}. | `modules/hall-ui/public/profile.js:58` |
481
486
  | `32d25ef2182d` | certain | Keep box | `modules/hall-ui/public/box-panel.js:164` |
482
487
  | `fbe40b5818a3` | certain | Key removed. | `modules/hall-ui/public/settings.js:1088` |
@@ -595,6 +600,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
595
600
  | `0c59d118db9b` | certain | Note (optional) | `modules/hall-ui/public/task-detail.js:460` |
596
601
  | `95f8f8a4b288` | certain | Nothing flagged here. That is either good news or an unread queue — the duplicates below are the cheapest plac | `modules/hall-ui/public/copy-desk.js:172` |
597
602
  | `fec5d78497e8` | certain | Nothing in the backlog right now. | `modules/hall-ui/public/work.js:953` |
603
+ | `2c6e0dfbd781` | certain | Nothing is waiting on you. Requests reach you by your craft — set yours in | `modules/hall-ui/public/collab.html:38` |
598
604
  | `18ce0723be0d` | certain | Nothing is waiting — no held changes need your approval. | `modules/hall-ui/public/gate.js:188` |
599
605
  | `2ec0fbb20a42` | certain | Nothing is waiting. A spark appears here when its author offers a quick idea up for development. | `modules/hall-ui/public/ideas.js:357` |
600
606
  | `ad9a17a8a5eb` | certain | Nothing shipped yet. | `modules/hall-ui/public/profile.js:144` |
@@ -608,10 +614,11 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
608
614
  | `0bc1d1470e22` | certain | Only an open goal can be managed. | `modules/hall-ui/public/goals-render.js:935` |
609
615
  | `fa818ee444b4` | certain | Only this goal's owner or a manager (or an Archon) may do this. | `modules/hall-ui/public/goals-render.js:934` |
610
616
  | `544011db314a` | certain | Only this goal's owner or a manager (or an Archon) may invite. | `modules/hall-ui/public/goals-render.js:904` |
617
+ | `0c471e06cfc1` | certain | Only you can settle your own request — mark it answered when someone helped, or withdraw it when you no longer | `modules/hall-ui/public/collab.html:46` |
611
618
  | `6912678b962b` | certain | Open details for task {…} | `modules/hall-ui/public/goals-render.js:262` |
612
619
  | `6912678b962b` | certain | Open details for task {…} | `modules/hall-ui/public/work.js:341` |
613
620
  | `261d123970e8` | certain | Open goal #{…} | `modules/hall-ui/public/roadmap.js:100` |
614
- | `3366b115217b` | certain | Open navigation | `modules/hall-ui/public/shell.js:303` |
621
+ | `3366b115217b` | certain | Open navigation | `modules/hall-ui/public/shell.js:307` |
615
622
  | `0f0b1e62148d` | certain | Open online environment | `modules/hall-ui/public/box-panel.js:421` |
616
623
  | `855277dff074` | certain | Open task #{…} | `modules/hall-ui/public/goals-render.js:311` |
617
624
  | `855277dff074` | certain | Open task #{…} | `modules/hall-ui/public/work.js:963` |
@@ -707,6 +714,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
707
714
  | `e86e43684bce` | certain | Request sent — the owner or a manager will review it. | `modules/hall-ui/public/goals-render.js:942` |
708
715
  | `88b36bd9368b` | certain | Request to join | `modules/hall-ui/public/my-projects.html:47` |
709
716
  | `280f20e7ddf5` | certain | Request withdrawn. | `modules/hall-ui/public/goals-render.js:957` |
717
+ | `61f862ffb9eb` | certain | Requests addressed to your craft, or to you by name. Answering is optional and declining costs nothing — nobod | `modules/hall-ui/public/collab.html:33` |
710
718
  | `a030881168b5` | certain | Reset to default | `modules/hall-ui/public/government.js:217` |
711
719
  | `fd89b5be9d3b` | certain | Reset zoom to fit | `modules/hall-ui/public/diagram-viewer.js:97` |
712
720
  | `9d9567d1dead` | certain | Reset {…} to its default permissions?\n\nEvery change you've made to this rank is discarded, and it takes effe | `modules/hall-ui/public/government.js:245` |
@@ -767,12 +775,13 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
767
775
  | `b057dfbf49c5` | certain | Sign in when Claude prompts you. | `modules/hall-ui/public/hall-render.js:609` |
768
776
  | `e57ec24e13ef` | certain | Sign in with GitHub | `modules/hall-ui/public/hall-render.js:252` |
769
777
  | `e57ec24e13ef` | certain | Sign in with GitHub | `modules/hall-ui/public/hall-render.js:355` |
770
- | `e57ec24e13ef` | certain | Sign in with GitHub | `modules/hall-ui/public/shell.js:273` |
771
- | `1883ad7f07fc` | certain | Sign out | `modules/hall-ui/public/shell.js:291` |
772
- | `1883ad7f07fc` | certain | Sign out | `modules/hall-ui/public/shell.js:426` |
778
+ | `e57ec24e13ef` | certain | Sign in with GitHub | `modules/hall-ui/public/shell.js:277` |
779
+ | `1883ad7f07fc` | certain | Sign out | `modules/hall-ui/public/shell.js:295` |
780
+ | `1883ad7f07fc` | certain | Sign out | `modules/hall-ui/public/shell.js:430` |
773
781
  | `ecc12c1dd4de` | certain | Skill voices | `modules/hall-ui/public/settings.html:244` |
774
782
  | `688ca7b6e30f` | certain | Slop — reads as machine-written | `modules/hall-ui/public/copy-desk.html:92` |
775
783
  | `82df09a08476` | certain | So the Archon knows who you are | `modules/hall-ui/public/hall-render.js:252` |
784
+ | `7da6a848cd26` | certain | Someone helped | `modules/hall-ui/public/collab.js:65` |
776
785
  | `867df58c9aaa` | certain | Something else | `modules/hall-ui/public/copy-desk.html:97` |
777
786
  | `7e4b5d31df51` | certain | Sort by karma (peer recognition + approvals) | `modules/hall-ui/public/hall-widgets.js:210` |
778
787
  | `f8ca223d8864` | certain | Sort by rank | `modules/hall-ui/public/hall-widgets.js:208` |
@@ -787,7 +796,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
787
796
  | `6c6544f5c011` | certain | Stored encrypted; never shown again after saving — only its last four digits. | `modules/hall-ui/public/settings.html:146` |
788
797
  | `1dd8f7dd6139` | certain | Submit "{…}" for review | `modules/hall-ui/public/modules.js:159` |
789
798
  | `d71b721636ae` | certain | Submit one of this project's own modules for review, so it can join the shared Cloud Bongos catalog with your | `modules/hall-ui/public/modules.html:55` |
790
- | `1e40f33483eb` | certain | Switch theme | `modules/hall-ui/public/shell.js:318` |
799
+ | `1e40f33483eb` | certain | Switch theme | `modules/hall-ui/public/shell.js:322` |
791
800
  | `93fa2e97dd35` | certain | System default for this skill | `modules/hall-ui/public/settings.js:74` |
792
801
  | `1cc5e12dde31` | certain | System oversight. Every panel below is Archon-only. | `modules/hall-ui/public/watch.js:897` |
793
802
  | `5f45e431346f` | certain | Task #{…} | `modules/hall-ui/public/task.js:176` |
@@ -802,7 +811,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
802
811
  | `2fc66e722e33` | certain | Tasks open once you have read the builder primer. | `modules/hall-ui/public/work.html:38` |
803
812
  | `44579eef0c8d` | certain | Tasks — {{worldName}} | `modules/hall-ui/public/work.html:6` |
804
813
  | `44579eef0c8d` | certain | Tasks — {{worldName}} | `modules/hall-ui/public/work.html:6` |
805
- | `2f4f047dc1b4` | certain | Text size | `modules/hall-ui/public/shell.js:317` |
814
+ | `2f4f047dc1b4` | certain | Text size | `modules/hall-ui/public/shell.js:321` |
806
815
  | `4a8d1beb6f98` | certain | That project isn’t on the platform. | `modules/hall-ui/public/my-projects.js:134` |
807
816
  | `c0e28f30524d` | certain | That project's own standing, as reported by that project. | `modules/hall-ui/public/profile.js:224` |
808
817
  | `50198c465225` | certain | The 16-value rank enum in three bands, which ranks are live, and the permissions each rank holds. | `modules/hall-ui/public/diagrams.html:68` |
@@ -927,12 +936,14 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
927
936
  | `b31fc908c962` | certain | You claimed #{…}. Open a terminal to start work. | `modules/hall-ui/public/goals-page.js:464` |
928
937
  | `b31fc908c962` | certain | You claimed #{…}. Open a terminal to start work. | `modules/hall-ui/public/work.js:751` |
929
938
  | `b31fc908c962` | certain | You claimed #{…}. Open a terminal to start work. | `modules/hall-ui/public/work.js:821` |
939
+ | `da274462bb30` | certain | You have no open asks. | `modules/hall-ui/public/collab.html:51` |
930
940
  | `3e5b3271bdb4` | certain | You have not voted on this item. Casting arrives with the vote form. | `modules/hall-ui/public/government.js:785` |
931
941
  | `6b759151f722` | certain | You have reached the end. The button below is now active. | `modules/hall-ui/public/primer.js:529` |
932
942
  | `6d5a4ca58dd6` | certain | You haven’t joined any goals yet. | `modules/hall-ui/public/goals.js:83` |
933
943
  | `e82ec651f80a` | certain | You voted | `modules/hall-ui/public/government.js:784` |
934
944
  | `8e1915b19274` | certain | You're in the {…} server with your rank role. | `modules/hall-ui/public/settings.js:820` |
935
945
  | `6a4ca8ccf466` | certain | You're not signed in yet. Sign in to manage your preferences. | `modules/hall-ui/public/settings.html:34` |
946
+ | `7d26333b5fb0` | certain | Your asks | `modules/hall-ui/public/collab.html:45` |
936
947
  | `59a66b5ac5dd` | certain | Your box is up — waiting for the terminal to come online (a few seconds after boot)… | `modules/hall-ui/public/box-panel.js:282` |
937
948
  | `f96bc227dba0` | certain | Your choice | `modules/hall-ui/public/settings.html:253` |
938
949
  | `ec388f700f01` | certain | Your dev box | `modules/hall-ui/public/settings.html:156` |
@@ -1011,6 +1022,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
1011
1022
  | `dacb901c4f10` | certain | idea number | `modules/hall-ui/public/task-detail.js:458` |
1012
1023
  | `7df22e74c87a` | certain | idea {…} | `modules/hall-ui/public/idea.js:190` |
1013
1024
  | `7df22e74c87a` | certain | idea {…} | `modules/hall-ui/public/ideas.js:326` |
1025
+ | `2123a62d69e3` | certain | if this stays empty. | `modules/hall-ui/public/collab.html:40` |
1014
1026
  | `b2b399fdd6a4` | certain | in progress | `modules/hall-ui/public/goals-render.js:188` |
1015
1027
  | `b2b399fdd6a4` | certain | in progress | `modules/hall-ui/public/roadmap.js:108` |
1016
1028
  | `b2b399fdd6a4` | certain | in progress | `modules/hall-ui/public/roadmap.js:194` |
@@ -3,17 +3,17 @@
3
3
  "generator": "scripts/gds/copy-inventory.js",
4
4
  "goal": 1000074,
5
5
  "counts": {
6
- "strings": 1385,
7
- "unique_ids": 1228,
8
- "certain": 1385,
6
+ "strings": 1397,
7
+ "unique_ids": 1238,
8
+ "certain": 1397,
9
9
  "heuristic": 0,
10
- "duplicate_ids": 127
10
+ "duplicate_ids": 129
11
11
  },
12
12
  "surfaces": {
13
13
  "builders-hall": {
14
14
  "at": "modules/hall-ui/public",
15
- "files": 67,
16
- "strings": 1096
15
+ "files": 70,
16
+ "strings": 1108
17
17
  },
18
18
  "status-dashboard": {
19
19
  "at": "modules/status-ui/public",
@@ -191,7 +191,7 @@
191
191
  "at": [
192
192
  "modules/hall-ui/public/hall-render.js:252",
193
193
  "modules/hall-ui/public/hall-render.js:355",
194
- "modules/hall-ui/public/shell.js:273"
194
+ "modules/hall-ui/public/shell.js:277"
195
195
  ]
196
196
  },
197
197
  {
@@ -338,6 +338,16 @@
338
338
  "modules/hall-ui/public/watch.html:31"
339
339
  ]
340
340
  },
341
+ {
342
+ "id": "b2656d0b2402",
343
+ "surface": "builders-hall",
344
+ "text": "Asked {…}",
345
+ "occurrences": 2,
346
+ "at": [
347
+ "modules/hall-ui/public/collab.js:36",
348
+ "modules/hall-ui/public/watch.js:205"
349
+ ]
350
+ },
341
351
  {
342
352
  "id": "08afb1257b71",
343
353
  "surface": "builders-hall",
@@ -398,6 +408,16 @@
398
408
  "modules/hall-ui/public/box-panel.js:370"
399
409
  ]
400
410
  },
411
+ {
412
+ "id": "c72c97dc33fe",
413
+ "surface": "builders-hall",
414
+ "text": "Collab — {{worldName}}",
415
+ "occurrences": 2,
416
+ "at": [
417
+ "modules/hall-ui/public/collab.html:6",
418
+ "modules/hall-ui/public/collab.html:6"
419
+ ]
420
+ },
401
421
  {
402
422
  "id": "9b16a22bf24c",
403
423
  "surface": "builders-hall",
@@ -1064,8 +1084,8 @@
1064
1084
  "text": "Sign out",
1065
1085
  "occurrences": 2,
1066
1086
  "at": [
1067
- "modules/hall-ui/public/shell.js:291",
1068
- "modules/hall-ui/public/shell.js:426"
1087
+ "modules/hall-ui/public/shell.js:295",
1088
+ "modules/hall-ui/public/shell.js:430"
1069
1089
  ]
1070
1090
  },
1071
1091
  {
@@ -2096,6 +2116,24 @@
2096
2116
  "origin": "js-markup",
2097
2117
  "confidence": "certain"
2098
2118
  },
2119
+ {
2120
+ "id": "688cc6dd38ee",
2121
+ "surface": "builders-hall",
2122
+ "text": "Asked of you",
2123
+ "file": "modules/hall-ui/public/collab.html",
2124
+ "line": 32,
2125
+ "origin": "html-text",
2126
+ "confidence": "certain"
2127
+ },
2128
+ {
2129
+ "id": "b2656d0b2402",
2130
+ "surface": "builders-hall",
2131
+ "text": "Asked {…}",
2132
+ "file": "modules/hall-ui/public/collab.js",
2133
+ "line": 36,
2134
+ "origin": "js-markup",
2135
+ "confidence": "certain"
2136
+ },
2099
2137
  {
2100
2138
  "id": "b2656d0b2402",
2101
2139
  "surface": "builders-hall",
@@ -2708,6 +2746,24 @@
2708
2746
  "origin": "html-attr:aria-label",
2709
2747
  "confidence": "certain"
2710
2748
  },
2749
+ {
2750
+ "id": "c72c97dc33fe",
2751
+ "surface": "builders-hall",
2752
+ "text": "Collab — {{worldName}}",
2753
+ "file": "modules/hall-ui/public/collab.html",
2754
+ "line": 6,
2755
+ "origin": "html-text",
2756
+ "confidence": "certain"
2757
+ },
2758
+ {
2759
+ "id": "c72c97dc33fe",
2760
+ "surface": "builders-hall",
2761
+ "text": "Collab — {{worldName}}",
2762
+ "file": "modules/hall-ui/public/collab.html",
2763
+ "line": 6,
2764
+ "origin": "html-title",
2765
+ "confidence": "certain"
2766
+ },
2711
2767
  {
2712
2768
  "id": "2867c85d196d",
2713
2769
  "surface": "builders-hall",
@@ -3113,6 +3169,15 @@
3113
3169
  "origin": "js-text-assign",
3114
3170
  "confidence": "certain"
3115
3171
  },
3172
+ {
3173
+ "id": "8a4b590fa1a0",
3174
+ "surface": "builders-hall",
3175
+ "text": "Could not load requests.",
3176
+ "file": "modules/hall-ui/public/collab.js",
3177
+ "line": 137,
3178
+ "origin": "js-text-assign",
3179
+ "confidence": "certain"
3180
+ },
3116
3181
  {
3117
3182
  "id": "7775fec0fa85",
3118
3183
  "surface": "builders-hall",
@@ -4882,7 +4947,7 @@
4882
4947
  "surface": "builders-hall",
4883
4948
  "text": "Jump to a page or record",
4884
4949
  "file": "modules/hall-ui/public/shell.js",
4885
- "line": 312,
4950
+ "line": 316,
4886
4951
  "origin": "js-markup",
4887
4952
  "confidence": "certain"
4888
4953
  },
@@ -4891,7 +4956,7 @@
4891
4956
  "surface": "builders-hall",
4892
4957
  "text": "Jump to…",
4893
4958
  "file": "modules/hall-ui/public/shell.js",
4894
- "line": 314,
4959
+ "line": 318,
4895
4960
  "origin": "js-markup",
4896
4961
  "confidence": "certain"
4897
4962
  },
@@ -5957,6 +6022,15 @@
5957
6022
  "origin": "js-markup",
5958
6023
  "confidence": "certain"
5959
6024
  },
6025
+ {
6026
+ "id": "2c6e0dfbd781",
6027
+ "surface": "builders-hall",
6028
+ "text": "Nothing is waiting on you. Requests reach you by your craft — set yours in",
6029
+ "file": "modules/hall-ui/public/collab.html",
6030
+ "line": 38,
6031
+ "origin": "html-text",
6032
+ "confidence": "certain"
6033
+ },
5960
6034
  {
5961
6035
  "id": "18ce0723be0d",
5962
6036
  "surface": "builders-hall",
@@ -6074,6 +6148,15 @@
6074
6148
  "origin": "js-notice",
6075
6149
  "confidence": "certain"
6076
6150
  },
6151
+ {
6152
+ "id": "0c471e06cfc1",
6153
+ "surface": "builders-hall",
6154
+ "text": "Only you can settle your own request — mark it answered when someone helped, or withdraw it when you no longer need it.",
6155
+ "file": "modules/hall-ui/public/collab.html",
6156
+ "line": 46,
6157
+ "origin": "html-text",
6158
+ "confidence": "certain"
6159
+ },
6077
6160
  {
6078
6161
  "id": "6912678b962b",
6079
6162
  "surface": "builders-hall",
@@ -6106,7 +6189,7 @@
6106
6189
  "surface": "builders-hall",
6107
6190
  "text": "Open navigation",
6108
6191
  "file": "modules/hall-ui/public/shell.js",
6109
- "line": 303,
6192
+ "line": 307,
6110
6193
  "origin": "js-markup",
6111
6194
  "confidence": "certain"
6112
6195
  },
@@ -6965,6 +7048,15 @@
6965
7048
  "origin": "js-notice",
6966
7049
  "confidence": "certain"
6967
7050
  },
7051
+ {
7052
+ "id": "61f862ffb9eb",
7053
+ "surface": "builders-hall",
7054
+ "text": "Requests addressed to your craft, or to you by name. Answering is optional and declining costs nothing — nobody is assigned by being asked.",
7055
+ "file": "modules/hall-ui/public/collab.html",
7056
+ "line": 33,
7057
+ "origin": "html-text",
7058
+ "confidence": "certain"
7059
+ },
6968
7060
  {
6969
7061
  "id": "a030881168b5",
6970
7062
  "surface": "builders-hall",
@@ -7510,7 +7602,7 @@
7510
7602
  "surface": "builders-hall",
7511
7603
  "text": "Sign in with GitHub",
7512
7604
  "file": "modules/hall-ui/public/shell.js",
7513
- "line": 273,
7605
+ "line": 277,
7514
7606
  "origin": "js-markup",
7515
7607
  "confidence": "certain"
7516
7608
  },
@@ -7519,7 +7611,7 @@
7519
7611
  "surface": "builders-hall",
7520
7612
  "text": "Sign out",
7521
7613
  "file": "modules/hall-ui/public/shell.js",
7522
- "line": 291,
7614
+ "line": 295,
7523
7615
  "origin": "js-markup",
7524
7616
  "confidence": "certain"
7525
7617
  },
@@ -7528,7 +7620,7 @@
7528
7620
  "surface": "builders-hall",
7529
7621
  "text": "Sign out",
7530
7622
  "file": "modules/hall-ui/public/shell.js",
7531
- "line": 426,
7623
+ "line": 430,
7532
7624
  "origin": "js-markup",
7533
7625
  "confidence": "certain"
7534
7626
  },
@@ -7559,6 +7651,15 @@
7559
7651
  "origin": "js-markup",
7560
7652
  "confidence": "certain"
7561
7653
  },
7654
+ {
7655
+ "id": "7da6a848cd26",
7656
+ "surface": "builders-hall",
7657
+ "text": "Someone helped",
7658
+ "file": "modules/hall-ui/public/collab.js",
7659
+ "line": 65,
7660
+ "origin": "js-markup",
7661
+ "confidence": "certain"
7662
+ },
7562
7663
  {
7563
7664
  "id": "867df58c9aaa",
7564
7665
  "surface": "builders-hall",
@@ -7690,7 +7791,7 @@
7690
7791
  "surface": "builders-hall",
7691
7792
  "text": "Switch theme",
7692
7793
  "file": "modules/hall-ui/public/shell.js",
7693
- "line": 318,
7794
+ "line": 322,
7694
7795
  "origin": "js-markup",
7695
7796
  "confidence": "certain"
7696
7797
  },
@@ -7825,7 +7926,7 @@
7825
7926
  "surface": "builders-hall",
7826
7927
  "text": "Text size",
7827
7928
  "file": "modules/hall-ui/public/shell.js",
7828
- "line": 317,
7929
+ "line": 321,
7829
7930
  "origin": "js-markup",
7830
7931
  "confidence": "certain"
7831
7932
  },
@@ -8945,6 +9046,15 @@
8945
9046
  "origin": "js-notice",
8946
9047
  "confidence": "certain"
8947
9048
  },
9049
+ {
9050
+ "id": "da274462bb30",
9051
+ "surface": "builders-hall",
9052
+ "text": "You have no open asks.",
9053
+ "file": "modules/hall-ui/public/collab.html",
9054
+ "line": 51,
9055
+ "origin": "html-text",
9056
+ "confidence": "certain"
9057
+ },
8948
9058
  {
8949
9059
  "id": "3e5b3271bdb4",
8950
9060
  "surface": "builders-hall",
@@ -8999,6 +9109,15 @@
8999
9109
  "origin": "html-text",
9000
9110
  "confidence": "certain"
9001
9111
  },
9112
+ {
9113
+ "id": "7d26333b5fb0",
9114
+ "surface": "builders-hall",
9115
+ "text": "Your asks",
9116
+ "file": "modules/hall-ui/public/collab.html",
9117
+ "line": 45,
9118
+ "origin": "html-text",
9119
+ "confidence": "certain"
9120
+ },
9002
9121
  {
9003
9122
  "id": "59a66b5ac5dd",
9004
9123
  "surface": "builders-hall",
@@ -9701,6 +9820,15 @@
9701
9820
  "origin": "js-markup",
9702
9821
  "confidence": "certain"
9703
9822
  },
9823
+ {
9824
+ "id": "2123a62d69e3",
9825
+ "surface": "builders-hall",
9826
+ "text": "if this stays empty.",
9827
+ "file": "modules/hall-ui/public/collab.html",
9828
+ "line": 40,
9829
+ "origin": "html-text",
9830
+ "confidence": "certain"
9831
+ },
9704
9832
  {
9705
9833
  "id": "b2b399fdd6a4",
9706
9834
  "surface": "builders-hall",
@@ -0,0 +1,77 @@
1
+ // modules/hall-ui/public/collab-lib.js — the Collab page's PURE helpers, exposed
2
+ // as window.OTBCollabLib (task 1003172 / C02, goal 1000075).
3
+ //
4
+ // Same split as goals-lib.js: everything here is a pure function of its
5
+ // arguments, so the rules can be executed by a test instead of grepped out of a
6
+ // DOM file. Loaded by collab.html BEFORE collab.js.
7
+ //
8
+ // THE RULE THAT MATTERS IS THE AGEING ONE. The criterion says a request nobody
9
+ // takes must be "visibly aging rather than silently dead" — so age is a BAND with
10
+ // its own label and class, not a sort order. Sorting alone fails the criterion:
11
+ // the oldest request looks identical to the newest, just lower down, and "nobody
12
+ // answered this for nine days" is exactly the fact the page exists to make
13
+ // impossible to miss.
14
+ (function () {
15
+ 'use strict';
16
+
17
+ // Bands, oldest-first so the first match wins. Deliberately coarse: an exact
18
+ // hour count invites reading precision into a signal that only needs to say
19
+ // "this has been sitting a while".
20
+ var BANDS = [
21
+ { id: 'stale', minDays: 7, label: 'waiting over a week', severity: 3 },
22
+ { id: 'aging', minDays: 3, label: 'waiting a few days', severity: 2 },
23
+ { id: 'waiting', minDays: 1, label: 'waiting a day', severity: 1 },
24
+ { id: 'fresh', minDays: 0, label: 'just asked', severity: 0 },
25
+ ];
26
+
27
+ var DAY_MS = 24 * 60 * 60 * 1000;
28
+
29
+ // ageBand(createdAt, nowMs) -> band. `nowMs` is injected so this is testable
30
+ // without a clock, and so a test can assert the boundaries rather than trust
31
+ // them. An unparseable date reads as 'fresh': an unknown age must never be
32
+ // rendered as an accusation.
33
+ function ageBand(createdAt, nowMs) {
34
+ var then = createdAt ? Date.parse(createdAt) : NaN;
35
+ var now = typeof nowMs === 'number' ? nowMs : Date.now();
36
+ if (isNaN(then)) return BANDS[BANDS.length - 1];
37
+ var days = (now - then) / DAY_MS;
38
+ for (var i = 0; i < BANDS.length; i += 1) {
39
+ if (days >= BANDS[i].minDays) return BANDS[i];
40
+ }
41
+ return BANDS[BANDS.length - 1];
42
+ }
43
+
44
+ // Oldest first WITHIN the page, because the thing most at risk of dying
45
+ // silently should be read first. This is in ADDITION to the band, never
46
+ // instead of it.
47
+ function byAgeOldestFirst(a, b) {
48
+ var ta = a && a.created_at ? Date.parse(a.created_at) : 0;
49
+ var tb = b && b.created_at ? Date.parse(b.created_at) : 0;
50
+ return (ta || 0) - (tb || 0);
51
+ }
52
+
53
+ // Who a request is addressed to, in words. A craft request and a by-name
54
+ // request read differently on purpose: "asked of builders" is a call anyone of
55
+ // that craft may take, "asked of you" is personal and should feel it.
56
+ function addressedTo(req, viewerId) {
57
+ if (req && req.needs_builder_id != null) {
58
+ return String(req.needs_builder_id) === String(viewerId) ? 'asked of you by name' : 'asked of one person';
59
+ }
60
+ if (req && req.needs_craft) return 'asked of ' + req.needs_craft + 's';
61
+ return 'asked';
62
+ }
63
+
64
+ // What the request is about, if anything. Returns null rather than an empty
65
+ // string so a caller can skip the element entirely.
66
+ function contextOf(req) {
67
+ if (!req) return null;
68
+ if (req.task_id != null) return { kind: 'task', id: req.task_id, href: '/builders/task/' + req.task_id };
69
+ if (req.goal_id != null) return { kind: 'goal', id: req.goal_id, href: '/builders/goals#/goal/' + req.goal_id };
70
+ return null;
71
+ }
72
+
73
+ window.OTBCollabLib = {
74
+ BANDS: BANDS, ageBand: ageBand, byAgeOldestFirst: byAgeOldestFirst,
75
+ addressedTo: addressedTo, contextOf: contextOf,
76
+ };
77
+ })();
@@ -0,0 +1,36 @@
1
+ /* modules/hall-ui/public/collab.css — the Collab page (task 1003172 / C02).
2
+ Tokens only from style.css's real set: inventing one renders as nothing, which
3
+ is how copy-desk.css ended up with invisible fallbacks (idea 1000821). */
4
+
5
+ .cb-list { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
6
+
7
+ .cb-item {
8
+ border: 1px solid var(--rule);
9
+ border-left: 3px solid var(--rule);
10
+ border-radius: var(--radius-sm);
11
+ background: var(--surface);
12
+ padding: 10px 12px;
13
+ }
14
+
15
+ .cb-item__stuck { margin: 0; color: var(--ink); font-size: 14px; line-height: 1.5; white-space: pre-wrap; }
16
+ .cb-item__meta { margin: 6px 0 0; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; font-size: 12px; }
17
+ .cb-item__actions { margin: 8px 0 0; display: flex; gap: 8px; }
18
+ .cb-who { color: var(--ink-soft); }
19
+ .cb-ctx { color: var(--accent); text-decoration: none; }
20
+ .cb-ctx:hover { text-decoration: underline; }
21
+ .cb-empty { color: var(--ink-faint); font-size: 13px; margin: 10px 0 0; }
22
+
23
+ /* THE AGEING SIGNAL. The criterion asks that a request nobody takes be visibly
24
+ aging rather than silently dead — so age changes how a row LOOKS, not only
25
+ where it sits. The left border carries it (a band of colour that reads at a
26
+ glance down a list) and the chip says it in words, because colour alone is not
27
+ a message and not everyone sees it. */
28
+ .cb-age { color: var(--ink-faint); font-size: 11.5px; letter-spacing: 0.01em; }
29
+ .cb-item--fresh { border-left-color: var(--rule); }
30
+ .cb-item--waiting { border-left-color: var(--ink-faint); }
31
+ .cb-item--aging { border-left-color: var(--warn); }
32
+ .cb-item--stale { border-left-color: var(--danger); }
33
+ .cb-item--aging .cb-age { color: var(--warn); }
34
+ .cb-item--stale .cb-age { color: var(--danger); font-weight: 600; }
35
+
36
+ .btn--quiet { background: transparent; color: var(--ink-soft); border-color: var(--rule); }
@@ -0,0 +1,66 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Collab — {{worldName}}</title>
7
+ <meta name="description" content="Requests for help addressed to your craft or to you by name, and the asks you have open — with how long each has been waiting.">
8
+ <link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
12
+ <link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
13
+ <link rel="stylesheet" href="/builders/collab.css">
14
+ </head>
15
+ <body data-page="collab" data-page-title="Collab">
16
+
17
+ <div class="app">
18
+ <aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
19
+ <div class="app-scrim" id="app-scrim" hidden></div>
20
+ <div class="app-body">
21
+ <header class="app-topbar" id="app-topbar"></header>
22
+ <div class="app-content">
23
+ <div class="page-head">
24
+ <p class="page-head__sub" id="cb-sub">Loading…</p>
25
+ </div>
26
+
27
+ <main>
28
+ <!-- ASKED OF YOU comes first: it is the half where someone is waiting on a
29
+ human. Your own asks are below, because they are a status check, not a
30
+ call to act. -->
31
+ <section class="scroll" aria-labelledby="cb-for-me-h">
32
+ <h2 class="scroll__h" id="cb-for-me-h">Asked of you</h2>
33
+ <p class="scroll__note" id="cb-for-me-note">
34
+ Requests addressed to your craft, or to you by name. Answering is optional
35
+ and declining costs nothing — nobody is assigned by being asked.
36
+ </p>
37
+ <ul class="cb-list" id="cb-for-me"></ul>
38
+ <p class="cb-empty" id="cb-for-me-empty" hidden>
39
+ Nothing is waiting on you. Requests reach you by your craft — set yours in
40
+ <a href="/builders/settings">Settings</a> if this stays empty.
41
+ </p>
42
+ </section>
43
+
44
+ <section class="scroll" aria-labelledby="cb-mine-h">
45
+ <h2 class="scroll__h" id="cb-mine-h">Your asks</h2>
46
+ <p class="scroll__note">
47
+ Only you can settle your own request — mark it answered when someone
48
+ helped, or withdraw it when you no longer need it.
49
+ </p>
50
+ <ul class="cb-list" id="cb-mine"></ul>
51
+ <p class="cb-empty" id="cb-mine-empty" hidden>You have no open asks.</p>
52
+ </section>
53
+ </main>
54
+ </div>
55
+ </div>
56
+ </div>
57
+
58
+ <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
59
+
60
+ <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
61
+ <script src="/builders/palette.js" defer></script>
62
+ <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
63
+ <script src="/builders/collab-lib.js?v=2026-08-24-c02"></script>
64
+ <script src="/builders/collab.js?v=2026-08-24-c02"></script>
65
+ </body>
66
+ </html>
@@ -0,0 +1,139 @@
1
+ // modules/hall-ui/public/collab.js — the Collab page (/builders/collab).
2
+ // Task 1003172 / C02 of goal 1000075, criterion collab-page-matches-my-craft.
3
+ //
4
+ // The hall surface for C01's help requests. Two sections, in this order on
5
+ // purpose: what is ASKED OF YOU first, because someone is waiting on a human
6
+ // answer, then YOUR OWN ASKS, which are a status check rather than a call to act.
7
+ //
8
+ // THE PART THAT IS EASY TO SKIP, and the criterion names it explicitly: a request
9
+ // nobody takes must be VISIBLY AGING, not silently dead. Sorting the oldest to the
10
+ // top does not satisfy that — the oldest then looks exactly like the newest, only
11
+ // lower. So every row carries an age BAND with its own label and styling, and the
12
+ // rules for it live in collab-lib.js where a test can execute them.
13
+ //
14
+ // MATCHING IS THE SERVER'S JOB, not this file's. GET /help-requests/for-me derives
15
+ // the viewer's crafts from their own profile; sending a craft from here would be
16
+ // the privacy hole C01's grader found (a ?craft= param that let anyone read
17
+ // another craft's requests). This page never names a craft in a request.
18
+ (() => {
19
+ 'use strict';
20
+
21
+ // NO baseUrl OVERRIDE. The generated client's methods (api.helpRequests.*, api.me.*)
22
+ // emit API-RELATIVE paths like '/help-requests/for-me'. Passing baseUrl:'' means
23
+ // "these paths are already absolute" and sends them to the site root, where nothing
24
+ // is mounted — every call 404s. Letting it default resolves them against
25
+ // '/api/bongos/v1' (api-prefix.js VERSIONED_API_PREFIX), which is where the router
26
+ // actually mounts. Pages that DO pass baseUrl:'' must build full paths themselves
27
+ // via api.request('GET', `${API}/...`) — the two styles must never be mixed.
28
+ const api = window.BongosClient.createClient({ credentials: 'same-origin', throwOnError: false });
29
+ const { escapeHtml, $, toast } = window.OTB;
30
+ const L = window.OTBCollabLib;
31
+
32
+ let settling = false;
33
+
34
+ function ageChip(req, nowMs) {
35
+ const band = L.ageBand(req.created_at, nowMs);
36
+ return `<span class="cb-age cb-age--${band.id}" title="Asked ${escapeHtml(String(req.created_at || ''))}">${escapeHtml(band.label)}</span>`;
37
+ }
38
+
39
+ function contextChip(req) {
40
+ const ctx = L.contextOf(req);
41
+ if (!ctx) return '';
42
+ return `<a class="cb-ctx" href="${escapeHtml(ctx.href)}">${escapeHtml(ctx.kind)} #${escapeHtml(String(ctx.id))}</a>`;
43
+ }
44
+
45
+ // A row for the "asked of you" list. There is deliberately NO "claim this"
46
+ // button: answering is a conversation, and inventing a reply thread here would
47
+ // be reimplementing the Agora, which this goal explicitly forbids (C06 owns the
48
+ // chat mirror). What the page owes a helper is the request, its age, its
49
+ // context, and who to answer.
50
+ function forMeRow(req, viewerId, nowMs) {
51
+ const who = L.addressedTo(req, viewerId);
52
+ return `<li class="cb-item cb-item--${L.ageBand(req.created_at, nowMs).id}">
53
+ <p class="cb-item__stuck">${escapeHtml(req.what_is_stuck || '')}</p>
54
+ <p class="cb-item__meta">
55
+ <span class="cb-who">${escapeHtml(who)}</span>
56
+ ${contextChip(req)}
57
+ ${ageChip(req, nowMs)}
58
+ </p>
59
+ </li>`;
60
+ }
61
+
62
+ // Your own ask, with the two settle actions. Only the asker can settle — the
63
+ // server enforces it in the UPDATE's WHERE, this is just the affordance.
64
+ function mineRow(req, nowMs) {
65
+ return `<li class="cb-item cb-item--${L.ageBand(req.created_at, nowMs).id}" data-id="${escapeHtml(String(req.id))}">
66
+ <p class="cb-item__stuck">${escapeHtml(req.what_is_stuck || '')}</p>
67
+ <p class="cb-item__meta">
68
+ <span class="cb-who">${escapeHtml(L.addressedTo(req, null))}</span>
69
+ ${contextChip(req)}
70
+ ${ageChip(req, nowMs)}
71
+ </p>
72
+ <p class="cb-item__actions">
73
+ <button class="btn btn--sm" data-settle="answered" data-id="${escapeHtml(String(req.id))}">Someone helped</button>
74
+ <button class="btn btn--sm btn--quiet" data-settle="withdrawn" data-id="${escapeHtml(String(req.id))}">Withdraw</button>
75
+ </p>
76
+ </li>`;
77
+ }
78
+
79
+ function renderList(hostSel, emptySel, rows, html) {
80
+ const host = $(hostSel);
81
+ const empty = $(emptySel);
82
+ if (!host) return;
83
+ host.innerHTML = rows.map(html).join('');
84
+ if (empty) empty.hidden = rows.length > 0;
85
+ }
86
+
87
+ async function load() {
88
+ const nowMs = Date.now();
89
+ const [forMe, mine, me] = await Promise.all([
90
+ api.helpRequests.getHelpRequestsForMe({}),
91
+ api.helpRequests.getHelpRequestsMine({ query: { status: 'open' } }),
92
+ api.me.getMe ? api.me.getMe({}) : Promise.resolve({ ok: false }),
93
+ ]);
94
+
95
+ const viewerId = me && me.ok && me.data && me.data.builder ? me.data.builder.id : null;
96
+
97
+ const forMeRows = ((forMe.ok && forMe.data && forMe.data.help_requests) || []).slice().sort(L.byAgeOldestFirst);
98
+ const mineRows = ((mine.ok && mine.data && mine.data.help_requests) || []).slice().sort(L.byAgeOldestFirst);
99
+
100
+ renderList('#cb-for-me', '#cb-for-me-empty', forMeRows, (r) => forMeRow(r, viewerId, nowMs));
101
+ renderList('#cb-mine', '#cb-mine-empty', mineRows, (r) => mineRow(r, nowMs));
102
+
103
+ const sub = $('#cb-sub');
104
+ if (sub) {
105
+ const crafts = (forMe.ok && forMe.data && forMe.data.crafts) || [];
106
+ sub.textContent = crafts.length
107
+ ? `Matching your craft: ${crafts.join(', ')}.`
108
+ : 'You have not set a craft, so only requests addressed to you by name reach you.';
109
+ }
110
+ }
111
+
112
+ async function settle(id, status) {
113
+ if (settling) return;
114
+ settling = true;
115
+ try {
116
+ const r = await api.helpRequests.patchHelpRequestsId({ id, body: { status } });
117
+ if (!r.ok) {
118
+ toast(window.OTB.errMessage ? window.OTB.errMessage(r.data) : 'Could not update that request.');
119
+ return;
120
+ }
121
+ toast(status === 'answered' ? 'Marked answered.' : 'Withdrawn.');
122
+ await load();
123
+ } finally {
124
+ settling = false;
125
+ }
126
+ }
127
+
128
+ document.addEventListener('click', (ev) => {
129
+ const btn = ev.target && ev.target.closest ? ev.target.closest('[data-settle]') : null;
130
+ if (!btn) return;
131
+ ev.preventDefault();
132
+ settle(Number(btn.getAttribute('data-id')), btn.getAttribute('data-settle'));
133
+ });
134
+
135
+ load().catch(() => {
136
+ const sub = $('#cb-sub');
137
+ if (sub) sub.textContent = 'Could not load requests.';
138
+ });
139
+ })();
@@ -153,6 +153,10 @@
153
153
  { id: 'profile', label: 'Profile', icon: 'profile', href: '/profile', gate: 'signed' },
154
154
  ] },
155
155
  { group: 'Community', gate: 'member', items: [
156
+ // task 1003172 (C02, goal 1000075): help requests addressed to you. In
157
+ // Community because the group is picked by WHOSE data it is, and this is
158
+ // other builders reaching you — not your own numbers (that is You).
159
+ { id: 'collab', label: 'Collab', icon: 'leaderboard', href: '/collab', gate: 'member' },
156
160
  { id: 'leaderboard', label: 'Leaderboard', icon: 'leaderboard', href: '/#leaderboard', view: 'leaderboard', gate: 'member' },
157
161
  // task 1002577: the cross-project builder directory (/scouting graduated
158
162
  // into the hall). Gated to MATCH its data — /api/gds/scouting checks
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.346",
3
+ "version": "1.19.347",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.346",
9
+ "version": "1.19.347",
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.346",
3
+ "version": "1.19.347",
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",
@@ -944,8 +944,11 @@ function mountInternalSurfaces(app) {
944
944
  // URL→gate map by parsing this file statically, and a comment in that gap drops
945
945
  // the regex out of its map — which reads as "every page on this tier is
946
946
  // ungated". It fails loudly by design; keep prose above the const.
947
+ // task 1003172 (C02): `collab` joins the same tier — every read behind it is
948
+ // any-builder and scoped to the caller by the SERVER (GET /help-requests/for-me
949
+ // derives crafts from the viewer's own profile), so signed-in is the right floor.
947
950
  const SIGNEDIN_BUILDERS_PAGE_RE =
948
- /^\/builders\/(?:work|pair|settings|atlas|modules|goals|roadmap|copy-desk|ideas|primer(?:\.md)?|diagrams)(?:\.html)?\/?$/;
951
+ /^\/builders\/(?:work|pair|settings|atlas|modules|goals|roadmap|copy-desk|ideas|collab|primer(?:\.md)?|diagrams)(?:\.html)?\/?$/;
949
952
  // The diagram image assets under /builders/diagrams/ ride the same tier as the
950
953
  // page that embeds them.
951
954
  const SIGNEDIN_BUILDERS_PREFIX_RE = /^\/builders\/diagrams\//;
package/src/module-api.js CHANGED
@@ -1122,8 +1122,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
1122
1122
  // landed since 1.19.344 with no explicit bump. run 32800819617. (task 1002620)
1123
1123
  // 1.19.346 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1124
1124
  // landed since 1.19.345 with no explicit bump. run 32801147433. (task 1002620)
1125
+ // 1.19.347 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1126
+ // landed since 1.19.346 with no explicit bump. run 32802265337. (task 1002620)
1125
1127
  // ---------------------------------------------------------------------------
1126
- const CORE_VERSION = '1.19.346'; // CI auto-patch carrier (ADR 0161); see changelog above
1128
+ const CORE_VERSION = '1.19.347'; // CI auto-patch carrier (ADR 0161); see changelog above
1127
1129
 
1128
1130
  // A namespaced logger so a module's log lines are attributable + consistent.
1129
1131
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -0,0 +1,172 @@
1
+ // tests/collab_page.mjs — the Collab page (task 1003172 / C02, goal 1000075,
2
+ // criterion collab-page-matches-my-craft).
3
+ //
4
+ // The criterion has three clauses and the third is the one that gets skipped:
5
+ // requests match the viewer's CRAFT, are ANSWERABLE from the page, and a request
6
+ // nobody takes is VISIBLY AGING rather than silently dead. Sorting the oldest to
7
+ // the top does not satisfy the third — the oldest then looks exactly like the
8
+ // newest, only lower down.
9
+ import { strict as assert } from 'node:assert';
10
+ import { test } from 'node:test';
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
16
+ const HALL = path.join(ROOT, 'modules', 'hall-ui', 'public');
17
+ const read = (...p) => fs.readFileSync(path.join(ROOT, ...p), 'utf8');
18
+
19
+ // collab-lib.js is pure, so RUN it rather than grep it.
20
+ function lib() {
21
+ const src = read('modules', 'hall-ui', 'public', 'collab-lib.js');
22
+ const win = {};
23
+ new Function('window', src)(win);
24
+ return win.OTBCollabLib;
25
+ }
26
+ const NOW = Date.parse('2026-08-24T12:00:00Z');
27
+ const daysAgo = (d) => new Date(NOW - d * 86400000).toISOString();
28
+
29
+ // ---- THE AGEING CLAUSE ----------------------------------------------------
30
+ test('age is a BAND with a label, not just a sort position', () => {
31
+ const L = lib();
32
+ assert.equal(L.ageBand(daysAgo(0), NOW).id, 'fresh');
33
+ assert.equal(L.ageBand(daysAgo(1), NOW).id, 'waiting');
34
+ assert.equal(L.ageBand(daysAgo(3), NOW).id, 'aging');
35
+ assert.equal(L.ageBand(daysAgo(7), NOW).id, 'stale');
36
+ assert.equal(L.ageBand(daysAgo(90), NOW).id, 'stale');
37
+ for (const b of L.BANDS) assert.ok(b.label && b.label.length > 4, `${b.id} says it in words`);
38
+ });
39
+
40
+ test('the boundaries are exact, so the bands mean something', () => {
41
+ const L = lib();
42
+ assert.equal(L.ageBand(daysAgo(0.99), NOW).id, 'fresh', 'just under a day is still fresh');
43
+ assert.equal(L.ageBand(daysAgo(2.99), NOW).id, 'waiting');
44
+ assert.equal(L.ageBand(daysAgo(6.99), NOW).id, 'aging');
45
+ });
46
+
47
+ test('an unknown age reads as fresh, never as an accusation', () => {
48
+ const L = lib();
49
+ for (const bad of [null, undefined, '', 'nonsense']) {
50
+ assert.equal(L.ageBand(bad, NOW).id, 'fresh', `${JSON.stringify(bad)} must not look neglected`);
51
+ }
52
+ });
53
+
54
+ test('ageing changes how a row LOOKS, not only where it sits', () => {
55
+ // The whole point of the clause. Each band must have its own visual treatment
56
+ // in CSS, or "visibly aging" is just a sort order with extra steps.
57
+ const css = read('modules', 'hall-ui', 'public', 'collab.css');
58
+ const L = lib();
59
+ for (const b of L.BANDS) {
60
+ assert.match(css, new RegExp(`\\.cb-item--${b.id}\\b`), `${b.id} has its own styling`);
61
+ }
62
+ assert.match(css, /\.cb-item--stale\s*\{[^}]*border-left-color/, 'the band reads down a list at a glance');
63
+ // Colour alone is not a message and not everyone sees it.
64
+ assert.match(css, /\.cb-item--stale \.cb-age/, 'and the words are emphasised too');
65
+ });
66
+
67
+ test('the row markup carries the band class and the label', () => {
68
+ const js = read('modules', 'hall-ui', 'public', 'collab.js');
69
+ assert.match(js, /cb-item--\$\{L\.ageBand\(/, 'the class comes from the band');
70
+ assert.match(js, /cb-age cb-age--\$\{band\.id\}/, 'and so does the chip');
71
+ assert.match(js, /escapeHtml\(band\.label\)/, 'the label is rendered, escaped');
72
+ });
73
+
74
+ test('oldest first WITHIN the page, in addition to the band', () => {
75
+ const L = lib();
76
+ const rows = [{ created_at: daysAgo(1) }, { created_at: daysAgo(9) }, { created_at: daysAgo(4) }];
77
+ const sorted = rows.slice().sort(L.byAgeOldestFirst);
78
+ assert.deepEqual(sorted.map((r) => L.ageBand(r.created_at, NOW).id), ['stale', 'aging', 'waiting']);
79
+ });
80
+
81
+ // ---- THE MATCHING CLAUSE --------------------------------------------------
82
+ test('the page NEVER sends a craft — matching is the server\'s job', () => {
83
+ // C01's grader found a ?craft= param that let any builder read another craft's
84
+ // requests. The fix put craft derivation on the server, and this page must not
85
+ // reintroduce the parameter from the client side.
86
+ const js = read('modules', 'hall-ui', 'public', 'collab.js');
87
+ // Assert on the REQUEST, not on any occurrence of the word: the first version
88
+ // matched `Matching your craft: ${crafts.join(', ')}` — the page's own subtitle —
89
+ // which is display text, not a parameter. Fifth time this session that a source
90
+ // assertion has been wider than its invariant (learning 1000196).
91
+ assert.match(js, /getHelpRequestsForMe\(\{\}\)/, 'the read is unparameterised');
92
+ assert.ok(!/(query|body):\s*\{[^}]*craft/.test(js), 'craft never travels in a query or body');
93
+ });
94
+
95
+ test('a craft request and a by-name request read differently', () => {
96
+ const L = lib();
97
+ assert.match(L.addressedTo({ needs_craft: 'artist' }, 1), /artists/);
98
+ assert.match(L.addressedTo({ needs_builder_id: 7 }, 7), /by name/);
99
+ assert.match(L.addressedTo({ needs_builder_id: 7 }, 9), /one person/);
100
+ });
101
+
102
+ test('context links to the task or goal, and to neither when absent', () => {
103
+ const L = lib();
104
+ assert.equal(L.contextOf({ task_id: 5 }).kind, 'task');
105
+ assert.equal(L.contextOf({ goal_id: 5 }).kind, 'goal');
106
+ assert.equal(L.contextOf({}), null, 'no context means no element, not an empty one');
107
+ });
108
+
109
+ // ---- THE ANSWERABLE CLAUSE ------------------------------------------------
110
+ test('the asker can settle in place, with both outcomes', () => {
111
+ const js = read('modules', 'hall-ui', 'public', 'collab.js');
112
+ assert.match(js, /data-settle="answered"/);
113
+ assert.match(js, /data-settle="withdrawn"/);
114
+ assert.match(js, /patchHelpRequestsId/, 'through the real API, not a local edit');
115
+ });
116
+
117
+ test('there is deliberately NO reply thread — that would reimplement the Agora', () => {
118
+ // The goal forbids reimplementing the Agora to get around C06's dependency.
119
+ const js = read('modules', 'hall-ui', 'public', 'collab.js');
120
+ for (const f of ['reply', 'comment', 'thread', 'message']) {
121
+ assert.ok(!new RegExp(`data-${f}|post${f}`, 'i').test(js), `no ${f} surface`);
122
+ }
123
+ });
124
+
125
+ // ---- placement ------------------------------------------------------------
126
+ test('it is a PAGE, not a Home card — the actionable half is the needs roster', () => {
127
+ // hall-ui/CLAUDE.md's tree: an action awaiting the builder goes in the
128
+ // needs-attention inbox and NEVER a new card, because one landing place for
129
+ // everything actionable is the whole point. C01 already put it there.
130
+ assert.ok(fs.existsSync(path.join(HALL, 'collab.html')));
131
+ const builders = read('modules', 'hall-ui', 'public', 'builders.js');
132
+ assert.ok(!/collab/i.test(builders), 'no Home card was added');
133
+ });
134
+
135
+ test('the page is behind the signed-in gate', () => {
136
+ const serve = read('src', 'bongos', 'routes', '..', 'serve-internal.js');
137
+ const m = /const SIGNEDIN_BUILDERS_PAGE_RE =\s*(\/[^\n]+\/);/.exec(serve);
138
+ assert.ok(m, 'the gate const is still statically parseable');
139
+ assert.ok(new RegExp(m[1].slice(1, -1)).test('/builders/collab'), 'collab is gated');
140
+ });
141
+
142
+ test('every CSS token the page uses exists in style.css', () => {
143
+ const css = read('modules', 'hall-ui', 'public', 'collab.css');
144
+ const style = read('modules', 'hall-ui', 'public', 'style.css');
145
+ const used = new Set([...css.matchAll(/var\((--[a-z-]+)\)/g)].map((x) => x[1]));
146
+ assert.ok(used.size >= 5);
147
+ for (const t of used) {
148
+ assert.ok(new RegExp(`^\\s*${t}:`, 'm').test(style), `${t} must be defined (idea 1000821)`);
149
+ }
150
+ });
151
+
152
+ // ---- THE CLIENT ACTUALLY REACHES THE API ----------------------------------
153
+ // The page called every endpoint through the GENERATED client (api.helpRequests.*),
154
+ // whose methods emit API-RELATIVE paths ('/help-requests/for-me'). Combined with
155
+ // baseUrl:'' — which the client documents as "these paths are already absolute" —
156
+ // every request went to the SITE ROOT and 404'd, so the page would have shipped
157
+ // dead on arrival. ideas.js and idea.js already shipped with exactly this defect.
158
+ //
159
+ // Grepped rather than executed on purpose: the defect is in URL CONSTRUCTION, and
160
+ // every existing test stubs the client, which is precisely why three pages shipped
161
+ // green while 404ing. A repo-wide version of this guard belongs in fitness.js once
162
+ // the two idea pages are fixed — scoped here to the page this task owns.
163
+ test('the client is not configured to send API calls to the site root', () => {
164
+ const js = read('modules', 'hall-ui', 'public', 'collab.js');
165
+ const namespaced = [...js.matchAll(/\bapi\.([a-zA-Z]\w*)\.([a-zA-Z]\w*)\(/g)];
166
+ assert.ok(namespaced.length > 0, 'this page uses the generated client');
167
+ assert.ok(
168
+ !/createClient\(\{[^}]*baseUrl:\s*(''|"")/.test(js),
169
+ "baseUrl:'' sends generated-client paths to the site root — drop it so they "
170
+ + 'resolve against /api/bongos/v1, or switch every call to api.request() with a full path',
171
+ );
172
+ });
@@ -149,7 +149,13 @@ test('sidebar groups read by audience, in order', () => {
149
149
  // Gated cosmetically to match its data endpoint — `project.curate`, floor METIC,
150
150
  // so the nav entry reads 'government' since ADR 0174 (it said 'archon', then
151
151
  // was stricter than the wall). requireProjectCuratePage is the wall server-side.
152
- assert.deepEqual(nav[2].items.map((i) => i.id), ['leaderboard', 'people'], 'Community = everyone else');
152
+ // task 1003172 (C02 of goal 1000075): 'collab' joins Community, FIRST. The
153
+ // group is picked by whose data it is, and a help request is another builder
154
+ // reaching you — not your own numbers (that is You), and not reference (Docs).
155
+ // First within the group because it is the one page here you ACT on; the
156
+ // leaderboard and the directory are browsing surfaces. Updating this oracle is
157
+ // the reviewed decision the pin exists to force.
158
+ assert.deepEqual(nav[2].items.map((i) => i.id), ['collab', 'leaderboard', 'people'], 'Community = everyone else');
153
159
  assert.deepEqual(nav[3].items.map((i) => i.id),
154
160
  ['primer', 'ranks', 'economy', 'diagrams', 'atlas', 'modules', 'apidocs'], 'Docs = reference material');
155
161
  // 'government' (the Permissions tab, BV1.R113) is the group's one archon-gated item.