@bongos/core 1.21.153 → 1.21.154
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 +22 -22
- package/docs/api/openapi.json +1 -1
- package/docs/copy-inventory.md +19 -17
- package/docs/copy-registry.json +38 -20
- package/docs/module-api-changelog.md +2 -0
- package/docs/modules-contract.md +1 -0
- package/docs/page-readings.json +22 -20
- package/modules/hall-ui/public/modules.html +4 -1
- package/modules/hall-ui/public/modules.js +32 -1
- package/modules/hall-ui/records/catalog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/bongos/module-listing.js +41 -5
- package/src/bongos/routes/modules.js +11 -3
- package/src/module-api.js +1 -1
- package/tests/hall_catalog_world.mjs +13 -1
- package/tests/module_store_listing.mjs +78 -2
package/.bongos-core.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.21.
|
|
6
|
-
"core_contract": "1.21.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.21.154",
|
|
6
|
+
"core_contract": "1.21.154",
|
|
7
|
+
"source_commit": "046cb72ed357309e34e58fb712b7efa7b56a4888",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-10-
|
|
9
|
+
"built_at": "2026-10-22T14:58:30.668Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 608,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 3808,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "9b227a8bf5b2795c452c985814ac1f22809900835d5148efee15c24a2203b4af",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2387,7 +2387,7 @@
|
|
|
2387
2387
|
{
|
|
2388
2388
|
"path": "docs/api/openapi.json",
|
|
2389
2389
|
"mode": "0000644",
|
|
2390
|
-
"sha256": "
|
|
2390
|
+
"sha256": "9402cb974357bea192c8fd919a1f49cc6e8f514b9948705f37ef5dafebca1278"
|
|
2391
2391
|
},
|
|
2392
2392
|
{
|
|
2393
2393
|
"path": "docs/architecture.md",
|
|
@@ -2412,12 +2412,12 @@
|
|
|
2412
2412
|
{
|
|
2413
2413
|
"path": "docs/copy-inventory.md",
|
|
2414
2414
|
"mode": "0000644",
|
|
2415
|
-
"sha256": "
|
|
2415
|
+
"sha256": "36192920b84dfe1a36853f586c7de9259b6ce62adba750ef73537a050604b6d7"
|
|
2416
2416
|
},
|
|
2417
2417
|
{
|
|
2418
2418
|
"path": "docs/copy-registry.json",
|
|
2419
2419
|
"mode": "0000644",
|
|
2420
|
-
"sha256": "
|
|
2420
|
+
"sha256": "0ce492be99e91cdf249acc1f49c2acfde87e652b5a73a95c1d12ff41865273ff"
|
|
2421
2421
|
},
|
|
2422
2422
|
{
|
|
2423
2423
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -2927,12 +2927,12 @@
|
|
|
2927
2927
|
{
|
|
2928
2928
|
"path": "docs/module-api-changelog.md",
|
|
2929
2929
|
"mode": "0000644",
|
|
2930
|
-
"sha256": "
|
|
2930
|
+
"sha256": "933a5d803257d84f6df3fa2fd66229f74e0463ba41f23256ab7db81c7a689ca8"
|
|
2931
2931
|
},
|
|
2932
2932
|
{
|
|
2933
2933
|
"path": "docs/modules-contract.md",
|
|
2934
2934
|
"mode": "0000644",
|
|
2935
|
-
"sha256": "
|
|
2935
|
+
"sha256": "4d0036450b55adc4539a9838f8478848e1cce16fa237b9207c10d08980a3d906"
|
|
2936
2936
|
},
|
|
2937
2937
|
{
|
|
2938
2938
|
"path": "docs/onboarding/diagrams/01-task-lifecycle.mmd",
|
|
@@ -3017,7 +3017,7 @@
|
|
|
3017
3017
|
{
|
|
3018
3018
|
"path": "docs/page-readings.json",
|
|
3019
3019
|
"mode": "0000644",
|
|
3020
|
-
"sha256": "
|
|
3020
|
+
"sha256": "5bfc13802503364ffb6812d85c052ec273404d5994affc1be4949dd819161b1f"
|
|
3021
3021
|
},
|
|
3022
3022
|
{
|
|
3023
3023
|
"path": "docs/project-context.template.md",
|
|
@@ -6162,12 +6162,12 @@
|
|
|
6162
6162
|
{
|
|
6163
6163
|
"path": "modules/hall-ui/public/modules.html",
|
|
6164
6164
|
"mode": "0000644",
|
|
6165
|
-
"sha256": "
|
|
6165
|
+
"sha256": "75a38104b51ea30550a616c8e709a3ec278bf153dedbd487acf89d247a61b19f"
|
|
6166
6166
|
},
|
|
6167
6167
|
{
|
|
6168
6168
|
"path": "modules/hall-ui/public/modules.js",
|
|
6169
6169
|
"mode": "0000644",
|
|
6170
|
-
"sha256": "
|
|
6170
|
+
"sha256": "6b5a48f2bb96e0478a7150265ded185e04d707b51f5ad7d3b813446115eb6142"
|
|
6171
6171
|
},
|
|
6172
6172
|
{
|
|
6173
6173
|
"path": "modules/hall-ui/public/modules.states.json",
|
|
@@ -6762,7 +6762,7 @@
|
|
|
6762
6762
|
{
|
|
6763
6763
|
"path": "modules/hall-ui/records/catalog.md",
|
|
6764
6764
|
"mode": "0000644",
|
|
6765
|
-
"sha256": "
|
|
6765
|
+
"sha256": "e760daf386c9e723843c573afcce713a1b18514684c57f58ad156f3a79d9b557"
|
|
6766
6766
|
},
|
|
6767
6767
|
{
|
|
6768
6768
|
"path": "modules/hall-ui/records/collaboration.md",
|
|
@@ -10557,12 +10557,12 @@
|
|
|
10557
10557
|
{
|
|
10558
10558
|
"path": "package-lock.json",
|
|
10559
10559
|
"mode": "0000644",
|
|
10560
|
-
"sha256": "
|
|
10560
|
+
"sha256": "8896ac22750733e9413d6ac302b78e85999a7878eb8ca8c1ec1af5e493abea42"
|
|
10561
10561
|
},
|
|
10562
10562
|
{
|
|
10563
10563
|
"path": "package.json",
|
|
10564
10564
|
"mode": "0000644",
|
|
10565
|
-
"sha256": "
|
|
10565
|
+
"sha256": "1a220fca30df53b3e1cc4ae053b91ec424e142b0e2aaf38bf53bf22cf444b5ba"
|
|
10566
10566
|
},
|
|
10567
10567
|
{
|
|
10568
10568
|
"path": "public-docs/index.html",
|
|
@@ -10582,7 +10582,7 @@
|
|
|
10582
10582
|
{
|
|
10583
10583
|
"path": "release-notes.json",
|
|
10584
10584
|
"mode": "0000644",
|
|
10585
|
-
"sha256": "
|
|
10585
|
+
"sha256": "3dbca36d0a1743a39859a3d3d26cf209db264cd6bd6c8588e7ebf1177be55b75"
|
|
10586
10586
|
},
|
|
10587
10587
|
{
|
|
10588
10588
|
"path": "scripts/cron/style_guide_audit.sh",
|
|
@@ -12692,7 +12692,7 @@
|
|
|
12692
12692
|
{
|
|
12693
12693
|
"path": "src/bongos/module-listing.js",
|
|
12694
12694
|
"mode": "0000644",
|
|
12695
|
-
"sha256": "
|
|
12695
|
+
"sha256": "2f2ac4e04af85579c9c2bfd6e6b8e880cff46cadf49ad3791948c9bca03c8d72"
|
|
12696
12696
|
},
|
|
12697
12697
|
{
|
|
12698
12698
|
"path": "src/bongos/module-overrides.js",
|
|
@@ -12872,7 +12872,7 @@
|
|
|
12872
12872
|
{
|
|
12873
12873
|
"path": "src/bongos/routes/modules.js",
|
|
12874
12874
|
"mode": "0000644",
|
|
12875
|
-
"sha256": "
|
|
12875
|
+
"sha256": "051144a0c2235022e7e423b6a48c2203dadb8142dedfb57edbbf6c5ba69206bb"
|
|
12876
12876
|
},
|
|
12877
12877
|
{
|
|
12878
12878
|
"path": "src/bongos/routes/my-passkeys.js",
|
|
@@ -12962,7 +12962,7 @@
|
|
|
12962
12962
|
{
|
|
12963
12963
|
"path": "src/module-api.js",
|
|
12964
12964
|
"mode": "0000644",
|
|
12965
|
-
"sha256": "
|
|
12965
|
+
"sha256": "39421d70df1f7628486cf17294e001fc06d49fa502847ca7786fff43fb2738c4"
|
|
12966
12966
|
},
|
|
12967
12967
|
{
|
|
12968
12968
|
"path": "src/module-loader/catalog.js",
|
|
@@ -15467,7 +15467,7 @@
|
|
|
15467
15467
|
{
|
|
15468
15468
|
"path": "tests/hall_catalog_world.mjs",
|
|
15469
15469
|
"mode": "0000644",
|
|
15470
|
-
"sha256": "
|
|
15470
|
+
"sha256": "d209d00843af7bab570d02be627761913e03129c80f29f0c036235633b8085a9"
|
|
15471
15471
|
},
|
|
15472
15472
|
{
|
|
15473
15473
|
"path": "tests/hall_city.mjs",
|
|
@@ -16642,7 +16642,7 @@
|
|
|
16642
16642
|
{
|
|
16643
16643
|
"path": "tests/module_store_listing.mjs",
|
|
16644
16644
|
"mode": "0000644",
|
|
16645
|
-
"sha256": "
|
|
16645
|
+
"sha256": "be639096f82a9750e25b57c7612e94754b6109ef13507144be9ceb384628eb91"
|
|
16646
16646
|
},
|
|
16647
16647
|
{
|
|
16648
16648
|
"path": "tests/module_store_publish.mjs",
|
package/docs/api/openapi.json
CHANGED
|
@@ -23004,7 +23004,7 @@
|
|
|
23004
23004
|
"store"
|
|
23005
23005
|
],
|
|
23006
23006
|
"summary": "GET /store/modules",
|
|
23007
|
-
"description": "The store catalog under the quality floor (task 1003807, ADR 0343 D2/D5): each listed module's newest version, if it passed the Security gate and scores at or above the floor, or under \"new\" if it has no real-world data yet. Below the bar is not listed at all. The floor is fixed in module-listing.js and returned here. rank: any-builder — a read of public store metadata, like /store/modules/latest: no entitlement taken, nothing changed.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
23007
|
+
"description": "The store catalog under the quality floor (task 1003807, ADR 0343 D2/D5): each listed module's newest version, if it passed the Security gate and scores at or above the floor, or under \"new\" if it has no real-world data yet. Below the bar is not listed at all. The floor is fixed in module-listing.js and returned here. `?sort=<part>` orders the listed modules by the part a buyer cares about (task 1003800, sortByPart); the parts it accepts come back as sort_parts. rank: any-builder — a read of public store metadata, like /store/modules/latest: no entitlement taken, nothing changed.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
23008
23008
|
"x-rank": "any-builder",
|
|
23009
23009
|
"x-source": "src/bongos/routes/modules.js",
|
|
23010
23010
|
"responses": {
|
package/docs/copy-inventory.md
CHANGED
|
@@ -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
|
-
**
|
|
7
|
+
**2771 strings** across 3 surfaces · 2350 distinct pieces of copy · 2771 certain, 0 heuristic · 241 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` | 148 |
|
|
13
|
+
| `builders-hall` | `modules/hall-ui/public` | 148 | 1963 |
|
|
14
14
|
| `status-dashboard` | `modules/status-ui/public` | 4 | 100 |
|
|
15
15
|
| `landing` | `modules/public-landing/public` | 9 | 708 |
|
|
16
16
|
|
|
@@ -78,7 +78,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
78
78
|
| `8226c979db4d` | 3x | Not now | modules/hall-ui/public/collab.js:668, modules/hall-ui/public/paintbrush.js:119, modules/hall-ui/public/paintbrush.js:183 |
|
|
79
79
|
| … | | _201 more in the JSON registry_ | |
|
|
80
80
|
|
|
81
|
-
## `builders-hall` —
|
|
81
|
+
## `builders-hall` — 1963 strings
|
|
82
82
|
|
|
83
83
|
| id | conf | text | where |
|
|
84
84
|
|---|---|---|---|
|
|
@@ -129,7 +129,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
129
129
|
| `0150152ea9ec` | certain | A known snag stopped the move to core {…}: | `modules/hall-ui/public/deploy.js:214` |
|
|
130
130
|
| `2e17d7e236d9` | certain | A module built here can be submitted upstream for review, so it can join the shared catalog with your name and | `modules/hall-ui/public/modules.html:74` |
|
|
131
131
|
| `0c611f5a76af` | certain | A move to core {…} failed{…} — this project has moved on since, and is serving {…} now. Whatever stopped that | `modules/hall-ui/public/deploy.js:374` |
|
|
132
|
-
| `
|
|
132
|
+
| `e395cd11edab` | certain | A part is strong at 80 out of 100 or more, and weak below the listing floor. A part with no data yet is not co | `modules/hall-ui/public/modules.html:90` |
|
|
133
133
|
| `a07c7b335a66` | certain | A protected-scope goal requires a Metic+ member. | `modules/hall-ui/public/goals-render.js:1306` |
|
|
134
134
|
| `a07c7b335a66` | certain | A protected-scope goal requires a Metic+ member. | `modules/hall-ui/public/goals-render.js:1328` |
|
|
135
135
|
| `5723baf26b2d` | certain | A quick idea is a thought filed fast. It is not an unfinished Full Idea — only its author can offer it up for | `modules/hall-ui/public/idea.js:475` |
|
|
@@ -176,7 +176,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
176
176
|
| `375b0e67bc30` | certain | Allow new releases and move to {…} | `modules/hall-ui/public/deploy.js:257` |
|
|
177
177
|
| `375b0e67bc30` | certain | Allow new releases and move to {…} | `modules/hall-ui/public/deploy.js:685` |
|
|
178
178
|
| `e99cc5d37404` | certain | Already a member? | `modules/hall-ui/public/hall-render.js:373` |
|
|
179
|
-
| `70ffbebe42fd` | certain | Also available as a Claude page | `modules/hall-ui/public/modules.html:
|
|
179
|
+
| `70ffbebe42fd` | certain | Also available as a Claude page | `modules/hall-ui/public/modules.html:103` |
|
|
180
180
|
| `da5368d10f85` | certain | Amendment {…} passed{…}, and the constitution below is not the one it decided: | `modules/hall-ui/public/government.js:671` |
|
|
181
181
|
| `2ca420e19dbb` | certain | An earlier attempt did not land. | `modules/hall-ui/public/deploy.js:374` |
|
|
182
182
|
| `69f0ca4a4d84` | certain | Analytics scope | `modules/hall-ui/public/hall-render.js:1184` |
|
|
@@ -368,7 +368,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
368
368
|
| `90691bceaf68` | certain | Could not load | `modules/hall-ui/public/project-settings.js:284` |
|
|
369
369
|
| `90691bceaf68` | certain | Could not load | `modules/hall-ui/public/task.js:1022` |
|
|
370
370
|
| `589a4cfc5442` | certain | Could not load idea {…}. | `modules/hall-ui/public/ideas.js:1018` |
|
|
371
|
-
| `192c893fc64e` | certain | Could not load modules. | `modules/hall-ui/public/modules.js:
|
|
371
|
+
| `192c893fc64e` | certain | Could not load modules. | `modules/hall-ui/public/modules.js:489` |
|
|
372
372
|
| `9912c3835136` | certain | Could not load requests — reload to try again. | `modules/hall-ui/public/collab.js:845` |
|
|
373
373
|
| `7775fec0fa85` | certain | Could not load sessions. | `modules/hall-ui/public/sessions.js:504` |
|
|
374
374
|
| `7775fec0fa85` | certain | Could not load sessions. | `modules/hall-ui/public/sessions.js:542` |
|
|
@@ -389,7 +389,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
389
389
|
| `64f9d51f0fa2` | certain | Could not load your settings. | `modules/hall-ui/public/settings.js:1204` |
|
|
390
390
|
| `de244c45d916` | certain | Could not load {…} | `modules/hall-ui/public/task.js:1015` |
|
|
391
391
|
| `2366356926b4` | certain | Could not mark the primer just now. Try again. | `modules/hall-ui/public/primer.js:259` |
|
|
392
|
-
| `753987b5da13` | certain | Could not reach the submit endpoint. | `modules/hall-ui/public/modules.js:
|
|
392
|
+
| `753987b5da13` | certain | Could not reach the submit endpoint. | `modules/hall-ui/public/modules.js:467` |
|
|
393
393
|
| `6f500726f0ac` | certain | Could not read the report: {…} | `modules/hall-ui/public/watch.js:768` |
|
|
394
394
|
| `21c2f33168a3` | certain | Could not read this project's deploy state: {…} | `modules/hall-ui/public/deploy.js:624` |
|
|
395
395
|
| `21a782f1af26` | certain | Could not read what this would bring in: {…}. The check below still applies. | `modules/hall-ui/public/deploy.js:542` |
|
|
@@ -831,7 +831,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
831
831
|
| `90623fec4cfa` | certain | No page is open. | `modules/hall-ui/public/tweak-editor.js:563` |
|
|
832
832
|
| `1a46c31343cb` | certain | No projects are hosted here yet. | `modules/hall-ui/public/fleet-costs.js:68` |
|
|
833
833
|
| `9f73b3f3dad0` | certain | No rank floor — any builder can claim this task | `modules/hall-ui/public/task-lib.js:82` |
|
|
834
|
-
| `4ffd939755e0` | certain | No real-world data yet, so it is listed but not ranked. | `modules/hall-ui/public/modules.js:
|
|
834
|
+
| `4ffd939755e0` | certain | No real-world data yet, so it is listed but not ranked. | `modules/hall-ui/public/modules.js:235` |
|
|
835
835
|
| `722d107d7a90` | certain | No render is attached to this page yet. /tweak attaches them when it applies a page; until then there is nothi | `modules/hall-ui/public/approval-queue.js:135` |
|
|
836
836
|
| `3fd0c6fcb92e` | certain | No request and no invite needed. Sign in with GitHub, and a step-by-step guide takes you the rest of the way. | `modules/hall-ui/public/hall-render.js:283` |
|
|
837
837
|
| `2947c1ddea9a` | certain | No running box to deploy. | `modules/hall-ui/public/deploy.js:708` |
|
|
@@ -902,7 +902,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
902
902
|
| `43626cff7071` | certain | Nothing of that kind in your sky. | `modules/hall-ui/public/sky-panel.js:123` |
|
|
903
903
|
| `b05a5f8cb074` | certain | Nothing personal is sent. | `modules/hall-ui/public/my-agents.js:450` |
|
|
904
904
|
| `ad9a17a8a5eb` | certain | Nothing shipped yet. | `modules/hall-ui/public/profile.js:297` |
|
|
905
|
-
| `f326632f2013` | certain | Nothing to submit — every module here ships with the core. | `modules/hall-ui/public/modules.js:
|
|
905
|
+
| `f326632f2013` | certain | Nothing to submit — every module here ships with the core. | `modules/hall-ui/public/modules.js:428` |
|
|
906
906
|
| `27c0d872db2d` | certain | Nothing you have typed above is lost by opening this. | `modules/hall-ui/public/ideas.html:155` |
|
|
907
907
|
| `26a07b64f49d` | certain | Now running core {…} | `modules/hall-ui/public/deploy.js:400` |
|
|
908
908
|
| `21cb60a8a96a` | certain | Now running core {…} — but GitHub was not updated. | `modules/hall-ui/public/deploy.js:390` |
|
|
@@ -1111,7 +1111,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1111
1111
|
| `48e5f1d19e90` | certain | Runner allowed to work | `modules/hall-ui/public/gate.js:425` |
|
|
1112
1112
|
| `41730c113dd5` | certain | Runners work only the goals above, and only while the switch is on. Each builder picks which of them their own | `modules/hall-ui/public/gate.js:425` |
|
|
1113
1113
|
| `b6596905f845` | certain | Said more than once | `modules/hall-ui/public/copy-desk.html:56` |
|
|
1114
|
-
| `ce046dcbd8e1` | certain | Save as PDF | `modules/hall-ui/public/modules.html:
|
|
1114
|
+
| `ce046dcbd8e1` | certain | Save as PDF | `modules/hall-ui/public/modules.html:104` |
|
|
1115
1115
|
| `819cdcaac499` | certain | Save current filters as | `modules/hall-ui/public/hall-kit.js:411` |
|
|
1116
1116
|
| `0c79b504aebe` | certain | Save key | `modules/hall-ui/public/settings.js:876` |
|
|
1117
1117
|
| `b3d55c8c765a` | certain | Save my choices | `modules/hall-ui/public/settings-specialities.js:163` |
|
|
@@ -1216,6 +1216,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1216
1216
|
| `867df58c9aaa` | certain | Something else | `modules/hall-ui/public/copy-desk.html:95` |
|
|
1217
1217
|
| `e11c18877155` | certain | Something is here. | `modules/hall-ui/public/sky-interaction.js:35` |
|
|
1218
1218
|
| `c377d8ed744b` | certain | Something went wrong. Try again. | `modules/hall-ui/public/profile-connections.js:331` |
|
|
1219
|
+
| `70383b194ec0` | certain | Sort by | `modules/hall-ui/public/modules.html:87` |
|
|
1219
1220
|
| `e0767e80206c` | certain | Sound for {…} | `modules/hall-ui/public/settings.js:309` |
|
|
1220
1221
|
| `7f4f7b25c61f` | certain | Sound preference saved. | `modules/hall-ui/public/settings.js:326` |
|
|
1221
1222
|
| `490f4bbbc75b` | certain | Sounds are switched off for now. Nothing plays when a task ships, when {{currency}} arrive, or when Claude fin | `modules/hall-ui/public/settings.html:396` |
|
|
@@ -1757,7 +1758,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1757
1758
|
| `7b5ad068dc26` | certain | not graded | `modules/hall-ui/public/hall-render.js:349` |
|
|
1758
1759
|
| `01a3d68d80c1` | certain | not graded (grader outage) | `modules/hall-ui/public/builders.js:557` |
|
|
1759
1760
|
| `d43b9bbb8d4d` | certain | not graded (quality lens unavailable) | `modules/hall-ui/public/builders.js:559` |
|
|
1760
|
-
| `380177c47d89` | certain | not measured yet | `modules/hall-ui/public/modules.js:
|
|
1761
|
+
| `380177c47d89` | certain | not measured yet | `modules/hall-ui/public/modules.js:252` |
|
|
1761
1762
|
| `c04e4491af67` | certain | not now | `modules/hall-ui/public/studio.html:110` |
|
|
1762
1763
|
| `3916a3dc8e98` | certain | not started | `modules/hall-ui/public/roadmap.js:120` |
|
|
1763
1764
|
| `3916a3dc8e98` | certain | not started | `modules/hall-ui/public/roadmap.js:341` |
|
|
@@ -1824,7 +1825,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1824
1825
|
| `c8c2551006dc` | certain | skip the tour | `modules/hall-ui/public/goal-map.html:121` |
|
|
1825
1826
|
| `c8c2551006dc` | certain | skip the tour | `modules/hall-ui/public/thinking.html:122` |
|
|
1826
1827
|
| `1e737a57eb20` | certain | start governing | `modules/hall-ui/public/city.html:99` |
|
|
1827
|
-
| `e294c3429c2e` | certain | strong at {…} | `modules/hall-ui/public/modules.js:
|
|
1828
|
+
| `e294c3429c2e` | certain | strong at {…} | `modules/hall-ui/public/modules.js:241` |
|
|
1828
1829
|
| `0215b2cdca8c` | certain | submit for review | `modules/hall-ui/public/modules.js:187` |
|
|
1829
1830
|
| `43e6f1592ae3` | certain | submit it in the editor | `modules/hall-ui/public/studio.js:836` |
|
|
1830
1831
|
| `9ec19e32f86f` | certain | submit page | `modules/hall-ui/public/tweak-editor.html:68` |
|
|
@@ -1895,7 +1896,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1895
1896
|
| `18cf722bdd7e` | certain | waiting · | `modules/hall-ui/public/sky-interaction.js:128` |
|
|
1896
1897
|
| `354861d3c2ce` | certain | waits for a restart | `modules/hall-ui/public/modules.js:139` |
|
|
1897
1898
|
| `35be9e0923da` | certain | walks the whole path in plain language. | `modules/hall-ui/public/hall-render.js:837` |
|
|
1898
|
-
| `20318b6da94e` | certain | weak at {…} | `modules/hall-ui/public/modules.js:
|
|
1899
|
+
| `20318b6da94e` | certain | weak at {…} | `modules/hall-ui/public/modules.js:243` |
|
|
1899
1900
|
| `db42054582ed` | certain | wells · | `modules/hall-ui/public/sky-interaction.js:129` |
|
|
1900
1901
|
| `17705daf7b85` | certain | what counts as passing | `modules/hall-ui/public/government.html:55` |
|
|
1901
1902
|
| `96a8971a5c44` | certain | what got un-stuck | `modules/hall-ui/public/blockers.js:132` |
|
|
@@ -1945,6 +1946,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1945
1946
|
| `80b4aa799a4b` | certain | {…} is writing this page now. You can read it, download it, or take another page from the studio. | `modules/hall-ui/public/tweak-editor.js:319` |
|
|
1946
1947
|
| `830d2be6f859` | certain | {…} line | `modules/hall-ui/public/tweak-editor.js:235` |
|
|
1947
1948
|
| `511a4b6073cf` | certain | {…} more PR{…} still being checked. Refresh shortly. | `modules/hall-ui/public/gate.js:225` |
|
|
1949
|
+
| `92469c4757f1` | certain | {…} not measured yet | `modules/hall-ui/public/modules.js:226` |
|
|
1948
1950
|
| `73c1372b43d9` | certain | {…} of {…} | `modules/hall-ui/public/sky-panel.js:120` |
|
|
1949
1951
|
| `cc5106af4b35` | certain | {…} of {…} criteria satisfied | `modules/hall-ui/public/goals-lib.js:32` |
|
|
1950
1952
|
| `cc5106af4b35` | certain | {…} of {…} criteria satisfied | `modules/hall-ui/public/goals-render.js:701` |
|
|
@@ -1968,7 +1970,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1968
1970
|
| `1a8a6267e490` | certain | {…} unfinished task{…} — say what happens to each | `modules/hall-ui/public/goals-render.js:1032` |
|
|
1969
1971
|
| `2f75f69cdb2f` | certain | {…} {…} changed | `modules/hall-ui/public/profile-crafts.js:218` |
|
|
1970
1972
|
| `8186b562553a` | certain | {…} {…} waiting | `modules/hall-ui/public/work.js:420` |
|
|
1971
|
-
| `a9216af9b05e` | certain | {…} {…} — how-to | `modules/hall-ui/public/modules.js:
|
|
1973
|
+
| `a9216af9b05e` | certain | {…} {…} — how-to | `modules/hall-ui/public/modules.js:544` |
|
|
1972
1974
|
| `0d4016a5531c` | certain | {…} {…}{…} | `modules/hall-ui/public/watch.js:514` |
|
|
1973
1975
|
| `0d4016a5531c` | certain | {…} {…}{…} | `modules/hall-ui/public/work.js:265` |
|
|
1974
1976
|
| `b3757ba3bb46` | certain | {…} · Stored encrypted; never shown again after saving. | `modules/hall-ui/public/settings.js:878` |
|
|
@@ -1976,7 +1978,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1976
1978
|
| `d73256fbc395` | certain | {…} · who votes, and how it {…} | `modules/hall-ui/public/board-room.js:460` |
|
|
1977
1979
|
| `cab2a4296aec` | certain | {…} · {…} · {…}{…} | `modules/hall-ui/public/goal-inbox.js:458` |
|
|
1978
1980
|
| `1ac723849449` | certain | {…} · {…}{…} | `modules/hall-ui/public/sky-interaction.js:33` |
|
|
1979
|
-
| `ddd2bfc1fb60` | certain | {…} — how to use it | `modules/hall-ui/public/modules.js:
|
|
1981
|
+
| `ddd2bfc1fb60` | certain | {…} — how to use it | `modules/hall-ui/public/modules.js:546` |
|
|
1980
1982
|
| `085f93f3f775` | certain | {…} — see all standings | `modules/hall-ui/public/hall-render.js:80` |
|
|
1981
1983
|
| `2a2728a4ec4f` | certain | {…}% of tasks complete | `modules/hall-ui/public/roadmap.js:63` |
|
|
1982
1984
|
| `15e545eb3ce2` | certain | {…}, no such task | `modules/hall-ui/public/task.js:1002` |
|
|
@@ -1991,7 +1993,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1991
1993
|
| `97e27ab169ae` | certain | {…}{…} session{…} across {…} builder{…}. | `modules/hall-ui/public/sessions.js:443` |
|
|
1992
1994
|
| `3d74668f69e5` | certain | {…}{…} · {…} | `modules/hall-ui/public/task.js:639` |
|
|
1993
1995
|
| `7f79bedecf60` | certain | {…}{…} — open it to see what went | `modules/hall-ui/public/goal-inbox.js:374` |
|
|
1994
|
-
| `1026fd5f68b7` | certain | · version | `modules/hall-ui/public/modules.js:
|
|
1996
|
+
| `1026fd5f68b7` | certain | · version | `modules/hall-ui/public/modules.js:549` |
|
|
1995
1997
|
| `4a5ff4f662df` | certain | — ${…} of ${…} this month ({…}%). {…} | `modules/hall-ui/public/hall-render.js:523` |
|
|
1996
1998
|
| `a6ae9cbdee30` | certain | — all builders — | `modules/hall-ui/public/builders.js:711` |
|
|
1997
1999
|
| `729d4ce22b99` | certain | — any — | `modules/hall-ui/public/hall-widgets.js:163` |
|
|
@@ -2026,7 +2028,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
2026
2028
|
| `5f1d914ad45e` | certain | …{…} more — | `modules/hall-ui/public/goal-inbox.js:561` |
|
|
2027
2029
|
| `de8fa5223aee` | certain | ‹ Prev | `modules/hall-ui/public/hall-kit.js:74` |
|
|
2028
2030
|
| `d4b84b7fbdeb` | certain | ← All goals | `modules/hall-ui/public/goals.html:77` |
|
|
2029
|
-
| `9159687c73ff` | certain | ← All modules | `modules/hall-ui/public/modules.html:
|
|
2031
|
+
| `9159687c73ff` | certain | ← All modules | `modules/hall-ui/public/modules.html:101` |
|
|
2030
2032
|
| `d063a7b9c83e` | certain | ← Back | `modules/hall-ui/public/page-notes.js:283` |
|
|
2031
2033
|
| `7926e30955d2` | certain | ← Back to goal | `modules/hall-ui/public/goals.html:189` |
|
|
2032
2034
|
| `7926e30955d2` | certain | ← Back to goal | `modules/hall-ui/public/goals.html:205` |
|
package/docs/copy-registry.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"generator": "scripts/gds/copy-inventory.js",
|
|
4
4
|
"goal": 1000074,
|
|
5
5
|
"counts": {
|
|
6
|
-
"strings":
|
|
7
|
-
"unique_ids":
|
|
8
|
-
"certain":
|
|
6
|
+
"strings": 2771,
|
|
7
|
+
"unique_ids": 2350,
|
|
8
|
+
"certain": 2771,
|
|
9
9
|
"heuristic": 0,
|
|
10
10
|
"duplicate_ids": 241
|
|
11
11
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
15
|
"files": 148,
|
|
16
|
-
"strings":
|
|
16
|
+
"strings": 1963
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -3054,11 +3054,11 @@
|
|
|
3054
3054
|
"confidence": "certain"
|
|
3055
3055
|
},
|
|
3056
3056
|
{
|
|
3057
|
-
"id": "
|
|
3057
|
+
"id": "e395cd11edab",
|
|
3058
3058
|
"surface": "builders-hall",
|
|
3059
|
-
"text": "A part is strong at 80 out of 100 or more, and weak below the listing floor. A part with no data yet is not counted against a module.",
|
|
3059
|
+
"text": "A part is strong at 80 out of 100 or more, and weak below the listing floor. A part with no data yet is not counted against a module. Sort by a part to see the modules strongest at it first; a module with no score for that part goes last, and a new module is never ranked.",
|
|
3060
3060
|
"file": "modules/hall-ui/public/modules.html",
|
|
3061
|
-
"line":
|
|
3061
|
+
"line": 90,
|
|
3062
3062
|
"origin": "html-text",
|
|
3063
3063
|
"confidence": "certain"
|
|
3064
3064
|
},
|
|
@@ -3481,7 +3481,7 @@
|
|
|
3481
3481
|
"surface": "builders-hall",
|
|
3482
3482
|
"text": "Also available as a Claude page",
|
|
3483
3483
|
"file": "modules/hall-ui/public/modules.html",
|
|
3484
|
-
"line":
|
|
3484
|
+
"line": 103,
|
|
3485
3485
|
"origin": "html-text",
|
|
3486
3486
|
"confidence": "certain"
|
|
3487
3487
|
},
|
|
@@ -5209,7 +5209,7 @@
|
|
|
5209
5209
|
"surface": "builders-hall",
|
|
5210
5210
|
"text": "Could not load modules.",
|
|
5211
5211
|
"file": "modules/hall-ui/public/modules.js",
|
|
5212
|
-
"line":
|
|
5212
|
+
"line": 489,
|
|
5213
5213
|
"origin": "js-notice",
|
|
5214
5214
|
"confidence": "certain"
|
|
5215
5215
|
},
|
|
@@ -5398,7 +5398,7 @@
|
|
|
5398
5398
|
"surface": "builders-hall",
|
|
5399
5399
|
"text": "Could not reach the submit endpoint.",
|
|
5400
5400
|
"file": "modules/hall-ui/public/modules.js",
|
|
5401
|
-
"line":
|
|
5401
|
+
"line": 467,
|
|
5402
5402
|
"origin": "js-notice",
|
|
5403
5403
|
"confidence": "certain"
|
|
5404
5404
|
},
|
|
@@ -9376,7 +9376,7 @@
|
|
|
9376
9376
|
"surface": "builders-hall",
|
|
9377
9377
|
"text": "No real-world data yet, so it is listed but not ranked.",
|
|
9378
9378
|
"file": "modules/hall-ui/public/modules.js",
|
|
9379
|
-
"line":
|
|
9379
|
+
"line": 235,
|
|
9380
9380
|
"origin": "js-markup",
|
|
9381
9381
|
"confidence": "certain"
|
|
9382
9382
|
},
|
|
@@ -10015,7 +10015,7 @@
|
|
|
10015
10015
|
"surface": "builders-hall",
|
|
10016
10016
|
"text": "Nothing to submit — every module here ships with the core.",
|
|
10017
10017
|
"file": "modules/hall-ui/public/modules.js",
|
|
10018
|
-
"line":
|
|
10018
|
+
"line": 428,
|
|
10019
10019
|
"origin": "js-text-assign",
|
|
10020
10020
|
"confidence": "certain"
|
|
10021
10021
|
},
|
|
@@ -11896,7 +11896,7 @@
|
|
|
11896
11896
|
"surface": "builders-hall",
|
|
11897
11897
|
"text": "Save as PDF",
|
|
11898
11898
|
"file": "modules/hall-ui/public/modules.html",
|
|
11899
|
-
"line":
|
|
11899
|
+
"line": 104,
|
|
11900
11900
|
"origin": "html-text",
|
|
11901
11901
|
"confidence": "certain"
|
|
11902
11902
|
},
|
|
@@ -12836,6 +12836,15 @@
|
|
|
12836
12836
|
"origin": "js-notice",
|
|
12837
12837
|
"confidence": "certain"
|
|
12838
12838
|
},
|
|
12839
|
+
{
|
|
12840
|
+
"id": "70383b194ec0",
|
|
12841
|
+
"surface": "builders-hall",
|
|
12842
|
+
"text": "Sort by",
|
|
12843
|
+
"file": "modules/hall-ui/public/modules.html",
|
|
12844
|
+
"line": 87,
|
|
12845
|
+
"origin": "html-text",
|
|
12846
|
+
"confidence": "certain"
|
|
12847
|
+
},
|
|
12839
12848
|
{
|
|
12840
12849
|
"id": "e0767e80206c",
|
|
12841
12850
|
"surface": "builders-hall",
|
|
@@ -17710,7 +17719,7 @@
|
|
|
17710
17719
|
"surface": "builders-hall",
|
|
17711
17720
|
"text": "not measured yet",
|
|
17712
17721
|
"file": "modules/hall-ui/public/modules.js",
|
|
17713
|
-
"line":
|
|
17722
|
+
"line": 252,
|
|
17714
17723
|
"origin": "js-markup",
|
|
17715
17724
|
"confidence": "certain"
|
|
17716
17725
|
},
|
|
@@ -18313,7 +18322,7 @@
|
|
|
18313
18322
|
"surface": "builders-hall",
|
|
18314
18323
|
"text": "strong at {…}",
|
|
18315
18324
|
"file": "modules/hall-ui/public/modules.js",
|
|
18316
|
-
"line":
|
|
18325
|
+
"line": 241,
|
|
18317
18326
|
"origin": "js-markup",
|
|
18318
18327
|
"confidence": "certain"
|
|
18319
18328
|
},
|
|
@@ -18952,7 +18961,7 @@
|
|
|
18952
18961
|
"surface": "builders-hall",
|
|
18953
18962
|
"text": "weak at {…}",
|
|
18954
18963
|
"file": "modules/hall-ui/public/modules.js",
|
|
18955
|
-
"line":
|
|
18964
|
+
"line": 243,
|
|
18956
18965
|
"origin": "js-markup",
|
|
18957
18966
|
"confidence": "certain"
|
|
18958
18967
|
},
|
|
@@ -19397,6 +19406,15 @@
|
|
|
19397
19406
|
"origin": "js-text-assign",
|
|
19398
19407
|
"confidence": "certain"
|
|
19399
19408
|
},
|
|
19409
|
+
{
|
|
19410
|
+
"id": "92469c4757f1",
|
|
19411
|
+
"surface": "builders-hall",
|
|
19412
|
+
"text": "{…} not measured yet",
|
|
19413
|
+
"file": "modules/hall-ui/public/modules.js",
|
|
19414
|
+
"line": 226,
|
|
19415
|
+
"origin": "js-markup",
|
|
19416
|
+
"confidence": "certain"
|
|
19417
|
+
},
|
|
19400
19418
|
{
|
|
19401
19419
|
"id": "73c1372b43d9",
|
|
19402
19420
|
"surface": "builders-hall",
|
|
@@ -19609,7 +19627,7 @@
|
|
|
19609
19627
|
"surface": "builders-hall",
|
|
19610
19628
|
"text": "{…} {…} — how-to",
|
|
19611
19629
|
"file": "modules/hall-ui/public/modules.js",
|
|
19612
|
-
"line":
|
|
19630
|
+
"line": 544,
|
|
19613
19631
|
"origin": "js-text-assign",
|
|
19614
19632
|
"confidence": "certain"
|
|
19615
19633
|
},
|
|
@@ -19681,7 +19699,7 @@
|
|
|
19681
19699
|
"surface": "builders-hall",
|
|
19682
19700
|
"text": "{…} — how to use it",
|
|
19683
19701
|
"file": "modules/hall-ui/public/modules.js",
|
|
19684
|
-
"line":
|
|
19702
|
+
"line": 546,
|
|
19685
19703
|
"origin": "js-text-assign",
|
|
19686
19704
|
"confidence": "certain"
|
|
19687
19705
|
},
|
|
@@ -19816,7 +19834,7 @@
|
|
|
19816
19834
|
"surface": "builders-hall",
|
|
19817
19835
|
"text": "· version",
|
|
19818
19836
|
"file": "modules/hall-ui/public/modules.js",
|
|
19819
|
-
"line":
|
|
19837
|
+
"line": 549,
|
|
19820
19838
|
"origin": "js-markup",
|
|
19821
19839
|
"confidence": "certain"
|
|
19822
19840
|
},
|
|
@@ -20131,7 +20149,7 @@
|
|
|
20131
20149
|
"surface": "builders-hall",
|
|
20132
20150
|
"text": "← All modules",
|
|
20133
20151
|
"file": "modules/hall-ui/public/modules.html",
|
|
20134
|
-
"line":
|
|
20152
|
+
"line": 101,
|
|
20135
20153
|
"origin": "html-text",
|
|
20136
20154
|
"confidence": "certain"
|
|
20137
20155
|
},
|
|
@@ -3143,5 +3143,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3143
3143
|
landed since 1.21.151 with no explicit bump. run 39671625798. (task 1002620)
|
|
3144
3144
|
1.21.153 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3145
3145
|
landed since 1.21.152 with no explicit bump. run 39675693737. (task 1002620)
|
|
3146
|
+
1.21.154 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3147
|
+
landed since 1.21.153 with no explicit bump. run 39800316919. (task 1002620)
|
|
3146
3148
|
---------------------------------------------------------------------------
|
|
3147
3149
|
```
|
package/docs/modules-contract.md
CHANGED
|
@@ -393,6 +393,7 @@ Run `bongos upgrade` to confirm coreVersion compatibility, then `node scripts/gd
|
|
|
393
393
|
- **A how-to is required** ([ADR 0347](adr/0347-every-store-module-ships-a-how-to.md)). `modules/<key>/HOWTO.md` is what a buyer reads — the store shows it as the module's page — so it is written for the person who installs the module, not for an AI working inside it (that is `CLAUDE.md`). It is plain Markdown: any AI or person can write it. Publish refuses the version unless it has these five level-2 headings, in any order and any case, each with some text under it: `## What it does`, `## Install and enable`, `## How to use it` (with one worked example), `## Configuration` ("None." is fine) and `## Limits and known issues` ("None known." is fine). HTML comments don't count as text, so the scaffold's prompts must be replaced. The refusal names each failing section. The check is one function, `checkHowto` in `scripts/gds/module-artifact.js`; the CLI runs it before uploading and the store runs it again on upload, so a hand-built upload can't skip it. `bongos module check` reports it as advice only — an upstream submit is not gated by it. Install and update don't re-check it, so a version published before the gate still installs. The file travels in the tarball, so its hash pins it to the version. An optional `howto.artifactUrl` in `module.json` (an `https://` link on `claude.ai`) is shown as an extra, never graded.
|
|
394
394
|
- **Getting listed** (task 1003807, [ADR 0343](adr/0343-a-module-score-is-a-security-gate-then-an-average-of-visible-parts.md)). Publishing a version does not by itself put it in the store's catalog, `GET /store/modules`. The catalog shows each module's newest version, judged by its current score. It must pass the Security gate first; a version that failed it, or has not been checked yet, is not listed. A new version with no install or reliability data yet is listed with a **New** label and is not ranked; the floor does not apply to it until it has a real score. Every other version is listed only if its overall score is **at least 50 out of 100**. Below that it is not listed at all. The floor is fixed in Bongos itself (`QUALITY_FLOOR` in `src/bongos/module-listing.js`) and returned in the catalog response; no author or instance setting moves it, and changing it is an owner-approved, reviewed task.
|
|
395
395
|
- **What a module is strong and weak at** (task 1003799, ADR 0343 D4). Each catalog entry also carries `parts` (the current result of Tests, Clean installs, Reliability, Tester feedback and How-to) and a reading of them: `strengths` (a part scored **80 or more**), `weaknesses` (a part scored **below the floor**) and `unmeasured` (a part with no score yet, which is never counted as weak). Each strength and weakness has a one-sentence `detail` saying what the number rests on. Security is not among them: every listed version has passed it. The reading is `readParts` in `src/bongos/module-listing.js`; the hall's Modules tab shows it in its "In the store" group, so two modules with the same overall score can be told apart.
|
|
396
|
+
- **Sorting by a part** (task 1003800). `GET /store/modules?sort=<part>` orders the listed modules by one part a buyer cares about: `tests`, `install`, `reliability`, `tester_feedback` or `docs` (the response lists them as `sort_parts`, and says which one it applied as `sort`). Modules with a score for that part come first, highest first; a tie goes to the higher overall score, then the module key. A module with no score for that part goes last, so missing data never wins or loses a place on its own. New modules are not sorted: they are never ranked. Any other `sort` value is refused with a 400. Without `sort`, both lists are in key order. The rule is `sortByPart` in `src/bongos/module-listing.js`.
|
|
396
397
|
|
|
397
398
|
### 7. Install from the store
|
|
398
399
|
|
package/docs/page-readings.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"adr": "0341 D3",
|
|
6
6
|
"reread": "node scripts/gds/page-reader.js --stale",
|
|
7
7
|
"how": ["Generated by node scripts/gds/page-reader.js (BV2.TW04, task 1004315; ADR 0341 D3). Do not hand-edit.","One entry per page of docs/page-inventory.json. Lines are in page order: the default state first, and a line only another state shows sits where that state shows it, with its state named.","placement: placed = a docs/copy-registry.json row renders it (file, line, string_id); shared = shell text every page of the surface carries (top bar, sidebar, footer), with its row when the registry has one; unplaced = the registry cannot place it (a rewrite becomes an engineer task, ADR 0341 D4).","text keeps {…} where the registry row has a hole, or where a value the page read from its API was filled in. A string that is only such data is counted under omitted.data, not listed.","reading_hash is over the non-shared lines (key, section, text); files_hash is over the page's inventory files. --check compares files_hash only and never opens a browser.","Rendered through the hall-preview harness (hall) and the ui-design kit stub (landing, status), at 1280x800 with the clock pinned, so a reading is the fixture world's page."],
|
|
8
|
-
"counts": {"pages":46,"lines":
|
|
8
|
+
"counts": {"pages":46,"lines":5563,"placed":1707,"shared":398,"unplaced":3458,"omitted_data":3605},
|
|
9
9
|
"pages": [
|
|
10
10
|
{
|
|
11
11
|
"id": "builders:atlas",
|
|
@@ -1885,10 +1885,10 @@
|
|
|
1885
1885
|
"surface": "builders",
|
|
1886
1886
|
"title": "Modules",
|
|
1887
1887
|
"states_read": ["browse","search-empty","lens","details","out"],
|
|
1888
|
-
"files_hash": "
|
|
1889
|
-
"reading_hash": "
|
|
1890
|
-
"counts": {"lines":
|
|
1891
|
-
"omitted": {"data":
|
|
1888
|
+
"files_hash": "78b02310ae1e8e04",
|
|
1889
|
+
"reading_hash": "a3861c8a1816328f",
|
|
1890
|
+
"counts": {"lines":57,"placed":25,"shared":8,"unplaced":24},
|
|
1891
|
+
"omitted": {"data":105},
|
|
1892
1892
|
"notes": ["details: an action failed (page.click: Timeout 6000ms exceeded.); read as it stood"],
|
|
1893
1893
|
"lines": [
|
|
1894
1894
|
{"key":"L0001","section":"skip link","text":"Skip to content","placement":"shared","file":"modules/hall-ui/public/shell.js","line":872,"string_id":"87cee78ca134"},
|
|
@@ -1931,21 +1931,23 @@
|
|
|
1931
1931
|
{"key":"L0038","section":"Module catalog","text":"submit for review","placement":"placed","file":"modules/hall-ui/public/modules.js","line":187,"string_id":"0215b2cdca8c"},
|
|
1932
1932
|
{"key":"L0039","section":"Module catalog","text":"A module built here can be submitted upstream for review, so it can join the shared catalog with your name and this project credited.","placement":"placed","file":"modules/hall-ui/public/modules.html","line":74,"string_id":"2e17d7e236d9"},
|
|
1933
1933
|
{"key":"L0040","section":"Module catalog","text":"In the store","placement":"placed","file":"modules/hall-ui/public/modules.html","line":83,"string_id":"ecfebcd52894"},
|
|
1934
|
-
{"key":"L0041","section":"Module catalog","text":"
|
|
1935
|
-
{"key":"L0042","section":"Module catalog","text":"
|
|
1936
|
-
{"key":"L0043","section":"Module catalog","text":"
|
|
1937
|
-
{"key":"L0044","section":"Module catalog","text":"
|
|
1938
|
-
{"key":"L0045","section":"Module catalog","text":"
|
|
1939
|
-
{"key":"L0046","section":"
|
|
1940
|
-
{"key":"L0047","section":"Module catalog","text":"
|
|
1941
|
-
{"key":"L0048","section":"
|
|
1942
|
-
{"key":"L0049","section":"
|
|
1943
|
-
{"key":"L0050","section":"What are you doing now?","text":"
|
|
1944
|
-
{"key":"L0051","section":"What are you doing now?","text":"
|
|
1945
|
-
{"key":"L0052","section":"What are you doing now?","text":"
|
|
1946
|
-
{"key":"L0053","section":"What are you doing now?","text":"
|
|
1947
|
-
{"key":"L0054","section":"What are you doing now?","text":"
|
|
1948
|
-
{"key":"L0055","section":"What are you doing now?","text":"
|
|
1934
|
+
{"key":"L0041","section":"Module catalog","text":"Sort by","placement":"placed","file":"modules/hall-ui/public/modules.html","line":87,"string_id":"70383b194ec0"},
|
|
1935
|
+
{"key":"L0042","section":"Module catalog","text":"Name","placement":"unplaced"},
|
|
1936
|
+
{"key":"L0043","section":"Module catalog","text":"No store module matches that {…}.","placement":"unplaced","state":"search-empty"},
|
|
1937
|
+
{"key":"L0044","section":"Module catalog","text":"scores","placement":"unplaced","occurrences":2},
|
|
1938
|
+
{"key":"L0045","section":"Module catalog","text":"strong at {…}","placement":"placed","file":"modules/hall-ui/public/modules.js","line":241,"string_id":"e294c3429c2e","occurrences":3},
|
|
1939
|
+
{"key":"L0046","section":"Module catalog","text":"weak at {…}","placement":"placed","file":"modules/hall-ui/public/modules.js","line":243,"string_id":"20318b6da94e"},
|
|
1940
|
+
{"key":"L0047","section":"Module catalog","text":"A part is strong at 80 out of 100 or more, and weak below the listing floor. A part with no data yet is not counted against a module. Sort by a part to see the modules strongest at it first; a module with no score for that part goes last, and a new module is never ranked.","placement":"placed","file":"modules/hall-ui/public/modules.html","line":90,"string_id":"e395cd11edab"},
|
|
1941
|
+
{"key":"L0048","section":"footer","text":"Sign out","placement":"shared","file":"modules/hall-ui/public/shell.js","line":520,"string_id":"1883ad7f07fc"},
|
|
1942
|
+
{"key":"L0049","section":"Module catalog","text":"Builders hall · this session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":810,"string_id":"fd1903586e8e"},
|
|
1943
|
+
{"key":"L0050","section":"What are you doing now?","text":"What are you doing now?","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":811,"string_id":"60853319f290"},
|
|
1944
|
+
{"key":"L0051","section":"What are you doing now?","text":"Pick one. You can switch any time from the top bar.","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":812,"string_id":"4f87d5b17b54"},
|
|
1945
|
+
{"key":"L0052","section":"What are you doing now?","text":"Building","placement":"unplaced"},
|
|
1946
|
+
{"key":"L0053","section":"What are you doing now?","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","occurrences":3},
|
|
1947
|
+
{"key":"L0054","section":"What are you doing now?","text":"Artistry","placement":"unplaced"},
|
|
1948
|
+
{"key":"L0055","section":"What are you doing now?","text":"Ideating","placement":"unplaced"},
|
|
1949
|
+
{"key":"L0056","section":"What are you doing now?","text":"Turn thoughts into {…}.","placement":"unplaced"},
|
|
1950
|
+
{"key":"L0057","section":"What are you doing now?","text":"Ask me again next session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":815,"string_id":"fa86bf403775"}
|
|
1949
1951
|
]
|
|
1950
1952
|
},
|
|
1951
1953
|
{
|
|
@@ -82,9 +82,12 @@
|
|
|
82
82
|
<div class="ov-group__head">
|
|
83
83
|
<span class="ov-group__name">In the store</span>
|
|
84
84
|
<span class="ov-group__n" id="store-n"></span>
|
|
85
|
+
<!-- task 1003800: the buyer picks the part that matters to them, and
|
|
86
|
+
the server orders by it (sortByPart). Filled from sort_parts. -->
|
|
87
|
+
<label class="ov-group__note">Sort by <select id="store-sort"><option value="">Name</option></select></label>
|
|
85
88
|
</div>
|
|
86
89
|
<ul class="ov-rows" id="store-grid"></ul>
|
|
87
|
-
<p class="ov-foot" id="store-lede">A part is strong at 80 out of 100 or more, and weak below the listing floor. A part with no data yet is not counted against a module.</p>
|
|
90
|
+
<p class="ov-foot" id="store-lede">A part is strong at 80 out of 100 or more, and weak below the listing floor. A part with no data yet is not counted against a module. Sort by a part to see the modules strongest at it first; a module with no score for that part goes last, and a new module is never ranked.</p>
|
|
88
91
|
</div>
|
|
89
92
|
</section>
|
|
90
93
|
|
|
@@ -211,6 +211,20 @@
|
|
|
211
211
|
// short labels; the sentence behind each is one click away in the details, the
|
|
212
212
|
// splitCriterion idea: scannable in a list, thorough on demand.
|
|
213
213
|
let storeModules = [];
|
|
214
|
+
// The part the buyer sorts by (task 1003800): '' is name order. The server owns
|
|
215
|
+
// the order and the list of parts; this page only asks for one and shows it.
|
|
216
|
+
let storeSort = '';
|
|
217
|
+
let storeSortTitle = '';
|
|
218
|
+
let storeSortWired = false;
|
|
219
|
+
|
|
220
|
+
// The sorted-by part's own score, so the order explains itself on every row.
|
|
221
|
+
function sortedPartFact(m) {
|
|
222
|
+
if (!storeSort || m.is_new) return '';
|
|
223
|
+
const p = m.parts && m.parts[storeSort];
|
|
224
|
+
return p && p.score != null
|
|
225
|
+
? `<span class="ov-fact">${escapeHtml(storeSortTitle)} <b>${escapeHtml(String(p.score))}</b></span>`
|
|
226
|
+
: `<span class="ov-fact">${escapeHtml(storeSortTitle)} not measured yet</span>`;
|
|
227
|
+
}
|
|
214
228
|
|
|
215
229
|
function partsList(items) {
|
|
216
230
|
return items.map((p) => `${escapeHtml(p.title)} <b>${escapeHtml(String(p.score))}</b>`).join(', ');
|
|
@@ -220,6 +234,8 @@
|
|
|
220
234
|
const facts = [m.is_new
|
|
221
235
|
? '<span class="fact-pill fact-pill--info" title="No real-world data yet, so it is listed but not ranked.">new</span>'
|
|
222
236
|
: `<span class="ov-fact">scores <b>${escapeHtml(String(m.overall))}</b></span>`];
|
|
237
|
+
const sorted = sortedPartFact(m);
|
|
238
|
+
if (sorted) facts.push(sorted);
|
|
223
239
|
const strong = Array.isArray(m.strengths) ? m.strengths : [];
|
|
224
240
|
const weak = Array.isArray(m.weaknesses) ? m.weaknesses : [];
|
|
225
241
|
if (strong.length) facts.push(`<span class="ov-fact">strong at ${partsList(strong)}</span>`);
|
|
@@ -275,11 +291,26 @@
|
|
|
275
291
|
|
|
276
292
|
// Best-effort and last: the store is a second read, and the modules on disk ARE
|
|
277
293
|
// this page, so a store that cannot be read costs this group and nothing else.
|
|
294
|
+
// Filled and wired once, on the first store read; a re-read only re-renders.
|
|
295
|
+
function fillStoreSort(parts) {
|
|
296
|
+
const select = $('#store-sort');
|
|
297
|
+
if (storeSortWired || !select || !Array.isArray(parts)) return;
|
|
298
|
+
storeSortWired = true;
|
|
299
|
+
for (const p of parts) select.add(new Option(p.title, p.part));
|
|
300
|
+
select.addEventListener('change', () => {
|
|
301
|
+
storeSort = select.value;
|
|
302
|
+
storeSortTitle = storeSort ? select.options[select.selectedIndex].text : '';
|
|
303
|
+
loadStore();
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
278
307
|
async function loadStore() {
|
|
279
308
|
try {
|
|
280
|
-
const
|
|
309
|
+
const q = storeSort ? `?sort=${encodeURIComponent(storeSort)}` : '';
|
|
310
|
+
const res = await api.request('GET', `${API}/store/modules${q}`);
|
|
281
311
|
if (!res.ok) return;
|
|
282
312
|
const body = res.data || {};
|
|
313
|
+
fillStoreSort(body.sort_parts);
|
|
283
314
|
storeModules = [...(body.listed || []), ...(body.new || [])];
|
|
284
315
|
renderStore();
|
|
285
316
|
} catch (err) {
|
|
@@ -12,3 +12,5 @@ Also gone, and the one rule in that list: `.ab-caption` takes the lede's `--meas
|
|
|
12
12
|
**A store version's how-to is a view of the Modules page, not a new page (task 1004366).** `/builders/modules?howto=<key>&version=<X.Y.Z>` hides the catalog and shows that version's `HOWTO.md` in the reading room's article card: the page adds `body.room` so `room.css`'s sizes apply, and the markdown goes through `md-reader.js` (author-written, so every byte is escaped; only absolute http(s), mailto and #anchor links become links). A query, not a `#/` route — the hash namespace is frozen to the idea and blocker overlays. "Save as PDF" is `window.print()` with an `@media print` block in `modules.css` that keeps only the document and prints in the system ink (`CanvasText`), because a literal colour is the token tier's. Harness fixture: `store__modules__howto.json`.
|
|
13
13
|
|
|
14
14
|
**The store's modules are a third group on the same panel, read as strengths and weaknesses (task 1003799, ADR 0343 D4).** `#store-group` ("In the store") lists what `GET /store/modules` lists, hidden until that read answers with something; it loads after the modules on disk and best-effort, so a store that cannot be read costs this group and nothing else. A row is the same `.ov-row`: the overall score as a plain fact (or the `new` info pill), `strong at Tests 96, How-to 88` as a plain fact, and `weak at …` as the warn pill, because a weakness is the exception a buyer must not miss. The sentence behind each part ("Tests scored 96 out of 100, from 40 test cases.") and the parts with no data yet sit in the row's `<details class="mod-more">`, the `splitCriterion` idea: short label in the list, the detail one click away. The reading is the server's (`readParts` in `src/bongos/module-listing.js`: strong at 80 or more, weak below the listing floor, unmeasured never weak); the page re-derives nothing. Harness fixture: `store__modules.json`.
|
|
15
|
+
|
|
16
|
+
**The buyer sorts the store by the part that matters to them (task 1003800, owner decision 2026-10-22).** A "Sort by" menu sits in the store group's head (`#store-sort`, `.ov-group__note`): Name, then the parts the server lists in `sort_parts`. Picking one re-reads `GET /store/modules?sort=<part>`, and the server orders the listed modules (`sortByPart` in `src/bongos/module-listing.js`: highest for that part first, a tie to the overall score then the key, a module with no score for the part last). The page never sorts the rows itself, and New modules keep name order because they are never ranked. While a part is picked, each listed row carries that part's score as a plain fact (`Reliability 91`, or `Reliability not measured yet`), so the order explains itself. The owner chose the buyer over a per-category "key part" because nothing records what a category is for, and a hidden weighting would break the score an author can check (ADR 0343 D3).
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.154",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.154",
|
|
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.21.
|
|
3
|
+
"version": "1.21.154",
|
|
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/release-notes.json
CHANGED
|
@@ -9940,5 +9940,11 @@
|
|
|
9940
9940
|
"id": "1004766",
|
|
9941
9941
|
"text": "When a project's server can't talk to GitHub, the land-status check now says which step failed and what GitHub answered, instead of a bare error."
|
|
9942
9942
|
}
|
|
9943
|
+
],
|
|
9944
|
+
"1.21.154": [
|
|
9945
|
+
{
|
|
9946
|
+
"id": "1003800",
|
|
9947
|
+
"text": "In the module store, a buyer can now sort by the score that matters to them, like reliability or tests, so the module strongest at that comes first."
|
|
9948
|
+
}
|
|
9943
9949
|
]
|
|
9944
9950
|
}
|
|
@@ -92,6 +92,40 @@ function readParts(signals = [], { floor = QUALITY_FLOOR } = {}) {
|
|
|
92
92
|
return { parts, strengths, weaknesses, unmeasured };
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Sort by the part a buyer cares about (task 1003800, owner decision 2026-10-22)
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
//
|
|
99
|
+
// A module strong at what a buyer needs should come first for THAT buyer. Nothing
|
|
100
|
+
// records what a category is "for", and a hidden per-category weighting would break
|
|
101
|
+
// the score an author can check (ADR 0343 D3), so the owner chose: the buyer picks
|
|
102
|
+
// the part. The rule, in a sentence an author can check:
|
|
103
|
+
//
|
|
104
|
+
// modules with a score for the part come first, highest first; a tie goes to the
|
|
105
|
+
// higher overall score, then the key. A module with no score for the part goes
|
|
106
|
+
// last, in key order: a missing part is never a zero, and never a first place.
|
|
107
|
+
//
|
|
108
|
+
// Only `listed` is sorted. A "New" version is never ranked (owner decision,
|
|
109
|
+
// 2026-10-01), so `new` keeps key order whatever the buyer picks. Today this runs
|
|
110
|
+
// over the whole store; once approved sub-categories exist (task 1004355) the same
|
|
111
|
+
// rule orders each one (ADR 0342 D2).
|
|
112
|
+
const SORT_PARTS = PARTS.map(({ part, title }) => ({ part, title }));
|
|
113
|
+
|
|
114
|
+
// `entries` carry `parts` as readParts builds them. Returns a new array. Pure.
|
|
115
|
+
function sortByPart(entries, part) {
|
|
116
|
+
const scoreOf = (e) => (e.parts && e.parts[part] ? e.parts[part].score : null);
|
|
117
|
+
return [...entries].sort((a, b) => {
|
|
118
|
+
const sa = scoreOf(a);
|
|
119
|
+
const sb = scoreOf(b);
|
|
120
|
+
if (sa == null || sb == null) {
|
|
121
|
+
if (sa != null) return -1;
|
|
122
|
+
if (sb != null) return 1;
|
|
123
|
+
return a.key.localeCompare(b.key);
|
|
124
|
+
}
|
|
125
|
+
return (sb - sa) || ((b.overall ?? -1) - (a.overall ?? -1)) || a.key.localeCompare(b.key);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
95
129
|
// The newest version row per module key, in semver order (a version that does not
|
|
96
130
|
// parse is skipped, as latestVersions does in module-store.js).
|
|
97
131
|
function newestPerModule(rows) {
|
|
@@ -106,12 +140,13 @@ function newestPerModule(rows) {
|
|
|
106
140
|
return [...out.values()];
|
|
107
141
|
}
|
|
108
142
|
|
|
109
|
-
// The catalog: { floor, listed: [...], new: [...] }, each entry
|
|
143
|
+
// The catalog: { floor, sort, sort_parts, listed: [...], new: [...] }, each entry
|
|
110
144
|
// { key, title, version, published_at, overall, is_new, parts, strengths, weaknesses,
|
|
111
145
|
// unmeasured } (the last four from readParts). Excluded versions are left
|
|
112
146
|
// out entirely: a module below the bar is not listed at all. Both lists are in key
|
|
113
|
-
// order
|
|
114
|
-
|
|
147
|
+
// order unless `sort` names a part, which orders `listed` by sortByPart; ranking
|
|
148
|
+
// is a separate task (1003808) and does not happen here.
|
|
149
|
+
async function listCatalog({ db, floor = QUALITY_FLOOR, sort = null } = {}) {
|
|
115
150
|
const pool = db || require('./pool').pool;
|
|
116
151
|
const { rows: versions } = await pool.query(
|
|
117
152
|
`SELECT m.module_key, m.title, v.id AS version_id, v.version, v.published_at
|
|
@@ -142,7 +177,7 @@ async function listCatalog({ db, floor = QUALITY_FLOOR } = {}) {
|
|
|
142
177
|
signals.get(k).push(s);
|
|
143
178
|
}
|
|
144
179
|
}
|
|
145
|
-
const out = { floor, listed: [], new: [] };
|
|
180
|
+
const out = { floor, sort: sort || null, sort_parts: SORT_PARTS, listed: [], new: [] };
|
|
146
181
|
for (const r of newest.sort((a, b) => a.module_key.localeCompare(b.module_key))) {
|
|
147
182
|
const score = scores.get(String(r.version_id)) || null;
|
|
148
183
|
const { placement } = placeVersion(score, floor);
|
|
@@ -153,7 +188,8 @@ async function listCatalog({ db, floor = QUALITY_FLOOR } = {}) {
|
|
|
153
188
|
...readParts(signals.get(String(r.version_id)) || [], { floor }),
|
|
154
189
|
});
|
|
155
190
|
}
|
|
191
|
+
if (sort) out.listed = sortByPart(out.listed, sort);
|
|
156
192
|
return out;
|
|
157
193
|
}
|
|
158
194
|
|
|
159
|
-
module.exports = { QUALITY_FLOOR, STRONG_AT, placeVersion, readParts, listCatalog };
|
|
195
|
+
module.exports = { QUALITY_FLOOR, STRONG_AT, SORT_PARTS, placeVersion, readParts, sortByPart, listCatalog };
|
|
@@ -110,7 +110,7 @@ const log = require('../logger').logger.child({ src: 'modules' });
|
|
|
110
110
|
// property access on a CommonJS namespace, so reaching these through one made them read
|
|
111
111
|
// as unused exports and tripped the dead-code ratchet that held this branch out of main.
|
|
112
112
|
const { listOverrides, setOverride } = require('../module-overrides');
|
|
113
|
-
const { listCatalog } = require('../module-listing');
|
|
113
|
+
const { listCatalog, SORT_PARTS } = require('../module-listing');
|
|
114
114
|
|
|
115
115
|
// The sentence for a toggle that waits on a restart (task 1004463). 'routes' are the
|
|
116
116
|
// module's pages, 'pollers' its background jobs. PURE.
|
|
@@ -733,12 +733,20 @@ module.exports = function buildModulesRouter() {
|
|
|
733
733
|
// listed module's newest version, if it passed the Security gate and scores at or
|
|
734
734
|
// above the floor, or under "new" if it has no real-world data yet. Below the bar
|
|
735
735
|
// is not listed at all. The floor is fixed in module-listing.js and returned here.
|
|
736
|
+
// `?sort=<part>` orders the listed modules by the part a buyer cares about
|
|
737
|
+
// (task 1003800, sortByPart); the parts it accepts come back as sort_parts.
|
|
736
738
|
//
|
|
737
739
|
// rank: any-builder — a read of public store metadata, like /store/modules/latest:
|
|
738
740
|
// no entitlement taken, nothing changed.
|
|
739
741
|
router.get('/store/modules', auth.requireBuilder,
|
|
740
|
-
asyncHandler('GET /store/modules', async (
|
|
741
|
-
const
|
|
742
|
+
asyncHandler('GET /store/modules', async (req, res) => {
|
|
743
|
+
const raw = req.query && req.query.sort;
|
|
744
|
+
const sort = typeof raw === 'string' && raw ? raw : null;
|
|
745
|
+
if (sort && !SORT_PARTS.some((p) => p.part === sort)) {
|
|
746
|
+
const names = SORT_PARTS.map((p) => `${p.title} (${p.part})`).join(', ');
|
|
747
|
+
return res.fail('bad_request', { status: 400, message: `The store can be sorted by one part of the score: ${names}.` });
|
|
748
|
+
}
|
|
749
|
+
const listing = await listCatalog({ sort });
|
|
742
750
|
res.set('Cache-Control', 'no-store');
|
|
743
751
|
res.json(listing);
|
|
744
752
|
}));
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.154'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -94,7 +94,7 @@ test('modules: the store group reads strengths and weaknesses off the server row
|
|
|
94
94
|
const html = read('modules.html');
|
|
95
95
|
assert.match(html, /<div class="ov-group" id="store-group" hidden>/, 'the store group is hidden until the store answers');
|
|
96
96
|
assert.match(html, /<ul class="ov-rows" id="store-grid"><\/ul>/);
|
|
97
|
-
assert.match(js, /api\.request\('GET', `\$\{API\}\/store\/modules`\)/, 'it reads GET /store/modules');
|
|
97
|
+
assert.match(js, /api\.request\('GET', `\$\{API\}\/store\/modules\$\{q\}`\)/, 'it reads GET /store/modules');
|
|
98
98
|
assert.match(js, /<span class="ov-fact">strong at \$\{partsList\(strong\)\}<\/span>/, 'a strength is a plain fact');
|
|
99
99
|
assert.match(js, /<span class="fact-pill fact-pill--warn">weak at \$\{partsList\(weak\)\}<\/span>/, 'a weakness is the warn pill');
|
|
100
100
|
assert.match(js, /not measured yet/, 'a part with no data is named, never counted as weak');
|
|
@@ -106,6 +106,18 @@ test('modules: the store group reads strengths and weaknesses off the server row
|
|
|
106
106
|
assert.notDeepEqual(two[0].strengths.map((s) => s.part), two[1].strengths.map((s) => s.part), '…that are strong at different things');
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
+
test('modules: the buyer sorts the store by the part that matters to them (task 1003800)', () => {
|
|
110
|
+
const js = read('modules.js');
|
|
111
|
+
const html = read('modules.html');
|
|
112
|
+
assert.match(html, /<select id="store-sort"><option value="">Name<\/option><\/select>/, 'the menu starts at name order');
|
|
113
|
+
assert.match(js, /\?sort=\$\{encodeURIComponent\(storeSort\)\}/, 'the server orders the list; the page asks for a part');
|
|
114
|
+
assert.match(js, /fillStoreSort\(body\.sort_parts\)/, 'the parts on the menu are the server\'s list');
|
|
115
|
+
assert.doesNotMatch(decomment(js), /\.sort\(\(a, b\)/, 'the page does not re-sort the store rows itself');
|
|
116
|
+
assert.match(js, /not measured yet<\/span>/, 'a row with no score for the sorted part says so, not a zero');
|
|
117
|
+
const fixture = JSON.parse(fs.readFileSync(path.join(ROOT, 'scripts', 'hall-preview', 'fixtures', 'store__modules.json'), 'utf8'));
|
|
118
|
+
assert.deepEqual(fixture.sort_parts.map((p) => p.part), ['tests', 'install', 'reliability', 'tester_feedback', 'docs'], 'the harness carries the menu');
|
|
119
|
+
});
|
|
120
|
+
|
|
109
121
|
test('modules: the kit filter bar carries the search and a state lens, and the empty state is the kit\'s', () => {
|
|
110
122
|
const js = read('modules.js');
|
|
111
123
|
assert.match(js, /kit\.makeFilterBar\(\{/, 'the bar is the kit\'s');
|
|
@@ -53,7 +53,7 @@ const poolPath = require.resolve('../src/bongos/pool.js');
|
|
|
53
53
|
require.cache[poolPath] = { id: poolPath, filename: poolPath, loaded: true, exports: { pool: { connect: async () => ({ ...pool, release() {} }), query: pool.query } } };
|
|
54
54
|
|
|
55
55
|
const buildModulesRouter = require('../src/bongos/routes/modules.js');
|
|
56
|
-
const { QUALITY_FLOOR, placeVersion } = require('../src/bongos/module-listing.js');
|
|
56
|
+
const { QUALITY_FLOOR, SORT_PARTS, placeVersion, sortByPart } = require('../src/bongos/module-listing.js');
|
|
57
57
|
|
|
58
58
|
function handlerFor(router, method, p) {
|
|
59
59
|
const layer = router.stack.find((l) => l.route && l.route.path === p && l.route.methods[method]);
|
|
@@ -163,7 +163,83 @@ test('GET /store/modules: an empty store answers empty lists and the floor', asy
|
|
|
163
163
|
state.modules = {}; state.versions = []; state.scores = [];
|
|
164
164
|
const { handle } = handlerFor(buildModulesRouter(), 'get', '/store/modules');
|
|
165
165
|
const res = await invoke(handle, {});
|
|
166
|
-
assert.deepEqual(res.body, { floor: 50, listed: [], new: [] });
|
|
166
|
+
assert.deepEqual(res.body, { floor: 50, sort: null, sort_parts: SORT_PARTS, listed: [], new: [] });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// sort by the part a buyer cares about (task 1003800, owner decision 2026-10-22)
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
const entry = (key, overall, partScores) => ({
|
|
174
|
+
key, overall,
|
|
175
|
+
parts: Object.fromEntries(Object.entries(partScores).map(([p, s]) => [p, { outcome: s == null ? 'no_data' : 'scored', score: s, sample_size: null }])),
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('sortByPart: highest first for the chosen part; a tie goes to the overall score, then the key', () => {
|
|
179
|
+
const out = sortByPart([
|
|
180
|
+
entry('b-mod', 70, { reliability: 90 }),
|
|
181
|
+
entry('a-mod', 70, { reliability: 90 }),
|
|
182
|
+
entry('steady', 60, { reliability: 95 }),
|
|
183
|
+
entry('higher', 80, { reliability: 90 }),
|
|
184
|
+
], 'reliability');
|
|
185
|
+
assert.deepEqual(out.map((e) => e.key), ['steady', 'higher', 'a-mod', 'b-mod']);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('sortByPart: a module with no score for the part goes last, never first, and the input is untouched', () => {
|
|
189
|
+
const input = [entry('z-unmeasured', 99, { reliability: null }), entry('low', 55, { reliability: 10 }), entry('a-missing', 99, {})];
|
|
190
|
+
const out = sortByPart(input, 'reliability');
|
|
191
|
+
assert.deepEqual(out.map((e) => e.key), ['low', 'a-missing', 'z-unmeasured']);
|
|
192
|
+
assert.equal(input[0].key, 'z-unmeasured', 'a new array; the listing it came from keeps its order');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('SORT_PARTS: every part a buyer compares, Security not among them', () => {
|
|
196
|
+
assert.deepEqual(SORT_PARTS.map((p) => p.part), ['tests', 'install', 'reliability', 'tester_feedback', 'docs']);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('GET /store/modules?sort=: orders the listed modules by the part, leaves New in key order', async () => {
|
|
200
|
+
state.modules = {
|
|
201
|
+
tested: { status: 'listed', title: 'Tested' }, steady: { status: 'listed', title: 'Steady' },
|
|
202
|
+
'b-new': { status: 'listed', title: 'B new' }, 'a-new': { status: 'listed', title: 'A new' },
|
|
203
|
+
};
|
|
204
|
+
state.versions = [
|
|
205
|
+
{ id: 21, module_key: 'tested', version: '1.0.0', published_at: 't' },
|
|
206
|
+
{ id: 22, module_key: 'steady', version: '1.0.0', published_at: 't' },
|
|
207
|
+
{ id: 23, module_key: 'b-new', version: '1.0.0', published_at: 't' },
|
|
208
|
+
{ id: 24, module_key: 'a-new', version: '1.0.0', published_at: 't' },
|
|
209
|
+
];
|
|
210
|
+
state.scores = [
|
|
211
|
+
{ id: 21, version_id: 21, computed_at: 1, ...score({ overall: 74 }) },
|
|
212
|
+
{ id: 22, version_id: 22, computed_at: 1, ...score({ overall: 74 }) },
|
|
213
|
+
{ id: 23, version_id: 23, computed_at: 1, ...score({ is_new: true }) },
|
|
214
|
+
{ id: 24, version_id: 24, computed_at: 1, ...score({ is_new: true }) },
|
|
215
|
+
];
|
|
216
|
+
state.signals = [
|
|
217
|
+
{ id: 21, version_id: 21, measured_at: 1, ...sig('tests', 96) },
|
|
218
|
+
{ id: 22, version_id: 21, measured_at: 1, ...sig('reliability', 52) },
|
|
219
|
+
{ id: 23, version_id: 22, measured_at: 1, ...sig('tests', 58) },
|
|
220
|
+
{ id: 24, version_id: 22, measured_at: 1, ...sig('reliability', 91) },
|
|
221
|
+
{ id: 25, version_id: 23, measured_at: 1, ...sig('reliability', 99) },
|
|
222
|
+
];
|
|
223
|
+
const { handle } = handlerFor(buildModulesRouter(), 'get', '/store/modules');
|
|
224
|
+
const byRel = await invoke(handle, { query: { sort: 'reliability' } });
|
|
225
|
+
assert.equal(byRel.body.sort, 'reliability');
|
|
226
|
+
assert.deepEqual(byRel.body.listed.map((m) => m.key), ['steady', 'tested'], 'the buyer who cares about reliability sees Steady first');
|
|
227
|
+
assert.deepEqual(byRel.body.new.map((m) => m.key), ['a-new', 'b-new'], 'New is never ranked, whatever the buyer picks');
|
|
228
|
+
const byTests = await invoke(handle, { query: { sort: 'tests' } });
|
|
229
|
+
assert.deepEqual(byTests.body.listed.map((m) => m.key), ['tested', 'steady']);
|
|
230
|
+
const plain = await invoke(handle, { query: {} });
|
|
231
|
+
assert.equal(plain.body.sort, null);
|
|
232
|
+
assert.deepEqual(plain.body.listed.map((m) => m.key), ['steady', 'tested'], 'no sort is key order, as before');
|
|
233
|
+
state.signals = [];
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('GET /store/modules?sort=: a part it does not know is refused, Security included', async () => {
|
|
237
|
+
const { handle } = handlerFor(buildModulesRouter(), 'get', '/store/modules');
|
|
238
|
+
for (const sort of ['security', 'price', 'overall']) {
|
|
239
|
+
const res = await invoke(handle, { query: { sort } });
|
|
240
|
+
assert.equal(res.statusCode, 400, `${sort} is not a part to sort by`);
|
|
241
|
+
assert.match(res.body.message, /Tests \(tests\), Clean installs \(install\), Reliability \(reliability\), Tester feedback \(tester_feedback\), How-to \(docs\)/, 'the refusal names each part as a person says it, with the value to send');
|
|
242
|
+
}
|
|
167
243
|
});
|
|
168
244
|
|
|
169
245
|
// ---------------------------------------------------------------------------
|