@bongos/core 1.21.169 → 1.21.170
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +22 -22
- package/docs/adr/0344-a-tester-is-a-project-that-opts-in-once.md +12 -0
- package/docs/copy-inventory.md +22 -20
- package/docs/copy-registry.json +41 -23
- package/docs/module-api-changelog.md +2 -0
- package/docs/modules-contract.md +1 -0
- package/docs/page-readings.json +1 -1
- package/modules/copy-desk/page-status.js +38 -1
- package/modules/hall-ui/public/approval-queue-lib.js +15 -1
- package/modules/hall-ui/public/approval-queue.css +9 -0
- package/modules/hall-ui/public/approval-queue.js +18 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +10 -0
- package/scripts/gds/module-assess-installs.js +94 -9
- package/src/module-api.js +1 -1
- package/tests/hall_approval_queue.mjs +35 -0
- package/tests/module_assess_installs.mjs +72 -6
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.170",
|
|
6
|
+
"core_contract": "1.21.170",
|
|
7
|
+
"source_commit": "dd8ede0d02e94bf42ebeecabda31e50d04207496",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-10-23T17:
|
|
9
|
+
"built_at": "2026-10-23T17:21:55.040Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 609,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 3831,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "e66d52e5a8929945674ce60631b73e08c435660bf0228c776306ec586cc77464",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2182,7 +2182,7 @@
|
|
|
2182
2182
|
{
|
|
2183
2183
|
"path": "docs/adr/0344-a-tester-is-a-project-that-opts-in-once.md",
|
|
2184
2184
|
"mode": "0000644",
|
|
2185
|
-
"sha256": "
|
|
2185
|
+
"sha256": "3ad19bfe66fbff271a718af8d151f814371b1d03fa45463a88010f9915ee1843"
|
|
2186
2186
|
},
|
|
2187
2187
|
{
|
|
2188
2188
|
"path": "docs/adr/0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md",
|
|
@@ -2412,12 +2412,12 @@
|
|
|
2412
2412
|
{
|
|
2413
2413
|
"path": "docs/copy-inventory.md",
|
|
2414
2414
|
"mode": "0000644",
|
|
2415
|
-
"sha256": "
|
|
2415
|
+
"sha256": "6342b5c569de415d86db07c9e557eee201292149a4fe69d65bbee1b66eee2a70"
|
|
2416
2416
|
},
|
|
2417
2417
|
{
|
|
2418
2418
|
"path": "docs/copy-registry.json",
|
|
2419
2419
|
"mode": "0000644",
|
|
2420
|
-
"sha256": "
|
|
2420
|
+
"sha256": "e2c97496bcaef11d81aba576f2dc6d50128ec1f04a5023ef02d7a67aabce07a0"
|
|
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": "69e717f42c7d4a55a1c985e886807078c1cc320c4650993ad7ecd5aba27d3d2c"
|
|
2931
2931
|
},
|
|
2932
2932
|
{
|
|
2933
2933
|
"path": "docs/modules-contract.md",
|
|
2934
2934
|
"mode": "0000644",
|
|
2935
|
-
"sha256": "
|
|
2935
|
+
"sha256": "73757f66afb17289933edd12b20e0c8d903b37d74a004a622826adf09742401f"
|
|
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": "60976ab4a0fa35bcdc1dcb4c14ddcd1221f7a6f98bd31f79c9b8bc458edb8ba8"
|
|
3021
3021
|
},
|
|
3022
3022
|
{
|
|
3023
3023
|
"path": "docs/project-context.template.md",
|
|
@@ -4487,7 +4487,7 @@
|
|
|
4487
4487
|
{
|
|
4488
4488
|
"path": "modules/copy-desk/page-status.js",
|
|
4489
4489
|
"mode": "0000644",
|
|
4490
|
-
"sha256": "
|
|
4490
|
+
"sha256": "bf482381365aac38d983048f87597cc7d498ff320915502e4d5cb8ac3387d3e4"
|
|
4491
4491
|
},
|
|
4492
4492
|
{
|
|
4493
4493
|
"path": "modules/copy-desk/pages.js",
|
|
@@ -5552,17 +5552,17 @@
|
|
|
5552
5552
|
{
|
|
5553
5553
|
"path": "modules/hall-ui/public/approval-queue-lib.js",
|
|
5554
5554
|
"mode": "0000644",
|
|
5555
|
-
"sha256": "
|
|
5555
|
+
"sha256": "876b7f94461c8a13e0ce85597e60a9ff089adcde86ed8c3fd540e06ac92eab87"
|
|
5556
5556
|
},
|
|
5557
5557
|
{
|
|
5558
5558
|
"path": "modules/hall-ui/public/approval-queue.css",
|
|
5559
5559
|
"mode": "0000644",
|
|
5560
|
-
"sha256": "
|
|
5560
|
+
"sha256": "6bf30c8a3d397a3f7994e4ecee848a76172f7ecb828d8bcf286f45eaa9c7bccb"
|
|
5561
5561
|
},
|
|
5562
5562
|
{
|
|
5563
5563
|
"path": "modules/hall-ui/public/approval-queue.js",
|
|
5564
5564
|
"mode": "0000644",
|
|
5565
|
-
"sha256": "
|
|
5565
|
+
"sha256": "3272cdcbad2a90892be9d248f2c8f52e6f97d44404d05c1b8159827e6ed54b75"
|
|
5566
5566
|
},
|
|
5567
5567
|
{
|
|
5568
5568
|
"path": "modules/hall-ui/public/atlas.css",
|
|
@@ -10602,12 +10602,12 @@
|
|
|
10602
10602
|
{
|
|
10603
10603
|
"path": "package-lock.json",
|
|
10604
10604
|
"mode": "0000644",
|
|
10605
|
-
"sha256": "
|
|
10605
|
+
"sha256": "612aedb1a80183da059e896d4a3325c4672d4159009eda6329159b0cc23d9c34"
|
|
10606
10606
|
},
|
|
10607
10607
|
{
|
|
10608
10608
|
"path": "package.json",
|
|
10609
10609
|
"mode": "0000644",
|
|
10610
|
-
"sha256": "
|
|
10610
|
+
"sha256": "9a75c884c3bbd879707469af51cac45699ae093714ed9a2176d6a95bfbb954e0"
|
|
10611
10611
|
},
|
|
10612
10612
|
{
|
|
10613
10613
|
"path": "public-docs/index.html",
|
|
@@ -10627,7 +10627,7 @@
|
|
|
10627
10627
|
{
|
|
10628
10628
|
"path": "release-notes.json",
|
|
10629
10629
|
"mode": "0000644",
|
|
10630
|
-
"sha256": "
|
|
10630
|
+
"sha256": "bc246f86279741c379c800cd01e417876b45cd7b73adf48d0bb8c7204517376e"
|
|
10631
10631
|
},
|
|
10632
10632
|
{
|
|
10633
10633
|
"path": "scripts/cron/style_guide_audit.sh",
|
|
@@ -11512,7 +11512,7 @@
|
|
|
11512
11512
|
{
|
|
11513
11513
|
"path": "scripts/gds/module-assess-installs.js",
|
|
11514
11514
|
"mode": "0000644",
|
|
11515
|
-
"sha256": "
|
|
11515
|
+
"sha256": "70b1ff5aa78d085481afba69f9f3ec8b0ba8b1901e2c683b36c9383585ada6be"
|
|
11516
11516
|
},
|
|
11517
11517
|
{
|
|
11518
11518
|
"path": "scripts/gds/module-assess-score.js",
|
|
@@ -13017,7 +13017,7 @@
|
|
|
13017
13017
|
{
|
|
13018
13018
|
"path": "src/module-api.js",
|
|
13019
13019
|
"mode": "0000644",
|
|
13020
|
-
"sha256": "
|
|
13020
|
+
"sha256": "1792868060800cda8dd8d8f8426cd070d7083a0d5ae74f1b0964e238ccf79e4d"
|
|
13021
13021
|
},
|
|
13022
13022
|
{
|
|
13023
13023
|
"path": "src/module-loader/catalog.js",
|
|
@@ -15512,7 +15512,7 @@
|
|
|
15512
15512
|
{
|
|
15513
15513
|
"path": "tests/hall_approval_queue.mjs",
|
|
15514
15514
|
"mode": "0000644",
|
|
15515
|
-
"sha256": "
|
|
15515
|
+
"sha256": "d036f80973aca6351c34f859439c073e9e9e8e410ccdc63e7148be07d88c8f4f"
|
|
15516
15516
|
},
|
|
15517
15517
|
{
|
|
15518
15518
|
"path": "tests/hall_audit.mjs",
|
|
@@ -16607,7 +16607,7 @@
|
|
|
16607
16607
|
{
|
|
16608
16608
|
"path": "tests/module_assess_installs.mjs",
|
|
16609
16609
|
"mode": "0000644",
|
|
16610
|
-
"sha256": "
|
|
16610
|
+
"sha256": "7baa761878723075aaf8e46b5e336abc3f5534d94425737c5a2e532a4b744035"
|
|
16611
16611
|
},
|
|
16612
16612
|
{
|
|
16613
16613
|
"path": "tests/module_assess_price_parity.mjs",
|
|
@@ -75,3 +75,15 @@ feedback part of the module's score (ADR 0343 D1, task 1003806).
|
|
|
75
75
|
updated.
|
|
76
76
|
- **Releasing to everyone at once.** Testing would only happen when volunteers happened to be
|
|
77
77
|
there.
|
|
78
|
+
|
|
79
|
+
## Implementation note — tester feedback (task 1003806, 2026-10-23)
|
|
80
|
+
|
|
81
|
+
D5 is `assessTesterFeedback` in `scripts/gds/module-assess-installs.js`, re-scored on every
|
|
82
|
+
install or error report alongside Install and Reliability. A report counts when it falls in the
|
|
83
|
+
version's **tester window** — from its first tester-channel row to the first later row on any
|
|
84
|
+
other channel (promoted or withdrawn), or now — because only tester projects are served a tester
|
|
85
|
+
version. A tester it went well for installed without failing and reported no runtime errors in
|
|
86
|
+
the window. "Whether they kept it" is not measured: nothing reports an uninstall. The module's
|
|
87
|
+
author never counts, and fewer than 3 testers is `pending`, as for Install. So that two friendly
|
|
88
|
+
testers cannot outrank two hundred real ones, the count is blended with ten neutral testers:
|
|
89
|
+
`round(100 × (went well + 5) / (testers + 10))` — 3 of 3 scores 62, 200 of 200 scores 98.
|
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
|
+
**2793 strings** across 3 surfaces · 2374 distinct pieces of copy · 2793 certain, 0 heuristic · 243 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` | 150 |
|
|
13
|
+
| `builders-hall` | `modules/hall-ui/public` | 150 | 1973 |
|
|
14
14
|
| `status-dashboard` | `modules/status-ui/public` | 4 | 100 |
|
|
15
15
|
| `landing` | `modules/public-landing/public` | 9 | 720 |
|
|
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
|
| … | | _203 more in the JSON registry_ | |
|
|
80
80
|
|
|
81
|
-
## `builders-hall` —
|
|
81
|
+
## `builders-hall` — 1973 strings
|
|
82
82
|
|
|
83
83
|
| id | conf | text | where |
|
|
84
84
|
|---|---|---|---|
|
|
@@ -836,7 +836,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
836
836
|
| `1a46c31343cb` | certain | No projects are hosted here yet. | `modules/hall-ui/public/fleet-costs.js:68` |
|
|
837
837
|
| `9f73b3f3dad0` | certain | No rank floor — any builder can claim this task | `modules/hall-ui/public/task-lib.js:82` |
|
|
838
838
|
| `4ffd939755e0` | certain | No real-world data yet, so it is listed but not ranked. | `modules/hall-ui/public/modules.js:334` |
|
|
839
|
-
| `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:
|
|
839
|
+
| `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:136` |
|
|
840
840
|
| `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` |
|
|
841
841
|
| `2947c1ddea9a` | certain | No running box to deploy. | `modules/hall-ui/public/deploy.js:708` |
|
|
842
842
|
| `c8fa4bdba44a` | certain | No saved views yet. Set the filters you want, then save them here. | `modules/hall-ui/public/hall-kit.js:411` |
|
|
@@ -853,9 +853,9 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
853
853
|
| `c9800f8b6bc8` | certain | No versions declared yet. | `modules/hall-ui/public/roadmap.js:481` |
|
|
854
854
|
| `4654849396a3` | certain | No work reviewed yet. | `modules/hall-ui/public/hall-widgets.js:191` |
|
|
855
855
|
| `9cf1c752b65d` | certain | No works shipped yet — the works charts draw once the first one lands. | `modules/hall-ui/public/profile-charts.js:594` |
|
|
856
|
-
| `9dbe1981055c` | certain | No {…} render is attached to this page. | `modules/hall-ui/public/approval-queue.js:
|
|
856
|
+
| `9dbe1981055c` | certain | No {…} render is attached to this page. | `modules/hall-ui/public/approval-queue.js:135` |
|
|
857
857
|
| `086665af6208` | certain | No {…} sessions recorded. Choose another kind to widen the ledger. | `modules/hall-ui/public/sessions.js:148` |
|
|
858
|
-
| `730a248e7888` | certain | No {…} {…} render; showing {…} {…}. | `modules/hall-ui/public/approval-queue.js:
|
|
858
|
+
| `730a248e7888` | certain | No {…} {…} render; showing {…} {…}. | `modules/hall-ui/public/approval-queue.js:140` |
|
|
859
859
|
| `1967d2176f2e` | certain | Nobody has an open ask right now. | `modules/hall-ui/public/collab.html:84` |
|
|
860
860
|
| `c65c9f5c02e3` | certain | Nobody is waiting on you. Requests reach you by role — | `modules/hall-ui/public/collab.html:54` |
|
|
861
861
|
| `d5cf262e671d` | certain | Nobody is writing this page. Take it, and every line here is yours to type over. | `modules/hall-ui/public/tweak-editor.js:314` |
|
|
@@ -924,7 +924,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
924
924
|
| `3e1d0b2334d1` | certain | Only if it needs it — with the AI at your side, or on your own. | `modules/hall-ui/public/studio.js:211` |
|
|
925
925
|
| `45b667751b95` | certain | Only me | `modules/hall-ui/public/settings.html:294` |
|
|
926
926
|
| `f9f2a3af318a` | certain | Only mine | `modules/hall-ui/public/work.html:70` |
|
|
927
|
-
| `ab48bab11b70` | certain | Only the artist who wrote this page, or another artist, can approve it or send it back. | `modules/hall-ui/public/approval-queue.js:
|
|
927
|
+
| `ab48bab11b70` | certain | Only the artist who wrote this page, or another artist, can approve it or send it back. | `modules/hall-ui/public/approval-queue.js:171` |
|
|
928
928
|
| `e11eae1d0a92` | certain | Only the goals your thoughts touched — everything else fades. | `modules/hall-ui/public/thinking.html:83` |
|
|
929
929
|
| `f74ec527907f` | certain | Only the people allowed to upgrade this project see an Update button here. | `modules/hall-ui/public/settings-software-update.js:116` |
|
|
930
930
|
| `f28917ca859e` | certain | Only the thoughts of the last thirty days. | `modules/hall-ui/public/thinking.html:81` |
|
|
@@ -954,7 +954,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
954
954
|
| `f6f68ba322be` | certain | Open {…} | `modules/hall-ui/public/goal-inbox.js:69` |
|
|
955
955
|
| `f6f68ba322be` | certain | Open {…} | `modules/hall-ui/public/goals.js:84` |
|
|
956
956
|
| `35d46f963cd1` | certain | Opening the doc… | `modules/hall-ui/public/page-notes.js:313` |
|
|
957
|
-
| `7a8dfadb7468` | certain | Opening the rendered page… | `modules/hall-ui/public/approval-queue.js:
|
|
957
|
+
| `7a8dfadb7468` | certain | Opening the rendered page… | `modules/hall-ui/public/approval-queue.js:130` |
|
|
958
958
|
| `62e75a9d0af4` | certain | Opening the reviews… | `modules/hall-ui/public/studio.html:171` |
|
|
959
959
|
| `94f3d9f46098` | certain | Opening your doc… | `modules/hall-ui/public/page-notes.js:146` |
|
|
960
960
|
| `94f3d9f46098` | certain | Opening your doc… | `modules/hall-ui/public/page-notes.js:338` |
|
|
@@ -1247,6 +1247,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1247
1247
|
| `6c6544f5c011` | certain | Stored encrypted; never shown again after saving — only its last four digits. | `modules/hall-ui/public/settings.html:226` |
|
|
1248
1248
|
| `5831de503015` | certain | Studio — {{worldName}} | `modules/hall-ui/public/studio.html:10` |
|
|
1249
1249
|
| `5831de503015` | certain | Studio — {{worldName}} | `modules/hall-ui/public/studio.html:10` |
|
|
1250
|
+
| `30b78bc382a1` | certain | Submitted, not applied yet | `modules/hall-ui/public/approval-queue.js:193` |
|
|
1251
|
+
| `7d56be90cd16` | certain | Submitted, not applied yet ({…}) | `modules/hall-ui/public/approval-queue.js:193` |
|
|
1250
1252
|
| `f79e1fb8ebd4` | certain | Suggested as you type; leave it on "no goal" and it waits for triage. | `modules/hall-ui/public/ideas.html:84` |
|
|
1251
1253
|
| `a1482eff4ead` | certain | Suggested for you | `modules/hall-ui/public/settings-specialities.js:384` |
|
|
1252
1254
|
| `1e40f33483eb` | certain | Switch theme | `modules/hall-ui/public/shell.js:549` |
|
|
@@ -1321,7 +1323,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1321
1323
|
| `5615bef4a99e` | certain | The governor city | `modules/hall-ui/public/city.html:79` |
|
|
1322
1324
|
| `19b23f2cc45b` | certain | The grading panel could not run when this task was shipped, so no quality signal was recorded — an outage, not | `modules/hall-ui/public/task.js:394` |
|
|
1323
1325
|
| `027e9420daa2` | certain | The headline lands, but the sub-line reads like a brochure. | `modules/hall-ui/public/studio.js:191` |
|
|
1324
|
-
| `f08587a38a2a` | certain | The hero line is too long on a phone. | `modules/hall-ui/public/approval-queue.js:
|
|
1326
|
+
| `f08587a38a2a` | certain | The hero line is too long on a phone. | `modules/hall-ui/public/approval-queue.js:177` |
|
|
1325
1327
|
| `9f45c63c9697` | certain | The kinds of work you prefer. Task suggestions lean toward your choices — pick one, several, or none. | `modules/hall-ui/public/settings.html:116` |
|
|
1326
1328
|
| `2a2152c7bcfb` | certain | The ladder's own shape at this size is {…}. {…} | `modules/hall-ui/public/government.js:926` |
|
|
1327
1329
|
| `814eb6889339` | certain | The ledger | `modules/hall-ui/public/goal-map.html:102` |
|
|
@@ -1340,7 +1342,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1340
1342
|
| `329a1d350f07` | certain | The page changed after your draft was saved, so the draft is not shown over its new words. Rewrite the lines a | `modules/hall-ui/public/tweak-editor.js:310` |
|
|
1341
1343
|
| `aa716f3dd551` | certain | The page could not be loaded. Try again. | `modules/hall-ui/public/builders.js:1455` |
|
|
1342
1344
|
| `28e0c1457b08` | certain | The page is in. | `modules/hall-ui/public/tweak-editor.html:96` |
|
|
1343
|
-
| `4821b0e74460` | certain | The page, rendered | `modules/hall-ui/public/approval-queue.js:
|
|
1345
|
+
| `4821b0e74460` | certain | The page, rendered | `modules/hall-ui/public/approval-queue.js:215` |
|
|
1344
1346
|
| `74bc22f38140` | certain | The platform confirmed this request came from the account it names. | `modules/hall-ui/public/watch.js:396` |
|
|
1345
1347
|
| `6c3d88de42de` | certain | The primer | `modules/hall-ui/public/primer.html:41` |
|
|
1346
1348
|
| `dea378cd4837` | certain | The primer did not load | `modules/hall-ui/public/primer.html:88` |
|
|
@@ -1349,7 +1351,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1349
1351
|
| `c0034db3281c` | certain | The reason is recorded in the audit log. This is recoverable: a retired task can be restored to the backlog wi | `modules/hall-ui/public/task.js:788` |
|
|
1350
1352
|
| `1ce78006fe00` | certain | The reference did not load | `modules/hall-ui/public/drachmae.html:85` |
|
|
1351
1353
|
| `c43812b59e97` | certain | The release notes could not be read: {…}. The versions are still listed. | `modules/hall-ui/public/settings-software-update.js:160` |
|
|
1352
|
-
| `8040b1d05ff2` | certain | The renders could not be read just now. Close the queue and open it again. | `modules/hall-ui/public/approval-queue.js:
|
|
1354
|
+
| `8040b1d05ff2` | certain | The renders could not be read just now. Close the queue and open it again. | `modules/hall-ui/public/approval-queue.js:131` |
|
|
1353
1355
|
| `3b5260b8780a` | certain | The reserved 16-rank ladder, which ranks are live today (archon, metic, thetes, xenos), and what each may do. | `modules/hall-ui/public/diagrams.html:60` |
|
|
1354
1356
|
| `cfa55899240b` | certain | The roadmap could not be loaded right now. Reload to try again. | `modules/hall-ui/public/roadmap.js:494` |
|
|
1355
1357
|
| `2dea5ed89aa1` | certain | The roadmap failed to load. Reload to try again. | `modules/hall-ui/public/roadmap.js:495` |
|
|
@@ -1388,7 +1390,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1388
1390
|
| `34555793f33e` | certain | The task list could not be loaded right now. | `modules/hall-ui/public/work.js:1026` |
|
|
1389
1391
|
| `8c991355687d` | certain | The upgrade checked {…} and refused:{…} | `modules/hall-ui/public/deploy.js:473` |
|
|
1390
1392
|
| `a42a37c43bfb` | certain | The words this project shows people — what has been flagged, and what nobody has looked at. | `modules/hall-ui/public/copy-desk.js:328` |
|
|
1391
|
-
| `d51b954e0aa9` | certain | The words you changed | `modules/hall-ui/public/approval-queue.js:
|
|
1393
|
+
| `d51b954e0aa9` | certain | The words you changed | `modules/hall-ui/public/approval-queue.js:215` |
|
|
1392
1394
|
| `bcdbded93d4e` | certain | The work board failed to load. Reload to try again. | `modules/hall-ui/public/work.js:1214` |
|
|
1393
1395
|
| `996d2aa0e2f0` | certain | Then copy the project onto your machine, install its dependencies, and sign in: | `modules/hall-ui/public/hall-render.js:848` |
|
|
1394
1396
|
| `383018c7df70` | certain | There is no transcript you can read for this session. | `modules/hall-ui/public/sessions.js:234` |
|
|
@@ -1432,7 +1434,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1432
1434
|
| `1116d2cad470` | certain | This project tests new versions first | `modules/hall-ui/public/modules.js:211` |
|
|
1433
1435
|
| `3c8aaec39c40` | certain | This project's code expects {…} database change{…} that its database has not applied yet. Anything that relies | `modules/hall-ui/public/deploy.js:587` |
|
|
1434
1436
|
| `6e9dfe1fb903` | certain | This restarts {…} onto core | `modules/hall-ui/public/deploy.js:569` |
|
|
1435
|
-
| `b9ed9a5f38dd` | certain | This round changed no lines. | `modules/hall-ui/public/approval-queue.js:
|
|
1437
|
+
| `b9ed9a5f38dd` | certain | This round changed no lines. | `modules/hall-ui/public/approval-queue.js:150` |
|
|
1436
1438
|
| `69e8beced2ff` | certain | This sends the idea back to its author. Say why, and which section. | `modules/hall-ui/public/board-room.js:379` |
|
|
1437
1439
|
| `09256febc32d` | certain | This sentence has {…} {…} where a value is dropped in when it is shown. | `modules/hall-ui/public/copy-desk.js:422` |
|
|
1438
1440
|
| `9333d71e3e15` | certain | This speciality offers no skills to choose. | `modules/hall-ui/public/settings-specialities.js:106` |
|
|
@@ -1518,7 +1520,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1518
1520
|
| `54952bcb6ca3` | certain | What it is for | `modules/hall-ui/public/goal-author.js:38` |
|
|
1519
1521
|
| `ae44a375e814` | certain | What it unlocks | `modules/hall-ui/public/ranks.js:109` |
|
|
1520
1522
|
| `00dc1f0f83e1` | certain | What kind of wrong? | `modules/hall-ui/public/copy-desk.html:87` |
|
|
1521
|
-
| `c9a4c6f16433` | certain | What needs changing | `modules/hall-ui/public/approval-queue.js:
|
|
1523
|
+
| `c9a4c6f16433` | certain | What needs changing | `modules/hall-ui/public/approval-queue.js:177` |
|
|
1522
1524
|
| `9a85f2c19b48` | certain | What should it say? | `modules/hall-ui/public/copy-desk.html:156` |
|
|
1523
1525
|
| `7c765a4209d1` | certain | What should this page be? | `modules/hall-ui/public/page-notes.js:141` |
|
|
1524
1526
|
| `2dd116d2d817` | certain | What the current wording gets wrong, and what yours does about it. | `modules/hall-ui/public/copy-desk.html:166` |
|
|
@@ -1650,8 +1652,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1650
1652
|
| `2177da93be9b` | certain | and leave this alone. | `modules/hall-ui/public/settings.html:347` |
|
|
1651
1653
|
| `0257cb508ae7` | certain | and {…} more below this thought | `modules/hall-ui/public/profile.js:489` |
|
|
1652
1654
|
| `d86265f99668` | certain | and {…} more waiting{…} | `modules/hall-ui/public/studio.js:311` |
|
|
1653
|
-
| `9bf4d3cabf80` | certain | approval queue | `modules/hall-ui/public/approval-queue.js:
|
|
1654
|
-
| `ac37199ccdbb` | certain | approve and ship | `modules/hall-ui/public/approval-queue.js:
|
|
1655
|
+
| `9bf4d3cabf80` | certain | approval queue | `modules/hall-ui/public/approval-queue.js:208` |
|
|
1656
|
+
| `ac37199ccdbb` | certain | approve and ship | `modules/hall-ui/public/approval-queue.js:175` |
|
|
1655
1657
|
| `b4033000c537` | certain | as a table | `modules/hall-ui/public/profile-charts.js:281` |
|
|
1656
1658
|
| `72f977e4c47f` | certain | as configuration: membership rank:{…} — the council rank below, once you have added it and assigned its member | `modules/hall-ui/public/government.js:923` |
|
|
1657
1659
|
| `d7c511423bc0` | certain | as configuration: {…} | `modules/hall-ui/public/government.js:833` |
|
|
@@ -1715,8 +1717,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1715
1717
|
| `8e66451d0c12` | certain | gravity — the goal that catches it lights as you type; letting it float is a real answer | `modules/hall-ui/public/thinking.html:102` |
|
|
1716
1718
|
| `c5862d055f1a` | certain | hard floor | `modules/hall-ui/public/gate.js:142` |
|
|
1717
1719
|
| `55d673a5f0ec` | certain | highest credits | `modules/hall-ui/public/hall-widgets.js:176` |
|
|
1718
|
-
| `c2267f78f931` | certain | highlighted: the words you changed | `modules/hall-ui/public/approval-queue.js:
|
|
1719
|
-
| `c2267f78f931` | certain | highlighted: the words you changed | `modules/hall-ui/public/approval-queue.js:
|
|
1720
|
+
| `c2267f78f931` | certain | highlighted: the words you changed | `modules/hall-ui/public/approval-queue.js:171` |
|
|
1721
|
+
| `c2267f78f931` | certain | highlighted: the words you changed | `modules/hall-ui/public/approval-queue.js:184` |
|
|
1720
1722
|
| `da40bf505b89` | certain | hist. | `modules/hall-ui/public/sky-panel.js:40` |
|
|
1721
1723
|
| `fda88709bc50` | certain | holds the repo. Follow the numbered path to see how your work gets to prod. This is the largest diagram — | `modules/hall-ui/public/diagrams.html:74` |
|
|
1722
1724
|
| `17ab8db79e1f` | certain | holds the worker, the code, and your local memory; the | `modules/hall-ui/public/diagrams.html:74` |
|
|
@@ -1788,7 +1790,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1788
1790
|
| `cbaf58cdbe35` | certain | open the queue | `modules/hall-ui/public/goal-inbox.js:561` |
|
|
1789
1791
|
| `af3a8e0fe6a8` | certain | open your goals | `modules/hall-ui/public/goal-inbox.js:127` |
|
|
1790
1792
|
| `af3a8e0fe6a8` | certain | open your goals | `modules/hall-ui/public/goal-inbox.js:158` |
|
|
1791
|
-
| `9a803563c948` | certain | open {…} | `modules/hall-ui/public/approval-queue.js:
|
|
1793
|
+
| `9a803563c948` | certain | open {…} | `modules/hall-ui/public/approval-queue.js:166` |
|
|
1792
1794
|
| `4f279d68175f` | certain | open, on the Security tab | `modules/hall-ui/public/watch.html:50` |
|
|
1793
1795
|
| `7b85e6494596` | certain | opens a new tab | `modules/hall-ui/public/palette.js:315` |
|
|
1794
1796
|
| `c38db02adb61` | certain | or above | `modules/hall-ui/public/government.js:602` |
|
|
@@ -1818,7 +1820,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1818
1820
|
| `9bec9eb4829e` | certain | scope: {…} | `modules/hall-ui/public/goals-render.js:855` |
|
|
1819
1821
|
| `9c35bc28a3e9` | certain | see it | `modules/hall-ui/public/goal-map.html:96` |
|
|
1820
1822
|
| `9c35bc28a3e9` | certain | see it | `modules/hall-ui/public/thinking.html:104` |
|
|
1821
|
-
| `92775a3d8d0d` | certain | send it back | `modules/hall-ui/public/approval-queue.js:
|
|
1823
|
+
| `92775a3d8d0d` | certain | send it back | `modules/hall-ui/public/approval-queue.js:182` |
|
|
1822
1824
|
| `276f0b25e030` | certain | sent today, live | `modules/hall-ui/public/my-agents.js:450` |
|
|
1823
1825
|
| `a13ee0cde7f2` | certain | set yours in Settings | `modules/hall-ui/public/collab.html:55` |
|
|
1824
1826
|
| `be368c58934a` | certain | settled · | `modules/hall-ui/public/sky-interaction.js:129` |
|
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": 2793,
|
|
7
|
+
"unique_ids": 2374,
|
|
8
|
+
"certain": 2793,
|
|
9
9
|
"heuristic": 0,
|
|
10
10
|
"duplicate_ids": 243
|
|
11
11
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
15
|
"files": 150,
|
|
16
|
-
"strings":
|
|
16
|
+
"strings": 1973
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -2260,8 +2260,8 @@
|
|
|
2260
2260
|
"text": "highlighted: the words you changed",
|
|
2261
2261
|
"occurrences": 2,
|
|
2262
2262
|
"at": [
|
|
2263
|
-
"modules/hall-ui/public/approval-queue.js:
|
|
2264
|
-
"modules/hall-ui/public/approval-queue.js:
|
|
2263
|
+
"modules/hall-ui/public/approval-queue.js:171",
|
|
2264
|
+
"modules/hall-ui/public/approval-queue.js:184"
|
|
2265
2265
|
]
|
|
2266
2266
|
},
|
|
2267
2267
|
{
|
|
@@ -9437,7 +9437,7 @@
|
|
|
9437
9437
|
"surface": "builders-hall",
|
|
9438
9438
|
"text": "No render is attached to this page yet. /tweak attaches them when it applies a page; until then there is nothing to approve from, so send it back or wait.",
|
|
9439
9439
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
9440
|
-
"line":
|
|
9440
|
+
"line": 136,
|
|
9441
9441
|
"origin": "js-markup",
|
|
9442
9442
|
"confidence": "certain"
|
|
9443
9443
|
},
|
|
@@ -9590,7 +9590,7 @@
|
|
|
9590
9590
|
"surface": "builders-hall",
|
|
9591
9591
|
"text": "No {…} render is attached to this page.",
|
|
9592
9592
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
9593
|
-
"line":
|
|
9593
|
+
"line": 135,
|
|
9594
9594
|
"origin": "js-markup",
|
|
9595
9595
|
"confidence": "certain"
|
|
9596
9596
|
},
|
|
@@ -9608,7 +9608,7 @@
|
|
|
9608
9608
|
"surface": "builders-hall",
|
|
9609
9609
|
"text": "No {…} {…} render; showing {…} {…}.",
|
|
9610
9610
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
9611
|
-
"line":
|
|
9611
|
+
"line": 140,
|
|
9612
9612
|
"origin": "js-markup",
|
|
9613
9613
|
"confidence": "certain"
|
|
9614
9614
|
},
|
|
@@ -10229,7 +10229,7 @@
|
|
|
10229
10229
|
"surface": "builders-hall",
|
|
10230
10230
|
"text": "Only the artist who wrote this page, or another artist, can approve it or send it back.",
|
|
10231
10231
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
10232
|
-
"line":
|
|
10232
|
+
"line": 171,
|
|
10233
10233
|
"origin": "js-markup",
|
|
10234
10234
|
"confidence": "certain"
|
|
10235
10235
|
},
|
|
@@ -10499,7 +10499,7 @@
|
|
|
10499
10499
|
"surface": "builders-hall",
|
|
10500
10500
|
"text": "Opening the rendered page…",
|
|
10501
10501
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
10502
|
-
"line":
|
|
10502
|
+
"line": 130,
|
|
10503
10503
|
"origin": "js-markup",
|
|
10504
10504
|
"confidence": "certain"
|
|
10505
10505
|
},
|
|
@@ -13131,6 +13131,24 @@
|
|
|
13131
13131
|
"origin": "html-title",
|
|
13132
13132
|
"confidence": "certain"
|
|
13133
13133
|
},
|
|
13134
|
+
{
|
|
13135
|
+
"id": "30b78bc382a1",
|
|
13136
|
+
"surface": "builders-hall",
|
|
13137
|
+
"text": "Submitted, not applied yet",
|
|
13138
|
+
"file": "modules/hall-ui/public/approval-queue.js",
|
|
13139
|
+
"line": 193,
|
|
13140
|
+
"origin": "js-markup",
|
|
13141
|
+
"confidence": "certain"
|
|
13142
|
+
},
|
|
13143
|
+
{
|
|
13144
|
+
"id": "7d56be90cd16",
|
|
13145
|
+
"surface": "builders-hall",
|
|
13146
|
+
"text": "Submitted, not applied yet ({…})",
|
|
13147
|
+
"file": "modules/hall-ui/public/approval-queue.js",
|
|
13148
|
+
"line": 193,
|
|
13149
|
+
"origin": "js-markup",
|
|
13150
|
+
"confidence": "certain"
|
|
13151
|
+
},
|
|
13134
13152
|
{
|
|
13135
13153
|
"id": "f79e1fb8ebd4",
|
|
13136
13154
|
"surface": "builders-hall",
|
|
@@ -13802,7 +13820,7 @@
|
|
|
13802
13820
|
"surface": "builders-hall",
|
|
13803
13821
|
"text": "The hero line is too long on a phone.",
|
|
13804
13822
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
13805
|
-
"line":
|
|
13823
|
+
"line": 177,
|
|
13806
13824
|
"origin": "js-markup",
|
|
13807
13825
|
"confidence": "certain"
|
|
13808
13826
|
},
|
|
@@ -13973,7 +13991,7 @@
|
|
|
13973
13991
|
"surface": "builders-hall",
|
|
13974
13992
|
"text": "The page, rendered",
|
|
13975
13993
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
13976
|
-
"line":
|
|
13994
|
+
"line": 215,
|
|
13977
13995
|
"origin": "js-markup",
|
|
13978
13996
|
"confidence": "certain"
|
|
13979
13997
|
},
|
|
@@ -14054,7 +14072,7 @@
|
|
|
14054
14072
|
"surface": "builders-hall",
|
|
14055
14073
|
"text": "The renders could not be read just now. Close the queue and open it again.",
|
|
14056
14074
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
14057
|
-
"line":
|
|
14075
|
+
"line": 131,
|
|
14058
14076
|
"origin": "js-markup",
|
|
14059
14077
|
"confidence": "certain"
|
|
14060
14078
|
},
|
|
@@ -14405,7 +14423,7 @@
|
|
|
14405
14423
|
"surface": "builders-hall",
|
|
14406
14424
|
"text": "The words you changed",
|
|
14407
14425
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
14408
|
-
"line":
|
|
14426
|
+
"line": 215,
|
|
14409
14427
|
"origin": "js-markup",
|
|
14410
14428
|
"confidence": "certain"
|
|
14411
14429
|
},
|
|
@@ -14801,7 +14819,7 @@
|
|
|
14801
14819
|
"surface": "builders-hall",
|
|
14802
14820
|
"text": "This round changed no lines.",
|
|
14803
14821
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
14804
|
-
"line":
|
|
14822
|
+
"line": 150,
|
|
14805
14823
|
"origin": "js-markup",
|
|
14806
14824
|
"confidence": "certain"
|
|
14807
14825
|
},
|
|
@@ -15575,7 +15593,7 @@
|
|
|
15575
15593
|
"surface": "builders-hall",
|
|
15576
15594
|
"text": "What needs changing",
|
|
15577
15595
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
15578
|
-
"line":
|
|
15596
|
+
"line": 177,
|
|
15579
15597
|
"origin": "js-markup",
|
|
15580
15598
|
"confidence": "certain"
|
|
15581
15599
|
},
|
|
@@ -16763,7 +16781,7 @@
|
|
|
16763
16781
|
"surface": "builders-hall",
|
|
16764
16782
|
"text": "approval queue",
|
|
16765
16783
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
16766
|
-
"line":
|
|
16784
|
+
"line": 208,
|
|
16767
16785
|
"origin": "js-markup",
|
|
16768
16786
|
"confidence": "certain"
|
|
16769
16787
|
},
|
|
@@ -16772,7 +16790,7 @@
|
|
|
16772
16790
|
"surface": "builders-hall",
|
|
16773
16791
|
"text": "approve and ship",
|
|
16774
16792
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
16775
|
-
"line":
|
|
16793
|
+
"line": 175,
|
|
16776
16794
|
"origin": "js-markup",
|
|
16777
16795
|
"confidence": "certain"
|
|
16778
16796
|
},
|
|
@@ -17348,7 +17366,7 @@
|
|
|
17348
17366
|
"surface": "builders-hall",
|
|
17349
17367
|
"text": "highlighted: the words you changed",
|
|
17350
17368
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
17351
|
-
"line":
|
|
17369
|
+
"line": 171,
|
|
17352
17370
|
"origin": "js-markup",
|
|
17353
17371
|
"confidence": "certain"
|
|
17354
17372
|
},
|
|
@@ -17357,7 +17375,7 @@
|
|
|
17357
17375
|
"surface": "builders-hall",
|
|
17358
17376
|
"text": "highlighted: the words you changed",
|
|
17359
17377
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
17360
|
-
"line":
|
|
17378
|
+
"line": 184,
|
|
17361
17379
|
"origin": "js-markup",
|
|
17362
17380
|
"confidence": "certain"
|
|
17363
17381
|
},
|
|
@@ -18005,7 +18023,7 @@
|
|
|
18005
18023
|
"surface": "builders-hall",
|
|
18006
18024
|
"text": "open {…}",
|
|
18007
18025
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
18008
|
-
"line":
|
|
18026
|
+
"line": 166,
|
|
18009
18027
|
"origin": "js-markup",
|
|
18010
18028
|
"confidence": "certain"
|
|
18011
18029
|
},
|
|
@@ -18275,7 +18293,7 @@
|
|
|
18275
18293
|
"surface": "builders-hall",
|
|
18276
18294
|
"text": "send it back",
|
|
18277
18295
|
"file": "modules/hall-ui/public/approval-queue.js",
|
|
18278
|
-
"line":
|
|
18296
|
+
"line": 182,
|
|
18279
18297
|
"origin": "js-markup",
|
|
18280
18298
|
"confidence": "certain"
|
|
18281
18299
|
},
|
|
@@ -3175,5 +3175,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3175
3175
|
landed since 1.21.167 with no explicit bump. run 39990468332. (task 1002620)
|
|
3176
3176
|
1.21.169 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3177
3177
|
landed since 1.21.168 with no explicit bump. run 39992064615. (task 1002620)
|
|
3178
|
+
1.21.170 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3179
|
+
landed since 1.21.169 with no explicit bump. run 39994339257. (task 1002620)
|
|
3178
3180
|
---------------------------------------------------------------------------
|
|
3179
3181
|
```
|
package/docs/modules-contract.md
CHANGED
|
@@ -394,6 +394,7 @@ Run `bongos upgrade` to confirm coreVersion compatibility, then `node scripts/gd
|
|
|
394
394
|
- Gate: Metic+ (`module.submit`) for now. This is not `bongos module submit`, which proposes a module *into* core.
|
|
395
395
|
- **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.
|
|
396
396
|
- **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.
|
|
397
|
+
- **Tester feedback** (task 1003806, ADR 0344 D5). While a version is on the tester channel, what tester projects report about it becomes its Tester feedback part: the share of testers whose install worked and who hit no runtime errors, blended with ten neutral testers so a small sample cannot score high — `round(100 × (went well + 5) / (testers + 10))`. Reports from after the version left the tester channel, and the author's own, never count; under 3 testers it is not scored yet. The rule is `assessTesterFeedback` in `scripts/gds/module-assess-installs.js`.
|
|
397
398
|
- **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.
|
|
398
399
|
- **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`.
|
|
399
400
|
- **Testing new versions first** (task 1003802, [ADR 0344](adr/0344-a-tester-is-a-project-that-opts-in-once.md) D1). A project can choose to get each new version of its modules before everyone else. It is the project that opts in, never one builder for someone else's project, and never by default. `GET /modules/testing` returns `{ tester, general_only, updated_at, updated_by }` to anyone signed in. `POST /modules/testing { tester: true|false }` switches it, and `POST /modules/:key/general-only { general_only: true|false }` keeps one module on the versions everyone gets while the rest are tested. Both writes take the `module.enable` permission, the same one that turns modules on and off. Both are reversible: switching testing off puts the project back on general versions from its next update, and a module's "general only" mark is kept if testing is switched off and on again. The choice is stored in the project's own settings (`modules.tester`, `modules.general_only`); anything that cannot be read counts as not testing. `testsVersionsOf` in `src/bongos/module-testing.js` is the one answer to "does this project get the tester version of this module", which installs and updates use (task 1003805). The hall's Modules tab has the switch in its "Testing new versions" group, and the general-only control in each module's details.
|
package/docs/page-readings.json
CHANGED
|
@@ -3978,7 +3978,7 @@
|
|
|
3978
3978
|
"surface": "builders",
|
|
3979
3979
|
"title": "Studio",
|
|
3980
3980
|
"states_read": ["home","out","flow","flow-pick","status","words-offer","words-edit","reviews","full","full-back","approvals","approvals-before","approvals-phone","approvals-note","approved","sent-back"],
|
|
3981
|
-
"files_hash": "
|
|
3981
|
+
"files_hash": "a072c3fdf156d639",
|
|
3982
3982
|
"reading_hash": "55e9090fdd96a979",
|
|
3983
3983
|
"counts": {"lines":30,"placed":16,"shared":6,"unplaced":8},
|
|
3984
3984
|
"omitted": {"data":4},
|
|
@@ -800,8 +800,40 @@ function approvalFor({ page, pageId, rounds, round, builderId, isArtist }) {
|
|
|
800
800
|
};
|
|
801
801
|
}
|
|
802
802
|
|
|
803
|
+
// The submitted-but-unapplied rounds, oldest first, in the in-flight list's
|
|
804
|
+
// own words (task 1004662's IN_FLIGHT.submitted), with how long each waited.
|
|
805
|
+
function notAppliedFor({ byPage, invById, now }) {
|
|
806
|
+
const nowMs = now instanceof Date ? now.getTime() : Number(now);
|
|
807
|
+
const rows = [];
|
|
808
|
+
for (const [pageId, rounds] of byPage) {
|
|
809
|
+
const open = rounds.filter((r) => !TERMINAL.has(r.status));
|
|
810
|
+
const round = open.length ? open[open.length - 1] : null;
|
|
811
|
+
if (!round || roundState(round) !== 'submitted') continue;
|
|
812
|
+
const page = invById.get(pageId) || null;
|
|
813
|
+
const b = pages.parseBatchBlock(round.description);
|
|
814
|
+
const since = iso((b.ok && b.batch.submitted_at) || round.updated_at || round.created_at);
|
|
815
|
+
const ms = since ? nowMs - Date.parse(since) : null;
|
|
816
|
+
const surface = page ? page.surface : String(pageId).split(':')[0];
|
|
817
|
+
rows.push({
|
|
818
|
+
page_id: pageId,
|
|
819
|
+
surface,
|
|
820
|
+
surface_word: surfaceWord(surface),
|
|
821
|
+
title: page ? (page.title || page.id) : pageId,
|
|
822
|
+
path: page ? page.path || null : null,
|
|
823
|
+
task_id: String(round.id),
|
|
824
|
+
state: 'submitted',
|
|
825
|
+
state_word: IN_FLIGHT.submitted.word,
|
|
826
|
+
since,
|
|
827
|
+
waited_days: ms == null ? null : Math.max(0, Math.floor(ms / DAY_MS)),
|
|
828
|
+
line_count: b.ok ? b.batch.lines.length : 0,
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
rows.sort((a, b) => String(a.since || '').localeCompare(String(b.since || '')) || Number(a.task_id) - Number(b.task_id));
|
|
832
|
+
return { not_applied: rows, not_applied_count: rows.length };
|
|
833
|
+
}
|
|
834
|
+
|
|
803
835
|
// GET /copy-desk/approvals — the queue, "1 of 2".
|
|
804
|
-
function composeApprovals({ inventory, tasks, builderId, crafts }) {
|
|
836
|
+
function composeApprovals({ inventory, tasks, builderId, crafts, now = Date.now() }) {
|
|
805
837
|
const byPage = indexRounds(tasks);
|
|
806
838
|
const invById = new Map(((inventory && inventory.pages) || []).filter((p) => p && p.id).map((p) => [p.id, p]));
|
|
807
839
|
const isArtist = (crafts || []).includes('artist');
|
|
@@ -825,6 +857,11 @@ function composeApprovals({ inventory, tasks, builderId, crafts }) {
|
|
|
825
857
|
is_artist: isArtist,
|
|
826
858
|
count: queue.length,
|
|
827
859
|
queue,
|
|
860
|
+
// task 1004765: rounds SUBMITTED but not yet applied. They sat unseen for
|
|
861
|
+
// weeks because this queue listed only applied rounds; they are listed
|
|
862
|
+
// apart, never in `queue`, so nothing can approve or send back a page that
|
|
863
|
+
// has not been applied, and `count` (the studio door) stays reviewable only.
|
|
864
|
+
...notAppliedFor({ byPage, invById, now }),
|
|
828
865
|
};
|
|
829
866
|
}
|
|
830
867
|
|
|
@@ -130,6 +130,20 @@
|
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
// task 1004765: a submitted round nobody has applied yet. Listed under the
|
|
134
|
+
// reviewable pages, marked so, with how long it has waited; it carries no
|
|
135
|
+
// verb, since there is no applied page to judge.
|
|
136
|
+
function notAppliedRow(r) {
|
|
137
|
+
const d = r && r.waited_days;
|
|
138
|
+
const waited = d == null ? '' : (d === 0 ? 'today' : `${d} day${d === 1 ? '' : 's'}`);
|
|
139
|
+
return {
|
|
140
|
+
task_id: String(r.task_id),
|
|
141
|
+
title: displayTitle(r),
|
|
142
|
+
mark: 'not applied yet',
|
|
143
|
+
line: `${r.state_word}${waited ? ` · waiting ${waited}` : ''}. The words are waiting to be applied, so there is nothing to approve yet.`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
133
147
|
function noteProblem(note) {
|
|
134
148
|
const n = String(note == null ? '' : note).replace(/\s+/g, ' ').trim();
|
|
135
149
|
if (n.length < MIN_NOTE) return `Say what needs changing in at least ${MIN_NOTE} characters.`;
|
|
@@ -157,7 +171,7 @@
|
|
|
157
171
|
const api = {
|
|
158
172
|
PHASES, DEVICES, MODES, MIN_NOTE, MAX_NOTE,
|
|
159
173
|
slotName, renderIndex, pickRender, hasAnyRender, displayTitle,
|
|
160
|
-
eyebrow, subLine, doorDetail, refusedWords, lineRow, noteProblem, approvedLine, sentBackLine,
|
|
174
|
+
eyebrow, subLine, doorDetail, refusedWords, lineRow, notAppliedRow, noteProblem, approvedLine, sentBackLine,
|
|
161
175
|
};
|
|
162
176
|
if (typeof window !== 'undefined') window.OTBApprovalQueueLib = api;
|
|
163
177
|
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
@@ -230,6 +230,15 @@ html.aq-open { overflow: hidden; }
|
|
|
230
230
|
.aq-line__pinned { font-size: 13px; line-height: 1.45; color: var(--aq-link); overflow-wrap: anywhere; }
|
|
231
231
|
.aq-lines__none { margin: 0; font-size: 14px; color: var(--aq-ink-faint); }
|
|
232
232
|
|
|
233
|
+
/* task 1004765: submitted rounds not applied yet, below the reviewable page. */
|
|
234
|
+
.aq-pending { padding: 14px 20px 18px; border-top: 1px solid var(--aq-rule); }
|
|
235
|
+
.aq-pending__h { margin: 0 0 8px; font-size: 13px; font-weight: 600; color: var(--aq-ink-faint); }
|
|
236
|
+
.aq-pending__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
|
|
237
|
+
.aq-pending__row { display: flex; flex-wrap: wrap; gap: 4px 8px; font-size: 14px; }
|
|
238
|
+
.aq-pending__title { font-weight: 600; }
|
|
239
|
+
.aq-pending__mark { font-family: var(--aq-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--aq-link); }
|
|
240
|
+
.aq-pending__line { flex-basis: 100%; font-size: 13px; color: var(--aq-ink-faint); overflow-wrap: anywhere; }
|
|
241
|
+
|
|
233
242
|
/* The decision row. */
|
|
234
243
|
.aq-foot {
|
|
235
244
|
display: flex;
|
|
@@ -105,6 +105,7 @@
|
|
|
105
105
|
state.loadError = null;
|
|
106
106
|
state.loaded = true;
|
|
107
107
|
state.queue = (res.data && res.data.queue) || [];
|
|
108
|
+
state.notApplied = (res.data && res.data.not_applied) || [];
|
|
108
109
|
if (state.idx >= state.queue.length) state.idx = 0;
|
|
109
110
|
tellCount();
|
|
110
111
|
}
|
|
@@ -183,6 +184,19 @@
|
|
|
183
184
|
return `<div class="aq-foot">${approve}${note}<span class="aq-hint">highlighted: the words you changed</span></div>${err}`;
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
// task 1004765: submitted rounds nobody has applied, below the reviewable
|
|
188
|
+
// pages. The server lists them apart; the panel only words them, and gives
|
|
189
|
+
// them no approve or send-back verb.
|
|
190
|
+
function notAppliedHtml() {
|
|
191
|
+
const rows = (state.notApplied || []).map((r) => L.notAppliedRow(r));
|
|
192
|
+
if (!rows.length) return '';
|
|
193
|
+
return `<section class="aq-pending" aria-label="Submitted, not applied yet">
|
|
194
|
+
<h3 class="aq-pending__h">Submitted, not applied yet (${rows.length})</h3>
|
|
195
|
+
<ul class="aq-pending__list">${rows.map((r) => `
|
|
196
|
+
<li class="aq-pending__row" data-task="${esc(r.task_id)}"><span class="aq-pending__title">${esc(r.title)}</span> <span class="aq-pending__mark">${esc(r.mark)}</span><span class="aq-pending__line">${esc(r.line)}</span></li>`).join('')}</ul>
|
|
197
|
+
</section>`;
|
|
198
|
+
}
|
|
199
|
+
|
|
186
200
|
function paint() {
|
|
187
201
|
if (!state.open) return;
|
|
188
202
|
const keepNote = document.getElementById('aq-note');
|
|
@@ -193,7 +207,8 @@
|
|
|
193
207
|
const words = state.loadError || (state.loaded ? 'Nothing is waiting for you. A page appears here once /tweak has applied it and its grade has passed.' : 'Opening the Approval queue…');
|
|
194
208
|
dialog.innerHTML = `
|
|
195
209
|
<div class="aq-head"><span class="aq-eyebrow">approval queue</span><h2 class="aq-title" id="aq-title">${state.loaded ? 'Nothing waiting' : 'Approval queue'}</h2><span class="aq-head__gap"></span>${close}</div>
|
|
196
|
-
<div class="aq-body aq-body--empty"><div class="aq-frame"><p class="aq-frame__note">${esc(words)}</p></div></div
|
|
210
|
+
<div class="aq-body aq-body--empty"><div class="aq-frame"><p class="aq-frame__note">${esc(words)}</p></div></div>
|
|
211
|
+
${notAppliedHtml()}`;
|
|
197
212
|
return;
|
|
198
213
|
}
|
|
199
214
|
loadRenders(item);
|
|
@@ -212,7 +227,8 @@
|
|
|
212
227
|
${linesHtml(item)}
|
|
213
228
|
</aside>
|
|
214
229
|
</div>
|
|
215
|
-
${footHtml(item)}
|
|
230
|
+
${footHtml(item)}
|
|
231
|
+
${notAppliedHtml()}`;
|
|
216
232
|
const note = document.getElementById('aq-note');
|
|
217
233
|
if (note) { note.value = noteValue; }
|
|
218
234
|
}
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.170",
|
|
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.170",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.170",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -10084,5 +10084,15 @@
|
|
|
10084
10084
|
"id": "1003805",
|
|
10085
10085
|
"text": "Projects that test new module versions now actually get them first, and everyone else only gets versions that have passed testing. Turning testing off keeps what you have and moves you to the tested versions from your next upd"
|
|
10086
10086
|
}
|
|
10087
|
+
],
|
|
10088
|
+
"1.21.170": [
|
|
10089
|
+
{
|
|
10090
|
+
"id": "1004765",
|
|
10091
|
+
"text": "The Approval queue now also shows pages an artist submitted that nobody has applied yet, under the pages ready to review. Each is marked not applied yet and says how many days it has waited. They have no approve or send back b"
|
|
10092
|
+
},
|
|
10093
|
+
{
|
|
10094
|
+
"id": "1003806",
|
|
10095
|
+
"text": "When a new module version is being tested, how it went for the testing projects now counts toward its quality score — and a handful of friendly testers can no longer outscore hundreds of real ones."
|
|
10096
|
+
}
|
|
10087
10097
|
]
|
|
10088
10098
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// scripts/gds/module-assess-installs.js — the Install
|
|
3
|
-
// module's assessment, worked out from what real installs report (task 1003793,
|
|
2
|
+
// scripts/gds/module-assess-installs.js — the Install, Reliability and Tester
|
|
3
|
+
// feedback parts of a module's assessment, worked out from what real installs report (task 1003793,
|
|
4
4
|
// ADR 0343 D1; reports from core_273, signal rows into core_265).
|
|
5
5
|
//
|
|
6
6
|
// WHY these two parts. Tests and Docs are things an author writes; Install and
|
|
@@ -17,6 +17,21 @@
|
|
|
17
17
|
// reported), over the last ERROR_DAY_WINDOW days.
|
|
18
18
|
// No reports → no_data (ADR 0343 D3: left out of the average, never a zero).
|
|
19
19
|
//
|
|
20
|
+
// Tester feedback (task 1003806, ADR 0344 D5) is the same reports, narrowed to
|
|
21
|
+
// the version's TESTER WINDOW: from the first time it went on the tester channel
|
|
22
|
+
// to the first time it left it (promoted or withdrawn), or now while it is still
|
|
23
|
+
// there. Only tester projects are served a tester version (ADR 0344 D1), so a
|
|
24
|
+
// report inside that window is a tester's. A tester it went well for is one whose
|
|
25
|
+
// install did not fail and who reported no runtime errors in the window.
|
|
26
|
+
// Sample size has to weigh — two friendly testers must not outrank two hundred
|
|
27
|
+
// real ones when a buyer sorts by this part — so the count is blended with
|
|
28
|
+
// PRIOR_TESTERS imaginary testers it went half well for:
|
|
29
|
+
// Tester feedback = round(100 × (went well + PRIOR_TESTERS/2) / (testers + PRIOR_TESTERS)).
|
|
30
|
+
// 3 of 3 → 62, 10 of 10 → 75, 200 of 200 → 98. MIN_HOLDERS applies here too.
|
|
31
|
+
// An install report is one row per holder, re-stamped on each attempt, so a
|
|
32
|
+
// tester who reinstalls after promotion leaves the window; error counts are per
|
|
33
|
+
// day, so they are placed by their day, not by when they were sent.
|
|
34
|
+
//
|
|
20
35
|
// What the reports may contain is fixed by the owner's privacy posture and
|
|
21
36
|
// enforced at intake (src/bongos/module-usage-reports.js): counts and short codes
|
|
22
37
|
// only, and nothing from a holder who switched reporting off.
|
|
@@ -29,6 +44,7 @@
|
|
|
29
44
|
const { ERROR_DAY_WINDOW } = require('../../src/bongos/module-usage-reports');
|
|
30
45
|
|
|
31
46
|
const MIN_HOLDERS = 3;
|
|
47
|
+
const PRIOR_TESTERS = 10;
|
|
32
48
|
|
|
33
49
|
// The not-yet-scored outcomes, shared by both parts. Pure.
|
|
34
50
|
function notYet(holders, what) {
|
|
@@ -67,8 +83,41 @@ function assessReliability(days = []) {
|
|
|
67
83
|
};
|
|
68
84
|
}
|
|
69
85
|
|
|
86
|
+
// Tester feedback from what testers reported in the tester window: `installs` is
|
|
87
|
+
// { holder_kind, holder_ref, ok, error_code } per holder, `days` the holder-days
|
|
88
|
+
// as Reliability takes them. One verdict per tester, blended with the prior. Pure.
|
|
89
|
+
function assessTesterFeedback({ installs = [], days = [] } = {}) {
|
|
90
|
+
const testers = new Map();
|
|
91
|
+
const of = (r) => {
|
|
92
|
+
const k = `${r.holder_kind}:${r.holder_ref}`;
|
|
93
|
+
if (!testers.has(k)) testers.set(k, { failed: false, errors: 0 });
|
|
94
|
+
return testers.get(k);
|
|
95
|
+
};
|
|
96
|
+
for (const r of installs) of(r).failed = !r.ok;
|
|
97
|
+
for (const d of days) of(d).errors += Number(d.error_count);
|
|
98
|
+
const all = [...testers.values()];
|
|
99
|
+
const n = all.length;
|
|
100
|
+
const wentWell = all.filter((t) => !t.failed && t.errors === 0).length;
|
|
101
|
+
const detail = {
|
|
102
|
+
testers: n, went_well: wentWell,
|
|
103
|
+
install_failed: all.filter((t) => t.failed).length,
|
|
104
|
+
with_errors: all.filter((t) => t.errors > 0).length,
|
|
105
|
+
errors: all.reduce((sum, t) => sum + t.errors, 0),
|
|
106
|
+
prior_testers: PRIOR_TESTERS,
|
|
107
|
+
};
|
|
108
|
+
if (n < MIN_HOLDERS) return { ...notYet(n, 'from the tester channel'), detail };
|
|
109
|
+
return {
|
|
110
|
+
outcome: 'scored',
|
|
111
|
+
score: Math.round((100 * (wentWell + PRIOR_TESTERS / 2)) / (n + PRIOR_TESTERS)),
|
|
112
|
+
sample_size: n,
|
|
113
|
+
reason: `it went well for ${wentWell} of ${n} testers (weighed against ${PRIOR_TESTERS} neutral ones, so a small sample cannot score high)`,
|
|
114
|
+
detail,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
70
118
|
// The author's own reports never count. Shared by both reads.
|
|
71
|
-
const
|
|
119
|
+
const notAuthor = (a) => `NOT (${a}.holder_kind = 'builder' AND ${a}.holder_ref = m.author_id::text)`;
|
|
120
|
+
const NOT_AUTHOR = notAuthor('r');
|
|
72
121
|
|
|
73
122
|
async function readReports(versionId, pool) {
|
|
74
123
|
const { rows: latest } = await pool.query(
|
|
@@ -87,26 +136,62 @@ async function readReports(versionId, pool) {
|
|
|
87
136
|
return { latest, days };
|
|
88
137
|
}
|
|
89
138
|
|
|
139
|
+
// The version's tester window, as SQL over its channel history (core_276): `t.start`
|
|
140
|
+
// is when it first went on the tester channel, `t.stop` when it next left it (null
|
|
141
|
+
// while it is still there). A version that was never a tester version has no row.
|
|
142
|
+
const TESTER_WINDOW = `(SELECT s.start,
|
|
143
|
+
(SELECT min(c2.set_at) FROM store_module_version_channels c2
|
|
144
|
+
WHERE c2.version_id = $1 AND c2.channel <> 'tester' AND c2.set_at > s.start) AS stop
|
|
145
|
+
FROM (SELECT min(c1.set_at) AS start FROM store_module_version_channels c1
|
|
146
|
+
WHERE c1.version_id = $1 AND c1.channel = 'tester') s
|
|
147
|
+
WHERE s.start IS NOT NULL) t`;
|
|
148
|
+
|
|
149
|
+
async function readTesterReports(versionId, pool) {
|
|
150
|
+
const { rows: installs } = await pool.query(
|
|
151
|
+
`SELECT ir.holder_kind, ir.holder_ref, ir.ok, ir.error_code
|
|
152
|
+
FROM module_install_reports ir
|
|
153
|
+
JOIN store_module_versions v ON v.id = ir.version_id
|
|
154
|
+
JOIN store_modules m ON m.module_key = v.module_key
|
|
155
|
+
CROSS JOIN ${TESTER_WINDOW}
|
|
156
|
+
WHERE ir.version_id = $1 AND ir.reported_at >= t.start AND (t.stop IS NULL OR ir.reported_at < t.stop)
|
|
157
|
+
AND ${notAuthor('ir')}`, [versionId]);
|
|
158
|
+
const { rows: days } = await pool.query(
|
|
159
|
+
`SELECT ec.holder_kind, ec.holder_ref, ec.error_count
|
|
160
|
+
FROM module_runtime_error_counts ec
|
|
161
|
+
JOIN store_module_versions v ON v.id = ec.version_id
|
|
162
|
+
JOIN store_modules m ON m.module_key = v.module_key
|
|
163
|
+
CROSS JOIN ${TESTER_WINDOW}
|
|
164
|
+
WHERE ec.version_id = $1 AND ec.day >= t.start::date AND (t.stop IS NULL OR ec.day <= t.stop::date)
|
|
165
|
+
AND ${notAuthor('ec')}`, [versionId]);
|
|
166
|
+
return { installs, days };
|
|
167
|
+
}
|
|
168
|
+
|
|
90
169
|
const sameResult = (cur, s) => !!cur && cur.outcome === s.outcome
|
|
91
170
|
&& (cur.score == null ? null : Number(cur.score)) === s.score
|
|
92
171
|
&& Number(cur.sample_size) === s.sample_size;
|
|
93
172
|
|
|
94
|
-
|
|
173
|
+
const PARTS = ['install', 'reliability', 'tester_feedback'];
|
|
174
|
+
|
|
175
|
+
// Re-score Install, Reliability and Tester feedback for one version from its reports, append a
|
|
95
176
|
// signal row for each part whose result moved, then re-compose the overall score
|
|
96
177
|
// (module-assess-score.js). Resolves { ok, written: [part…], score }.
|
|
97
178
|
async function recordInstallSignals(versionId, { db } = {}) {
|
|
98
179
|
const pool = db || require('../../src/bongos/pool').pool;
|
|
99
180
|
const { latest, days } = await readReports(versionId, pool);
|
|
100
|
-
const next = {
|
|
181
|
+
const next = {
|
|
182
|
+
install: assessInstall(latest),
|
|
183
|
+
reliability: assessReliability(days),
|
|
184
|
+
tester_feedback: assessTesterFeedback(await readTesterReports(versionId, pool)),
|
|
185
|
+
};
|
|
101
186
|
|
|
102
187
|
const { rows: current } = await pool.query(
|
|
103
188
|
`SELECT DISTINCT ON (part) part, outcome, score, sample_size
|
|
104
|
-
FROM module_assessment_signals WHERE version_id = $1 AND part IN ('install', 'reliability')
|
|
189
|
+
FROM module_assessment_signals WHERE version_id = $1 AND part IN ('install', 'reliability', 'tester_feedback')
|
|
105
190
|
ORDER BY part, measured_at DESC, id DESC`, [versionId]);
|
|
106
191
|
const byPart = Object.fromEntries(current.map((r) => [r.part, r]));
|
|
107
192
|
|
|
108
193
|
const written = [];
|
|
109
|
-
for (const part of
|
|
194
|
+
for (const part of PARTS) {
|
|
110
195
|
const s = next[part];
|
|
111
196
|
if (sameResult(byPart[part], s)) continue;
|
|
112
197
|
await pool.query(
|
|
@@ -128,7 +213,7 @@ async function main(argv) {
|
|
|
128
213
|
return 2;
|
|
129
214
|
}
|
|
130
215
|
const res = await recordInstallSignals(id);
|
|
131
|
-
for (const part of
|
|
216
|
+
for (const part of PARTS) {
|
|
132
217
|
const s = res.signals[part];
|
|
133
218
|
console.log(`${part}: ${s.outcome}${s.score != null ? ` ${s.score}/100` : ''} — ${s.reason}${res.written.includes(part) ? '' : ' (unchanged)'}`);
|
|
134
219
|
}
|
|
@@ -140,4 +225,4 @@ if (require.main === module) {
|
|
|
140
225
|
main(process.argv.slice(2)).then((code) => { process.exitCode = code; }, (e) => { console.error(e.stack || e.message); process.exitCode = 1; });
|
|
141
226
|
}
|
|
142
227
|
|
|
143
|
-
module.exports = { MIN_HOLDERS, assessInstall, assessReliability, recordInstallSignals };
|
|
228
|
+
module.exports = { MIN_HOLDERS, PRIOR_TESTERS, assessInstall, assessReliability, assessTesterFeedback, recordInstallSignals };
|
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.170'; // 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');
|
|
@@ -311,6 +311,41 @@ await test('empty, closed with Escape, and focus goes back where it was', async
|
|
|
311
311
|
assert.deepEqual(b.counts, [0]);
|
|
312
312
|
});
|
|
313
313
|
|
|
314
|
+
// task 1004765: a round SUBMITTED but never applied sat unseen for weeks. The
|
|
315
|
+
// server lists it apart (not_applied), the panel shows it below the reviewable
|
|
316
|
+
// page, marked, with how long it has waited, and with no verb; the door counts
|
|
317
|
+
// only what can be reviewed.
|
|
318
|
+
await test('submitted but not applied: listed apart, marked, how long, no verbs, not in the door count', async () => {
|
|
319
|
+
const NOW = Date.parse('2026-10-20T12:00:00Z');
|
|
320
|
+
const submitted = {
|
|
321
|
+
id: 60, status: 'ready', holder_id: null, artist_id: ME, artist_login: 'me', updated_at: '2026-09-29T08:00:00Z',
|
|
322
|
+
source_ref: pages.roundSourceRef('landing:projects', 2),
|
|
323
|
+
description: ['Page tweak round 2.', pages.composeBatchBlock({ page_id: 'landing:projects', reading_hash: 'h', submitted_at: '2026-09-29T08:00:00Z', lines: [{ key: 'L0002', section: 'list', before: 'Every project', after: 'All of them', placement: 'placed', file: 'x.html', line: 1, string_id: 'a' }] })].join('\n\n'),
|
|
324
|
+
};
|
|
325
|
+
const tasks = [...TASKS.filter((t) => t.id !== 42), submitted];
|
|
326
|
+
const data = pageStatus.composeApprovals({ inventory: INVENTORY, tasks, builderId: ME, crafts: ['artist'], now: NOW });
|
|
327
|
+
assert.equal(data.count, 1, 'the door counts reviewable rounds only');
|
|
328
|
+
assert.deepEqual(data.queue.map((q) => q.task_id), ['41']);
|
|
329
|
+
assert.deepEqual(data.not_applied.map((r) => [r.page_id, r.task_id, r.state_word, r.waited_days, r.line_count]),
|
|
330
|
+
[['landing:projects', '60', 'Submitted, waiting to be applied', 21, 1]]);
|
|
331
|
+
|
|
332
|
+
const b = bootPanel({ answers: { [APPROVALS]: { ok: true, ...data }, [vis(41)]: slotsFor(41) } });
|
|
333
|
+
await b.panel.open(); await settle(); await settle();
|
|
334
|
+
assert.equal(text(b.q('.aq-pending__h')), 'Submitted, not applied yet (1)');
|
|
335
|
+
const row = b.q('.aq-pending__row');
|
|
336
|
+
assert.match(text(row), /Projects/);
|
|
337
|
+
assert.match(text(row), /not applied yet/);
|
|
338
|
+
assert.match(text(row), /Submitted, waiting to be applied · waiting 21 days\. The words are waiting to be applied/);
|
|
339
|
+
assert.equal(row.querySelectorAll('button').length, 0, 'no approve or send-back on a page nobody applied');
|
|
340
|
+
assert.deepEqual(b.counts, [1]);
|
|
341
|
+
|
|
342
|
+
const empty = bootPanel({ answers: { [APPROVALS]: { ok: true, ...pageStatus.composeApprovals({ inventory: INVENTORY, tasks: [submitted], builderId: ME, crafts: ['artist'], now: NOW }) } } });
|
|
343
|
+
await empty.panel.open(); await settle();
|
|
344
|
+
assert.equal(text(empty.q('.aq-title')), 'Nothing waiting', 'nothing to review');
|
|
345
|
+
assert.ok(empty.q('.aq-pending__row'), 'but the submitted page is still shown');
|
|
346
|
+
assert.deepEqual(empty.counts, [0]);
|
|
347
|
+
});
|
|
348
|
+
|
|
314
349
|
// ── the writes: only the host's, and the host's are the studio's allowlist ──
|
|
315
350
|
|
|
316
351
|
await test('the panel writes only through the writes it is handed; the studio hands it its own WRITE_ROUTES', () => {
|
|
@@ -20,7 +20,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
20
20
|
|
|
21
21
|
const require = createRequire(import.meta.url);
|
|
22
22
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
23
|
-
const { MIN_HOLDERS, assessInstall, assessReliability, recordInstallSignals } = require('../scripts/gds/module-assess-installs.js');
|
|
23
|
+
const { MIN_HOLDERS, PRIOR_TESTERS, assessInstall, assessReliability, assessTesterFeedback, recordInstallSignals } = require('../scripts/gds/module-assess-installs.js');
|
|
24
24
|
const reports = require('../src/bongos/module-usage-reports.js');
|
|
25
25
|
const { ERROR_CODE_RE } = require('../src/bongos/module-usage-reports.js');
|
|
26
26
|
const { cmdInstall } = require('../scripts/gds/module.js');
|
|
@@ -146,11 +146,13 @@ test('the switch: off writes an opt-out row, on deletes it', async () => {
|
|
|
146
146
|
|
|
147
147
|
// ---- the re-score ----
|
|
148
148
|
|
|
149
|
-
function scoringDb({ latest, days, current = [] }) {
|
|
149
|
+
function scoringDb({ latest, days, current = [], testerInstalls = [], testerDays = [] }) {
|
|
150
150
|
return fakeDb([
|
|
151
|
-
[/FROM module_install_reports r/, () => ({ rows: latest })],
|
|
152
|
-
[/FROM module_runtime_error_counts r/, () => ({ rows: days })],
|
|
153
|
-
[/
|
|
151
|
+
[/FROM module_install_reports r\s/, () => ({ rows: latest })],
|
|
152
|
+
[/FROM module_runtime_error_counts r\s/, () => ({ rows: days })],
|
|
153
|
+
[/FROM module_install_reports ir/, () => ({ rows: testerInstalls })],
|
|
154
|
+
[/FROM module_runtime_error_counts ec/, () => ({ rows: testerDays })],
|
|
155
|
+
[/part IN \('install', 'reliability', 'tester_feedback'\)/, () => ({ rows: current })],
|
|
154
156
|
[/INSERT INTO module_assessment_signals/, () => ({ rows: [] })],
|
|
155
157
|
[/SELECT DISTINCT ON \(part\) id, part, outcome, score, sample_size/, () => ({ rows: [] })],
|
|
156
158
|
[/FROM module_assessment_scores/, () => ({ rows: [] })],
|
|
@@ -173,7 +175,10 @@ test('re-score: writes a signal row only for a part whose result moved, then re-
|
|
|
173
175
|
const latest = [{ ok: true }, { ok: true }, { ok: false, error_code: 'place_failed' }];
|
|
174
176
|
const db = scoringDb({
|
|
175
177
|
latest, days: [],
|
|
176
|
-
current: [
|
|
178
|
+
current: [
|
|
179
|
+
{ part: 'reliability', outcome: 'no_data', score: null, sample_size: 0 },
|
|
180
|
+
{ part: 'tester_feedback', outcome: 'no_data', score: null, sample_size: 0 },
|
|
181
|
+
],
|
|
177
182
|
});
|
|
178
183
|
const res = await recordInstallSignals(5, { db });
|
|
179
184
|
assert.deepEqual(res.written, ['install'], 'reliability is unchanged, so no new row');
|
|
@@ -184,12 +189,73 @@ test('re-score: writes a signal row only for a part whose result moved, then re-
|
|
|
184
189
|
const again = scoringDb({ latest, days: [], current: [
|
|
185
190
|
{ part: 'install', outcome: 'scored', score: 67, sample_size: 3 },
|
|
186
191
|
{ part: 'reliability', outcome: 'no_data', score: null, sample_size: 0 },
|
|
192
|
+
{ part: 'tester_feedback', outcome: 'no_data', score: null, sample_size: 0 },
|
|
187
193
|
] });
|
|
188
194
|
const res2 = await recordInstallSignals(5, { db: again });
|
|
189
195
|
assert.deepEqual(res2.written, []);
|
|
190
196
|
assert.ok(!again.calls.some((c) => /INSERT/.test(c.sql)), 'nothing moved, nothing written');
|
|
191
197
|
});
|
|
192
198
|
|
|
199
|
+
// ---- tester feedback (task 1003806, ADR 0344 D5) ----
|
|
200
|
+
|
|
201
|
+
const testers = (n, { failed = 0, erroring = 0 } = {}) => {
|
|
202
|
+
const installs = Array.from({ length: n }, (_, i) => ({ holder_kind: 'instance', holder_ref: `p${i}`, ok: i >= failed, error_code: i < failed ? 'place_failed' : null }));
|
|
203
|
+
const days = Array.from({ length: erroring }, (_, i) => ({ holder_kind: 'instance', holder_ref: `p${n - 1 - i}`, error_count: 2 }));
|
|
204
|
+
return { installs, days };
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
test('Tester feedback: no tester reports is no_data, and fewer than MIN_HOLDERS is pending', () => {
|
|
208
|
+
assert.equal(assessTesterFeedback({}).outcome, 'no_data');
|
|
209
|
+
const s = assessTesterFeedback(testers(MIN_HOLDERS - 1));
|
|
210
|
+
assert.equal(s.outcome, 'pending');
|
|
211
|
+
assert.equal(s.score, null);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('Tester feedback: a tester it went well for installed cleanly AND hit no runtime errors', () => {
|
|
215
|
+
const s = assessTesterFeedback(testers(20, { failed: 2, erroring: 3 }));
|
|
216
|
+
assert.equal(s.detail.went_well, 15);
|
|
217
|
+
assert.equal(s.detail.install_failed, 2);
|
|
218
|
+
assert.equal(s.detail.with_errors, 3);
|
|
219
|
+
assert.equal(s.detail.errors, 6);
|
|
220
|
+
assert.equal(s.score, Math.round((100 * (15 + PRIOR_TESTERS / 2)) / (20 + PRIOR_TESTERS)));
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('Tester feedback: a tester who only sent error counts still counts once', () => {
|
|
224
|
+
const s = assessTesterFeedback({ installs: [], days: [
|
|
225
|
+
{ holder_kind: 'instance', holder_ref: 'a', error_count: 0 },
|
|
226
|
+
{ holder_kind: 'instance', holder_ref: 'a', error_count: 0 },
|
|
227
|
+
{ holder_kind: 'instance', holder_ref: 'b', error_count: 0 },
|
|
228
|
+
{ holder_kind: 'builder', holder_ref: 'a', error_count: 1 },
|
|
229
|
+
] });
|
|
230
|
+
assert.equal(s.sample_size, 3);
|
|
231
|
+
assert.equal(s.detail.went_well, 2);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test('Tester feedback: sample size weighs — a few friendly testers cannot outrank many real ones', () => {
|
|
235
|
+
const few = assessTesterFeedback(testers(3)).score;
|
|
236
|
+
const many = assessTesterFeedback(testers(200, { failed: 10 })).score;
|
|
237
|
+
assert.ok(many > few, `200 testers at 95% (${many}) must beat 3 at 100% (${few})`);
|
|
238
|
+
assert.ok(assessTesterFeedback(testers(200)).score >= 95, 'a large clean sample still scores high');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('re-score: tester reports are read only inside the tester window, author excluded', async () => {
|
|
242
|
+
const t4 = testers(4);
|
|
243
|
+
const db = scoringDb({ latest: [], days: [], testerInstalls: t4.installs, testerDays: t4.days });
|
|
244
|
+
const res = await recordInstallSignals(5, { db });
|
|
245
|
+
assert.ok(res.written.includes('tester_feedback'));
|
|
246
|
+
assert.equal(res.signals.tester_feedback.score, Math.round((100 * (4 + PRIOR_TESTERS / 2)) / (4 + PRIOR_TESTERS)));
|
|
247
|
+
for (const [table, a, when] of [['module_install_reports', 'ir', /ir\.reported_at >= t\.start AND \(t\.stop IS NULL OR ir\.reported_at < t\.stop\)/],
|
|
248
|
+
['module_runtime_error_counts', 'ec', /ec\.day >= t\.start::date AND \(t\.stop IS NULL OR ec\.day <= t\.stop::date\)/]]) {
|
|
249
|
+
const sql = db.calls.find((c) => c.sql.includes(`FROM ${table} ${a}`)).sql;
|
|
250
|
+
assert.match(sql, /c1\.channel = 'tester'/, `${table}: the window opens on the tester channel`);
|
|
251
|
+
assert.match(sql, /c2\.channel <> 'tester' AND c2\.set_at > s\.start/, `${table}: and closes when it leaves it`);
|
|
252
|
+
assert.match(sql, when, `${table}: only reports inside the window`);
|
|
253
|
+
assert.ok(sql.includes(`NOT (${a}.holder_kind = 'builder' AND ${a}.holder_ref = m.author_id::text)`), `${table}: excludes the author`);
|
|
254
|
+
}
|
|
255
|
+
const insert = db.calls.find((c) => /INSERT INTO module_assessment_signals/.test(c.sql) && c.params[1] === 'tester_feedback');
|
|
256
|
+
assert.deepEqual(insert.params.slice(0, 5), [5, 'tester_feedback', 'scored', res.signals.tester_feedback.score, 4]);
|
|
257
|
+
});
|
|
258
|
+
|
|
193
259
|
// ---- the migration: there is nowhere to put anything but counts ----
|
|
194
260
|
|
|
195
261
|
test('core_273: three additive tables whose columns hold counts and codes only', () => {
|