@bongos/core 1.21.170 → 1.21.172
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 +26 -26
- package/docs/copy-inventory.md +10 -9
- package/docs/copy-registry.json +19 -10
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-readings.json +16 -16
- package/modules/copy-desk/page-status.js +34 -0
- package/modules/copy-desk/tests/copy_page_next.mjs +30 -2
- package/modules/hall-ui/public/brand-apply.js +13 -6
- package/modules/hall-ui/public/sky-data.js +5 -4
- package/modules/hall-ui/public/sky-panel.js +1 -1
- package/modules/hall-ui/public/studio-lib.js +13 -1
- package/modules/hall-ui/public/studio.js +9 -1
- package/modules/lifecycle/sky.js +8 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/src/module-api.js +1 -1
- package/tests/goal_map_page.mjs +5 -5
- package/tests/hall_sky.mjs +16 -4
- package/tests/hall_studio_home.mjs +24 -0
- package/tests/reward_mode_surfacing.mjs +16 -3
- package/tests/sky_read.mjs +13 -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.172",
|
|
6
|
+
"core_contract": "1.21.172",
|
|
7
|
+
"source_commit": "a5b5fc89c2bddcd402af46f6271b80c329759d8f",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-10-23T17:
|
|
9
|
+
"built_at": "2026-10-23T17:54:54.483Z",
|
|
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": "3f3467a2aa833a4abbd6faf317d2656fb98738368a7376cdd163964d5f34a529",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2412,12 +2412,12 @@
|
|
|
2412
2412
|
{
|
|
2413
2413
|
"path": "docs/copy-inventory.md",
|
|
2414
2414
|
"mode": "0000644",
|
|
2415
|
-
"sha256": "
|
|
2415
|
+
"sha256": "eec10b588ffe284a8e52e0f5e167b24ffee253b336454d980906d1d299f789c2"
|
|
2416
2416
|
},
|
|
2417
2417
|
{
|
|
2418
2418
|
"path": "docs/copy-registry.json",
|
|
2419
2419
|
"mode": "0000644",
|
|
2420
|
-
"sha256": "
|
|
2420
|
+
"sha256": "f537f321c123a47d6c8a7e026293bac969b79ab648462f1fbaa51929c8282584"
|
|
2421
2421
|
},
|
|
2422
2422
|
{
|
|
2423
2423
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -2927,7 +2927,7 @@
|
|
|
2927
2927
|
{
|
|
2928
2928
|
"path": "docs/module-api-changelog.md",
|
|
2929
2929
|
"mode": "0000644",
|
|
2930
|
-
"sha256": "
|
|
2930
|
+
"sha256": "af6395d2e0df7248fff770ebe525441b4af0ba73ff474328bfb69ace36f3180b"
|
|
2931
2931
|
},
|
|
2932
2932
|
{
|
|
2933
2933
|
"path": "docs/modules-contract.md",
|
|
@@ -3017,7 +3017,7 @@
|
|
|
3017
3017
|
{
|
|
3018
3018
|
"path": "docs/page-readings.json",
|
|
3019
3019
|
"mode": "0000644",
|
|
3020
|
-
"sha256": "
|
|
3020
|
+
"sha256": "4a1fa9bbf1b952ebabc5587a38f634de6fdee2678ba6b9c8ef6a6e0862dff820"
|
|
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": "59a1cf0aeee19aee9595ab59b08bae6423bd98a60743eb3edec89e0dbe799c69"
|
|
4491
4491
|
},
|
|
4492
4492
|
{
|
|
4493
4493
|
"path": "modules/copy-desk/pages.js",
|
|
@@ -4552,7 +4552,7 @@
|
|
|
4552
4552
|
{
|
|
4553
4553
|
"path": "modules/copy-desk/tests/copy_page_next.mjs",
|
|
4554
4554
|
"mode": "0000644",
|
|
4555
|
-
"sha256": "
|
|
4555
|
+
"sha256": "29faf19e0c0c99cb749a048d4fb73c33dfdd9e363ee46fa6e00465b286cfb096"
|
|
4556
4556
|
},
|
|
4557
4557
|
{
|
|
4558
4558
|
"path": "modules/copy-desk/tests/copy_page_status.mjs",
|
|
@@ -5627,7 +5627,7 @@
|
|
|
5627
5627
|
{
|
|
5628
5628
|
"path": "modules/hall-ui/public/brand-apply.js",
|
|
5629
5629
|
"mode": "0000644",
|
|
5630
|
-
"sha256": "
|
|
5630
|
+
"sha256": "311edb9f328f02ee1d30a3711a149f9e168dd7d3b7a83ab4403250653ef8a8fe"
|
|
5631
5631
|
},
|
|
5632
5632
|
{
|
|
5633
5633
|
"path": "modules/hall-ui/public/brand-holes.js",
|
|
@@ -6522,7 +6522,7 @@
|
|
|
6522
6522
|
{
|
|
6523
6523
|
"path": "modules/hall-ui/public/sky-data.js",
|
|
6524
6524
|
"mode": "0000644",
|
|
6525
|
-
"sha256": "
|
|
6525
|
+
"sha256": "a92f2804b755a0d4b3a28d4fdb9f8bc50e274cf04c4627b220f34c84a45a08ed"
|
|
6526
6526
|
},
|
|
6527
6527
|
{
|
|
6528
6528
|
"path": "modules/hall-ui/public/sky-draw.js",
|
|
@@ -6542,7 +6542,7 @@
|
|
|
6542
6542
|
{
|
|
6543
6543
|
"path": "modules/hall-ui/public/sky-panel.js",
|
|
6544
6544
|
"mode": "0000644",
|
|
6545
|
-
"sha256": "
|
|
6545
|
+
"sha256": "a34bc56a1ebe3c65282f9f2e1d36b1102eda4cbf5fc26192239281aec0bf8f81"
|
|
6546
6546
|
},
|
|
6547
6547
|
{
|
|
6548
6548
|
"path": "modules/hall-ui/public/sky-tour.js",
|
|
@@ -6627,7 +6627,7 @@
|
|
|
6627
6627
|
{
|
|
6628
6628
|
"path": "modules/hall-ui/public/studio-lib.js",
|
|
6629
6629
|
"mode": "0000644",
|
|
6630
|
-
"sha256": "
|
|
6630
|
+
"sha256": "e8ff3230c35edc9315fa850f3875cdebb0d6f776a6d51a9b6cba18c0dd0ae303"
|
|
6631
6631
|
},
|
|
6632
6632
|
{
|
|
6633
6633
|
"path": "modules/hall-ui/public/studio.css",
|
|
@@ -6642,7 +6642,7 @@
|
|
|
6642
6642
|
{
|
|
6643
6643
|
"path": "modules/hall-ui/public/studio.js",
|
|
6644
6644
|
"mode": "0000644",
|
|
6645
|
-
"sha256": "
|
|
6645
|
+
"sha256": "2b1adabd2d219c418ea335ad348668df75cc1f49673df3c5472c4107e8595691"
|
|
6646
6646
|
},
|
|
6647
6647
|
{
|
|
6648
6648
|
"path": "modules/hall-ui/public/studio.states.json",
|
|
@@ -7807,7 +7807,7 @@
|
|
|
7807
7807
|
{
|
|
7808
7808
|
"path": "modules/lifecycle/sky.js",
|
|
7809
7809
|
"mode": "0000644",
|
|
7810
|
-
"sha256": "
|
|
7810
|
+
"sha256": "98011414e3e393667766a499bc8a7ad3f145d2c42563dca9ff6f35b8c4da16a2"
|
|
7811
7811
|
},
|
|
7812
7812
|
{
|
|
7813
7813
|
"path": "modules/lifecycle/task-classifier.js",
|
|
@@ -10602,12 +10602,12 @@
|
|
|
10602
10602
|
{
|
|
10603
10603
|
"path": "package-lock.json",
|
|
10604
10604
|
"mode": "0000644",
|
|
10605
|
-
"sha256": "
|
|
10605
|
+
"sha256": "11e80d0bbd4cbe40847b4b7b3185243caca27ca1eb62a23d16af9244aedf7d5f"
|
|
10606
10606
|
},
|
|
10607
10607
|
{
|
|
10608
10608
|
"path": "package.json",
|
|
10609
10609
|
"mode": "0000644",
|
|
10610
|
-
"sha256": "
|
|
10610
|
+
"sha256": "ac557c163c39ef7b845595788a40b41a256fa82143a06632ac9a0805479a8285"
|
|
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": "a0f68aef51f5fbd8e741303c53f90c1342c0c9cc19f99d8c9de37fce33edb894"
|
|
10631
10631
|
},
|
|
10632
10632
|
{
|
|
10633
10633
|
"path": "scripts/cron/style_guide_audit.sh",
|
|
@@ -13017,7 +13017,7 @@
|
|
|
13017
13017
|
{
|
|
13018
13018
|
"path": "src/module-api.js",
|
|
13019
13019
|
"mode": "0000644",
|
|
13020
|
-
"sha256": "
|
|
13020
|
+
"sha256": "d91f9c6725d2c369f3af694a4e9b1ecada83994a2208961414c7814f0bf65b48"
|
|
13021
13021
|
},
|
|
13022
13022
|
{
|
|
13023
13023
|
"path": "src/module-loader/catalog.js",
|
|
@@ -14917,7 +14917,7 @@
|
|
|
14917
14917
|
{
|
|
14918
14918
|
"path": "tests/goal_map_page.mjs",
|
|
14919
14919
|
"mode": "0000644",
|
|
14920
|
-
"sha256": "
|
|
14920
|
+
"sha256": "aa83961201bf3ff0757330b16a799b4de407d0cc2522282f18712dbf624a84ea"
|
|
14921
14921
|
},
|
|
14922
14922
|
{
|
|
14923
14923
|
"path": "tests/goal_member_kinds.mjs",
|
|
@@ -15732,7 +15732,7 @@
|
|
|
15732
15732
|
{
|
|
15733
15733
|
"path": "tests/hall_sky.mjs",
|
|
15734
15734
|
"mode": "0000644",
|
|
15735
|
-
"sha256": "
|
|
15735
|
+
"sha256": "f798b71ce6f21026afc89bbca1e35fca06919d38f56eca1f8d8cfc75b3e9ac85"
|
|
15736
15736
|
},
|
|
15737
15737
|
{
|
|
15738
15738
|
"path": "tests/hall_states_regex_escapes.mjs",
|
|
@@ -15772,7 +15772,7 @@
|
|
|
15772
15772
|
{
|
|
15773
15773
|
"path": "tests/hall_studio_home.mjs",
|
|
15774
15774
|
"mode": "0000644",
|
|
15775
|
-
"sha256": "
|
|
15775
|
+
"sha256": "2f8bb467008b2a8007627a50e7b9432924364688c24f9a2d8ef38f8966034fef"
|
|
15776
15776
|
},
|
|
15777
15777
|
{
|
|
15778
15778
|
"path": "tests/hall_studio_world.mjs",
|
|
@@ -17887,7 +17887,7 @@
|
|
|
17887
17887
|
{
|
|
17888
17888
|
"path": "tests/reward_mode_surfacing.mjs",
|
|
17889
17889
|
"mode": "0000644",
|
|
17890
|
-
"sha256": "
|
|
17890
|
+
"sha256": "9d8e28d50abfb2b0e339f7b934a1ae230812340739bcdc13e4550f7e99d8659b"
|
|
17891
17891
|
},
|
|
17892
17892
|
{
|
|
17893
17893
|
"path": "tests/role_responsibilities.mjs",
|
|
@@ -18462,7 +18462,7 @@
|
|
|
18462
18462
|
{
|
|
18463
18463
|
"path": "tests/sky_read.mjs",
|
|
18464
18464
|
"mode": "0000644",
|
|
18465
|
-
"sha256": "
|
|
18465
|
+
"sha256": "238538864553803ed21a6330bf5a3570d1da8bd2f691af45b9eec1773a3bd8cd"
|
|
18466
18466
|
},
|
|
18467
18467
|
{
|
|
18468
18468
|
"path": "tests/smoke_dependencies_witness.mjs",
|
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
|
+
**2794 strings** across 3 surfaces · 2375 distinct pieces of copy · 2794 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 | 1974 |
|
|
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` — 1974 strings
|
|
82
82
|
|
|
83
83
|
| id | conf | text | where |
|
|
84
84
|
|---|---|---|---|
|
|
@@ -590,9 +590,9 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
590
590
|
| `320a0516a33e` | certain | Goals seeded — awaiting kickoff | `modules/hall-ui/public/roadmap.js:271` |
|
|
591
591
|
| `bba4caf837e9` | certain | Goals — {{worldName}} | `modules/hall-ui/public/goals.html:6` |
|
|
592
592
|
| `bba4caf837e9` | certain | Goals — {{worldName}} | `modules/hall-ui/public/goals.html:6` |
|
|
593
|
-
| `d0f76a295d09` | certain | Good afternoon. | `modules/hall-ui/public/studio.js:
|
|
593
|
+
| `d0f76a295d09` | certain | Good afternoon. | `modules/hall-ui/public/studio.js:968` |
|
|
594
594
|
| `80fd51db38d7` | certain | Good evening. | `modules/hall-ui/public/studio.html:101` |
|
|
595
|
-
| `36083f00396d` | certain | Good morning. | `modules/hall-ui/public/studio.js:
|
|
595
|
+
| `36083f00396d` | certain | Good morning. | `modules/hall-ui/public/studio.js:967` |
|
|
596
596
|
| `cab3de254155` | certain | Government — {{worldName}} | `modules/hall-ui/public/government.html:6` |
|
|
597
597
|
| `cab3de254155` | certain | Government — {{worldName}} | `modules/hall-ui/public/government.html:6` |
|
|
598
598
|
| `24d58d284952` | certain | Grades across all recent shipped work — a rolling aggregate. | `modules/hall-ui/public/watch.html:86` |
|
|
@@ -778,6 +778,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
778
778
|
| `9dacaf382dc8` | certain | More ways to open the page | `modules/hall-ui/public/tweak-editor.html:59` |
|
|
779
779
|
| `46fea78f33c6` | certain | Most recent first. Each load of this page is itself recorded in the audit log, and so is every transcript {…} | `modules/hall-ui/public/sessions.js:445` |
|
|
780
780
|
| `f14a91b3f625` | certain | Most recent first. Each row is one recorded work session; every id on it opens its record. | `modules/hall-ui/public/sessions.html:56` |
|
|
781
|
+
| `2d5aea02c40e` | certain | Most words to write | `modules/hall-ui/public/studio.js:737` |
|
|
781
782
|
| `e52ae74f0052` | certain | Move to goal | `modules/hall-ui/public/task.js:562` |
|
|
782
783
|
| `e77d5f3beaef` | certain | Move to {…} | `modules/hall-ui/public/deploy.js:569` |
|
|
783
784
|
| `e77d5f3beaef` | certain | Move to {…} | `modules/hall-ui/public/deploy.js:731` |
|
|
@@ -861,7 +862,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
861
862
|
| `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` |
|
|
862
863
|
| `91c409a4aea5` | certain | Nobody vouched for this name — anyone can file a request naming anyone. Confirm who is asking before you admit | `modules/hall-ui/public/watch.js:397` |
|
|
863
864
|
| `4016b9cf8d57` | certain | None of your asks have been settled yet. | `modules/hall-ui/public/collab.html:134` |
|
|
864
|
-
| `2dd1beb3b2ab` | certain | None yet. A signed-in builder on the live site sees a Review button in its top bar; what they write lands here | `modules/hall-ui/public/studio.js:
|
|
865
|
+
| `2dd1beb3b2ab` | certain | None yet. A signed-in builder on the live site sees a Review button in its top bar; what they write lands here | `modules/hall-ui/public/studio.js:746` |
|
|
865
866
|
| `36c97d6094bf` | certain | None yet. Yours will be the first. | `modules/hall-ui/public/lightbulb.js:94` |
|
|
866
867
|
| `36c97d6094bf` | certain | None yet. Yours will be the first. | `modules/hall-ui/public/page-notes.js:177` |
|
|
867
868
|
| `7b915c25289b` | certain | None. Governing is held, not picked: you steer your own agents. | `modules/hall-ui/public/my-agents.js:371` |
|
|
@@ -1106,7 +1107,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1106
1107
|
| `f1b2b158c3b1` | certain | Review quality | `modules/hall-ui/public/watch.html:85` |
|
|
1107
1108
|
| `4e062d4f8d69` | certain | Reviews by builder | `modules/hall-ui/public/watch.html:78` |
|
|
1108
1109
|
| `5c233879ce4e` | certain | Reviews from before Tweak Mode | `modules/hall-ui/public/studio.html:165` |
|
|
1109
|
-
| `4c112975a739` | certain | Rewrite it in the editor | `modules/hall-ui/public/studio.js:
|
|
1110
|
+
| `4c112975a739` | certain | Rewrite it in the editor | `modules/hall-ui/public/studio.js:843` |
|
|
1110
1111
|
| `c56d9a6451a7` | certain | Rewrite them in the tweak editor | `modules/hall-ui/public/paintbrush.js:252` |
|
|
1111
1112
|
| `f7ed4ba07178` | certain | Roadmap — {{worldName}} | `modules/hall-ui/public/roadmap.html:6` |
|
|
1112
1113
|
| `f7ed4ba07178` | certain | Roadmap — {{worldName}} | `modules/hall-ui/public/roadmap.html:6` |
|
|
@@ -1383,7 +1384,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1383
1384
|
| `2287c62afcdd` | certain | The street | `modules/hall-ui/public/street.html:103` |
|
|
1384
1385
|
| `fdc4b3013936` | certain | The street — {{worldName}} | `modules/hall-ui/public/street.html:6` |
|
|
1385
1386
|
| `fdc4b3013936` | certain | The street — {{worldName}} | `modules/hall-ui/public/street.html:6` |
|
|
1386
|
-
| `d00666a91ac4` | certain | The studio page is yours, but its words could not be read just now. Open it in the editor. | `modules/hall-ui/public/studio.js:
|
|
1387
|
+
| `d00666a91ac4` | certain | The studio page is yours, but its words could not be read just now. Open it in the editor. | `modules/hall-ui/public/studio.js:914` |
|
|
1387
1388
|
| `7d016f4a65bf` | certain | The system is mid-build. These diagrams deliberately separate what stands today from what is yet planned, so t | `modules/hall-ui/public/diagrams.html:40` |
|
|
1388
1389
|
| `599fd0e82d1c` | certain | The task board — all work | `modules/hall-ui/public/studio.html:178` |
|
|
1389
1390
|
| `198ba9353bf6` | certain | The task feed did not answer. Nothing on the record has changed, only the read failed. Try again in a moment, | `modules/hall-ui/public/task.js:1015` |
|
|
@@ -1836,7 +1837,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1836
1837
|
| `1e737a57eb20` | certain | start governing | `modules/hall-ui/public/city.html:99` |
|
|
1837
1838
|
| `e294c3429c2e` | certain | strong at {…} | `modules/hall-ui/public/modules.js:340` |
|
|
1838
1839
|
| `0215b2cdca8c` | certain | submit for review | `modules/hall-ui/public/modules.js:286` |
|
|
1839
|
-
| `43e6f1592ae3` | certain | submit it in the editor | `modules/hall-ui/public/studio.js:
|
|
1840
|
+
| `43e6f1592ae3` | certain | submit it in the editor | `modules/hall-ui/public/studio.js:844` |
|
|
1840
1841
|
| `9ec19e32f86f` | certain | submit page | `modules/hall-ui/public/tweak-editor.html:68` |
|
|
1841
1842
|
| `da30b952a60b` | certain | system / unattributed | `modules/hall-ui/public/goals-page.js:284` |
|
|
1842
1843
|
| `da30b952a60b` | certain | system / unattributed | `modules/hall-ui/public/work.js:581` |
|
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": 2794,
|
|
7
|
+
"unique_ids": 2375,
|
|
8
|
+
"certain": 2794,
|
|
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": 1974
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -7223,7 +7223,7 @@
|
|
|
7223
7223
|
"surface": "builders-hall",
|
|
7224
7224
|
"text": "Good afternoon.",
|
|
7225
7225
|
"file": "modules/hall-ui/public/studio.js",
|
|
7226
|
-
"line":
|
|
7226
|
+
"line": 968,
|
|
7227
7227
|
"origin": "js-text-assign",
|
|
7228
7228
|
"confidence": "certain"
|
|
7229
7229
|
},
|
|
@@ -7241,7 +7241,7 @@
|
|
|
7241
7241
|
"surface": "builders-hall",
|
|
7242
7242
|
"text": "Good morning.",
|
|
7243
7243
|
"file": "modules/hall-ui/public/studio.js",
|
|
7244
|
-
"line":
|
|
7244
|
+
"line": 967,
|
|
7245
7245
|
"origin": "js-text-assign",
|
|
7246
7246
|
"confidence": "certain"
|
|
7247
7247
|
},
|
|
@@ -8910,6 +8910,15 @@
|
|
|
8910
8910
|
"origin": "html-text",
|
|
8911
8911
|
"confidence": "certain"
|
|
8912
8912
|
},
|
|
8913
|
+
{
|
|
8914
|
+
"id": "2d5aea02c40e",
|
|
8915
|
+
"surface": "builders-hall",
|
|
8916
|
+
"text": "Most words to write",
|
|
8917
|
+
"file": "modules/hall-ui/public/studio.js",
|
|
8918
|
+
"line": 737,
|
|
8919
|
+
"origin": "js-markup",
|
|
8920
|
+
"confidence": "certain"
|
|
8921
|
+
},
|
|
8913
8922
|
{
|
|
8914
8923
|
"id": "e52ae74f0052",
|
|
8915
8924
|
"surface": "builders-hall",
|
|
@@ -9662,7 +9671,7 @@
|
|
|
9662
9671
|
"surface": "builders-hall",
|
|
9663
9672
|
"text": "None yet. A signed-in builder on the live site sees a Review button in its top bar; what they write lands here.",
|
|
9664
9673
|
"file": "modules/hall-ui/public/studio.js",
|
|
9665
|
-
"line":
|
|
9674
|
+
"line": 746,
|
|
9666
9675
|
"origin": "js-markup",
|
|
9667
9676
|
"confidence": "certain"
|
|
9668
9677
|
},
|
|
@@ -11867,7 +11876,7 @@
|
|
|
11867
11876
|
"surface": "builders-hall",
|
|
11868
11877
|
"text": "Rewrite it in the editor",
|
|
11869
11878
|
"file": "modules/hall-ui/public/studio.js",
|
|
11870
|
-
"line":
|
|
11879
|
+
"line": 843,
|
|
11871
11880
|
"origin": "js-markup",
|
|
11872
11881
|
"confidence": "certain"
|
|
11873
11882
|
},
|
|
@@ -14360,7 +14369,7 @@
|
|
|
14360
14369
|
"surface": "builders-hall",
|
|
14361
14370
|
"text": "The studio page is yours, but its words could not be read just now. Open it in the editor.",
|
|
14362
14371
|
"file": "modules/hall-ui/public/studio.js",
|
|
14363
|
-
"line":
|
|
14372
|
+
"line": 914,
|
|
14364
14373
|
"origin": "js-text-assign",
|
|
14365
14374
|
"confidence": "certain"
|
|
14366
14375
|
},
|
|
@@ -18437,7 +18446,7 @@
|
|
|
18437
18446
|
"surface": "builders-hall",
|
|
18438
18447
|
"text": "submit it in the editor",
|
|
18439
18448
|
"file": "modules/hall-ui/public/studio.js",
|
|
18440
|
-
"line":
|
|
18449
|
+
"line": 844,
|
|
18441
18450
|
"origin": "js-markup",
|
|
18442
18451
|
"confidence": "certain"
|
|
18443
18452
|
},
|
|
@@ -3177,5 +3177,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3177
3177
|
landed since 1.21.168 with no explicit bump. run 39992064615. (task 1002620)
|
|
3178
3178
|
1.21.170 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3179
3179
|
landed since 1.21.169 with no explicit bump. run 39994339257. (task 1002620)
|
|
3180
|
+
1.21.171 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3181
|
+
landed since 1.21.170 with no explicit bump. run 39996371081. (task 1002620)
|
|
3182
|
+
1.21.172 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3183
|
+
landed since 1.21.171 with no explicit bump. run 39998870016. (task 1002620)
|
|
3180
3184
|
---------------------------------------------------------------------------
|
|
3181
3185
|
```
|
package/docs/page-readings.json
CHANGED
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"surface": "builders",
|
|
51
51
|
"title": "Blockers",
|
|
52
52
|
"states_read": ["default"],
|
|
53
|
-
"files_hash": "
|
|
53
|
+
"files_hash": "f5bf3c2244e3fbdd",
|
|
54
54
|
"reading_hash": "2b3fd98b7ba1bff6",
|
|
55
55
|
"counts": {"lines":28,"placed":12,"shared":8,"unplaced":8},
|
|
56
56
|
"omitted": {"data":9},
|
|
@@ -1141,7 +1141,7 @@
|
|
|
1141
1141
|
"surface": "builders",
|
|
1142
1142
|
"title": "The sky",
|
|
1143
1143
|
"states_read": ["tour","sky","wells","ledger","full","full-back","out"],
|
|
1144
|
-
"files_hash": "
|
|
1144
|
+
"files_hash": "5f2c2f22c2fc9dc6",
|
|
1145
1145
|
"reading_hash": "b83eb8c92a23fd3c",
|
|
1146
1146
|
"counts": {"lines":47,"placed":23,"shared":7,"unplaced":17},
|
|
1147
1147
|
"omitted": {"data":8},
|
|
@@ -1201,7 +1201,7 @@
|
|
|
1201
1201
|
"surface": "builders",
|
|
1202
1202
|
"title": "Goals",
|
|
1203
1203
|
"states_read": ["list","filters","cards","detail","dependencies","out","uat","uat-panel","author"],
|
|
1204
|
-
"files_hash": "
|
|
1204
|
+
"files_hash": "4f6f4eb5e9f00412",
|
|
1205
1205
|
"reading_hash": "7aa1a6e1946dc077",
|
|
1206
1206
|
"counts": {"lines":156,"placed":52,"shared":8,"unplaced":96},
|
|
1207
1207
|
"omitted": {"data":268},
|
|
@@ -1326,7 +1326,7 @@
|
|
|
1326
1326
|
{"key":"L0117","section":"Goals","text":"Version","placement":"unplaced"},
|
|
1327
1327
|
{"key":"L0118","section":"Goals","text":"— any —","placement":"placed","file":"modules/hall-ui/public/hall-widgets.js","line":173,"string_id":"729d4ce22b99","occurrences":2},
|
|
1328
1328
|
{"key":"L0119","section":"Goals","text":"Status","placement":"unplaced"},
|
|
1329
|
-
{"key":"L0120","section":"Goals","text":"open","placement":"placed","file":"modules/hall-ui/public/approval-queue.js","line":
|
|
1329
|
+
{"key":"L0120","section":"Goals","text":"open","placement":"placed","file":"modules/hall-ui/public/approval-queue.js","line":166,"string_id":"9a803563c948","occurrences":16},
|
|
1330
1330
|
{"key":"L0121","section":"Goals","text":"Sort","placement":"unplaced"},
|
|
1331
1331
|
{"key":"L0122","section":"Goals","text":"priority (version order)","placement":"unplaced"},
|
|
1332
1332
|
{"key":"L0123","section":"Goals","text":"newest first","placement":"unplaced"},
|
|
@@ -1439,7 +1439,7 @@
|
|
|
1439
1439
|
"surface": "builders",
|
|
1440
1440
|
"title": "Builders' Hall",
|
|
1441
1441
|
"states_read": ["home","standing","standing-karma","out","founding","genesis","decide","demo"],
|
|
1442
|
-
"files_hash": "
|
|
1442
|
+
"files_hash": "e32eb071e46bec38",
|
|
1443
1443
|
"reading_hash": "42dbab4f3948fe66",
|
|
1444
1444
|
"counts": {"lines":117,"placed":63,"shared":10,"unplaced":44},
|
|
1445
1445
|
"omitted": {"data":101},
|
|
@@ -1569,7 +1569,7 @@
|
|
|
1569
1569
|
"surface": "builders",
|
|
1570
1570
|
"title": "Idea",
|
|
1571
1571
|
"states_read": ["record","missing"],
|
|
1572
|
-
"files_hash": "
|
|
1572
|
+
"files_hash": "7456ea53572161d4",
|
|
1573
1573
|
"reading_hash": "c9f9a84ab844c704",
|
|
1574
1574
|
"counts": {"lines":52,"placed":23,"shared":9,"unplaced":20},
|
|
1575
1575
|
"omitted": {"data":18},
|
|
@@ -1609,7 +1609,7 @@
|
|
|
1609
1609
|
{"key":"L0032","section":"Everything that fell out of it","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","state":"missing"},
|
|
1610
1610
|
{"key":"L0033","section":"footer","text":"Sign out","placement":"shared","file":"modules/hall-ui/public/shell.js","line":520,"string_id":"1883ad7f07fc","state":"missing","occurrences":2},
|
|
1611
1611
|
{"key":"L0034","section":"Everything that fell out of it","text":"Builders hall · this session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":674,"string_id":"fd1903586e8e","state":"missing"},
|
|
1612
|
-
{"key":"L0035","section":"12A changelog every builder can read","text":"open","placement":"placed","file":"modules/hall-ui/public/approval-queue.js","line":
|
|
1612
|
+
{"key":"L0035","section":"12A changelog every builder can read","text":"open","placement":"placed","file":"modules/hall-ui/public/approval-queue.js","line":166,"string_id":"9a803563c948"},
|
|
1613
1613
|
{"key":"L0036","section":"12A changelog every builder can read","text":"had the thought · {…}","placement":"unplaced"},
|
|
1614
1614
|
{"key":"L0037","section":"12A changelog every builder can read","text":"{…}/100 complete","placement":"placed","file":"modules/hall-ui/public/idea.js","line":87,"string_id":"c43050b62297"},
|
|
1615
1615
|
{"key":"L0038","section":"12A changelog every builder can read","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","occurrences":4},
|
|
@@ -1719,7 +1719,7 @@
|
|
|
1719
1719
|
"surface": "builders",
|
|
1720
1720
|
"title": "Modules",
|
|
1721
1721
|
"states_read": ["browse","search-empty","lens","details","out"],
|
|
1722
|
-
"files_hash": "
|
|
1722
|
+
"files_hash": "6dc5460706966349",
|
|
1723
1723
|
"reading_hash": "a17f501b72321b7b",
|
|
1724
1724
|
"counts": {"lines":62,"placed":29,"shared":8,"unplaced":25},
|
|
1725
1725
|
"omitted": {"data":106},
|
|
@@ -2325,7 +2325,7 @@
|
|
|
2325
2325
|
"surface": "builders",
|
|
2326
2326
|
"title": "Profile",
|
|
2327
2327
|
"states_read": ["own","works","activity","activity-artist","activity-ideator","other","artist","ideator","out"],
|
|
2328
|
-
"files_hash": "
|
|
2328
|
+
"files_hash": "82a35b34a5115325",
|
|
2329
2329
|
"reading_hash": "8088d60a4e93bb91",
|
|
2330
2330
|
"counts": {"lines":196,"placed":70,"shared":8,"unplaced":118},
|
|
2331
2331
|
"omitted": {"data":197},
|
|
@@ -2721,7 +2721,7 @@
|
|
|
2721
2721
|
"surface": "builders",
|
|
2722
2722
|
"title": "Builders",
|
|
2723
2723
|
"states_read": ["roster","artists","search","out"],
|
|
2724
|
-
"files_hash": "
|
|
2724
|
+
"files_hash": "88e2f7aca1994346",
|
|
2725
2725
|
"reading_hash": "5c7e507e9600e59c",
|
|
2726
2726
|
"counts": {"lines":47,"placed":13,"shared":8,"unplaced":26},
|
|
2727
2727
|
"omitted": {"data":60},
|
|
@@ -2781,7 +2781,7 @@
|
|
|
2781
2781
|
"surface": "builders",
|
|
2782
2782
|
"title": "Sessions",
|
|
2783
2783
|
"states_read": ["ledger","out"],
|
|
2784
|
-
"files_hash": "
|
|
2784
|
+
"files_hash": "cd14ebd4d35e16bf",
|
|
2785
2785
|
"reading_hash": "5c18aaed7f2e8e0f",
|
|
2786
2786
|
"counts": {"lines":331,"placed":21,"shared":8,"unplaced":302},
|
|
2787
2787
|
"omitted": {"data":303},
|
|
@@ -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": "b9928eadcd3c7524",
|
|
3982
3982
|
"reading_hash": "55e9090fdd96a979",
|
|
3983
3983
|
"counts": {"lines":30,"placed":16,"shared":6,"unplaced":8},
|
|
3984
3984
|
"omitted": {"data":4},
|
|
@@ -4021,7 +4021,7 @@
|
|
|
4021
4021
|
"surface": "builders",
|
|
4022
4022
|
"title": "Task",
|
|
4023
4023
|
"states_read": ["record","from-goal","missing"],
|
|
4024
|
-
"files_hash": "
|
|
4024
|
+
"files_hash": "5535b9790b56985e",
|
|
4025
4025
|
"reading_hash": "3e80d092af1d7e2c",
|
|
4026
4026
|
"counts": {"lines":132,"placed":35,"shared":10,"unplaced":87},
|
|
4027
4027
|
"omitted": {"data":75},
|
|
@@ -4166,7 +4166,7 @@
|
|
|
4166
4166
|
"surface": "builders",
|
|
4167
4167
|
"title": "Your thinking",
|
|
4168
4168
|
"states_read": ["tour","sky","list","simple","full-idea","full","full-list","full-back","out"],
|
|
4169
|
-
"files_hash": "
|
|
4169
|
+
"files_hash": "a7b04635b758d676",
|
|
4170
4170
|
"reading_hash": "532805c03c3a8a4c",
|
|
4171
4171
|
"counts": {"lines":48,"placed":23,"shared":7,"unplaced":18},
|
|
4172
4172
|
"omitted": {"data":8},
|
|
@@ -4263,7 +4263,7 @@
|
|
|
4263
4263
|
"surface": "builders",
|
|
4264
4264
|
"title": "Watch",
|
|
4265
4265
|
"states_read": ["board","out","operations","security"],
|
|
4266
|
-
"files_hash": "
|
|
4266
|
+
"files_hash": "3980ad82a03bc297",
|
|
4267
4267
|
"reading_hash": "d698ac62c904aa65",
|
|
4268
4268
|
"counts": {"lines":88,"placed":38,"shared":8,"unplaced":42},
|
|
4269
4269
|
"omitted": {"data":166},
|
|
@@ -4364,7 +4364,7 @@
|
|
|
4364
4364
|
"surface": "builders",
|
|
4365
4365
|
"title": "Tasks",
|
|
4366
4366
|
"states_read": ["ready","in-progress","backlog","completed","filters-open","out","founding"],
|
|
4367
|
-
"files_hash": "
|
|
4367
|
+
"files_hash": "1dcb08711f496752",
|
|
4368
4368
|
"reading_hash": "a425c7a090d8bf03",
|
|
4369
4369
|
"counts": {"lines":125,"placed":54,"shared":8,"unplaced":63},
|
|
4370
4370
|
"omitted": {"data":316},
|
|
@@ -242,6 +242,32 @@ function changelogFor(rounds) {
|
|
|
242
242
|
// Composition.
|
|
243
243
|
// ---------------------------------------------------------------------------
|
|
244
244
|
|
|
245
|
+
// task 1004670: how much text a page shows, the measure the worst-pages report
|
|
246
|
+
// and the copy queue rank by. Distinct non-shared lines of its reading: shell
|
|
247
|
+
// text every page carries is no page's own work, and a line a page repeats in
|
|
248
|
+
// several states is written once. null when the page has not been read.
|
|
249
|
+
function textLinesOf(readingPage) {
|
|
250
|
+
if (!readingPage || !Array.isArray(readingPage.lines)) return null;
|
|
251
|
+
return new Set(readingPage.lines.filter((l) => l && l.placement !== 'shared').map((l) => String(l.text))).size;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// The worst-pages report (task 1004670): the pages with the most text first,
|
|
255
|
+
// each with its status, so the copy queue's priority is visible in the Studio.
|
|
256
|
+
const WORST_PAGES = 5;
|
|
257
|
+
function worstPagesFor(inventory, rIdx, list) {
|
|
258
|
+
const byId = new Map(list.map((p) => [p.id, p]));
|
|
259
|
+
return ((inventory && inventory.pages) || [])
|
|
260
|
+
.filter((p) => p && p.id)
|
|
261
|
+
.map((p, i) => ({ p, i, n: textLinesOf(rIdx.get(p.id)) }))
|
|
262
|
+
.filter((x) => x.n != null && x.n > 0)
|
|
263
|
+
.sort((a, b) => b.n - a.n || a.i - b.i)
|
|
264
|
+
.slice(0, WORST_PAGES)
|
|
265
|
+
.map(({ p, n }) => ({
|
|
266
|
+
page_id: p.id, title: p.title || p.id, surface: p.surface, path: p.path || null,
|
|
267
|
+
text_lines: n, status_label: (byId.get(p.id) || {}).status_label || null,
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
270
|
+
|
|
245
271
|
function readingIndex(readings) {
|
|
246
272
|
const m = new Map();
|
|
247
273
|
for (const p of (readings && readings.pages) || []) if (p && p.id) m.set(p.id, p);
|
|
@@ -393,6 +419,7 @@ function composePages({ inventory, readings, tasks, now = Date.now() }) {
|
|
|
393
419
|
surfaces,
|
|
394
420
|
totals: { tweaked, total: list.length, line: `${tweaked} of ${list.length} pages tweaked` },
|
|
395
421
|
in_flight: inFlightFor(list, byPage, now instanceof Date ? now.getTime() : Number(now)),
|
|
422
|
+
worst_pages: worstPagesFor(inventory, rIdx, list),
|
|
396
423
|
};
|
|
397
424
|
}
|
|
398
425
|
|
|
@@ -575,6 +602,8 @@ function candidateFor({ page, rounds, readingPage, readingsOk, ask, builderId, a
|
|
|
575
602
|
askers,
|
|
576
603
|
oldest_ask_at: ask ? iso(ask.oldest_ask_at) : null,
|
|
577
604
|
changed_lines: changedLines,
|
|
605
|
+
// task 1004670: more text ranks a page higher within its tier.
|
|
606
|
+
text_lines: textLinesOf(readingPage) || 0,
|
|
578
607
|
drift_known: drift.known,
|
|
579
608
|
count: counts.count,
|
|
580
609
|
status_label: counts.status_label,
|
|
@@ -594,6 +623,9 @@ function compareCandidates(a, b) {
|
|
|
594
623
|
if (at !== bt) return at - bt;
|
|
595
624
|
}
|
|
596
625
|
if (a.tier === 'changed' && a.changed_lines !== b.changed_lines) return b.changed_lines - a.changed_lines;
|
|
626
|
+
// task 1004670: within a tier, the page with the most text leads (the
|
|
627
|
+
// worst-pages report's measure), then the inventory's own order.
|
|
628
|
+
if (a.text_lines !== b.text_lines) return b.text_lines - a.text_lines;
|
|
597
629
|
return a._base - b._base;
|
|
598
630
|
}
|
|
599
631
|
|
|
@@ -922,6 +954,8 @@ module.exports = {
|
|
|
922
954
|
roundState,
|
|
923
955
|
changelogFor,
|
|
924
956
|
composePages,
|
|
957
|
+
textLinesOf,
|
|
958
|
+
WORST_PAGES,
|
|
925
959
|
composePage,
|
|
926
960
|
lastRoundShippedAt,
|
|
927
961
|
composeTally,
|
|
@@ -92,11 +92,12 @@ const order = (out) => [out.recommended, ...out.alternatives].filter(Boolean).ma
|
|
|
92
92
|
const EXPECTED = [
|
|
93
93
|
'builders:work', 'landing:privacy', 'builders:tasks',
|
|
94
94
|
'builders:goals', 'status:board',
|
|
95
|
-
|
|
95
|
+
// task 1004670: within a tier the page with the most text leads, then surface order.
|
|
96
|
+
'landing:index', 'status:explore', 'builders:ideas',
|
|
96
97
|
'landing:projects',
|
|
97
98
|
];
|
|
98
99
|
|
|
99
|
-
await test('the order on the fixture: asked, changed, untweaked (
|
|
100
|
+
await test('the order on the fixture: asked, changed, untweaked (most text first), tweaked last', () => {
|
|
100
101
|
const out = next(F.ALICE.id);
|
|
101
102
|
assert.deepEqual(order(out), EXPECTED);
|
|
102
103
|
assert.deepEqual([...new Set([out.recommended, ...out.alternatives].map((c) => c.tier))], ['asked', 'changed', 'untweaked', 'tweaked']);
|
|
@@ -180,5 +181,32 @@ await test('nothing left to take: recommended is null, never a held page', () =>
|
|
|
180
181
|
assert.equal(out.skipped.length, 2);
|
|
181
182
|
});
|
|
182
183
|
|
|
184
|
+
// task 1004670: the worst-pages report. Pages with the most text lead the copy
|
|
185
|
+
// queue within their tier, and the Studio's page list names the worst ones.
|
|
186
|
+
await test('more text leads within a tier; shell text and repeats do not count', () => {
|
|
187
|
+
const L = (text, placement = 'placed') => ({ key: 'k', section: 's', text, placement });
|
|
188
|
+
const inventory = { surfaces: [{ id: 'landing' }], pages: [
|
|
189
|
+
{ id: 'landing:a', surface: 'landing', title: 'A', path: '/a', files: [] },
|
|
190
|
+
{ id: 'landing:b', surface: 'landing', title: 'B', path: '/b', files: [] },
|
|
191
|
+
{ id: 'landing:c', surface: 'landing', title: 'C', path: '/c', files: [] },
|
|
192
|
+
] };
|
|
193
|
+
const readings = { pages: [
|
|
194
|
+
{ id: 'landing:a', reading_hash: 'a', lines: [L('one'), L('Skip', 'shared'), L('Nav', 'shared'), L('Footer', 'shared')] },
|
|
195
|
+
{ id: 'landing:b', reading_hash: 'b', lines: [L('one'), L('two'), L('three'), L('two')] },
|
|
196
|
+
{ id: 'landing:c', reading_hash: 'c', lines: [L('one'), L('two')] },
|
|
197
|
+
] };
|
|
198
|
+
assert.equal(status.textLinesOf(readings.pages[0]), 1, 'shared shell text is no page\'s own');
|
|
199
|
+
assert.equal(status.textLinesOf(readings.pages[1]), 3, 'a repeated line is written once');
|
|
200
|
+
assert.equal(status.textLinesOf(null), null);
|
|
201
|
+
const out = status.composeNext({ inventory, readings, tasks: [], asks: [], builderId: F.ALICE.id, alternatives: 9 });
|
|
202
|
+
assert.deepEqual(order(out), ['landing:b', 'landing:c', 'landing:a']);
|
|
203
|
+
assert.deepEqual([out.recommended.text_lines, out.recommended.tier], [3, 'untweaked']);
|
|
204
|
+
|
|
205
|
+
const pagesOut = status.composePages({ inventory, readings, tasks: [] });
|
|
206
|
+
assert.deepEqual(pagesOut.worst_pages.map((w) => [w.page_id, w.text_lines, w.status_label]),
|
|
207
|
+
[['landing:b', 3, 'Untweaked'], ['landing:c', 2, 'Untweaked'], ['landing:a', 1, 'Untweaked']]);
|
|
208
|
+
assert.deepEqual(status.composePages({ inventory, readings: null, tasks: [] }).worst_pages, [], 'unread pages are not ranked');
|
|
209
|
+
});
|
|
210
|
+
|
|
183
211
|
console.log(`\ncopy_page_next: ${passed} passed, ${failed} failed`);
|
|
184
212
|
if (failed) process.exit(1);
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
// estimate with a "<Currency> Est." label; settled → the actual ledger payout
|
|
92
92
|
// with a plain "<Currency>" label.
|
|
93
93
|
//
|
|
94
|
-
// task 1002469 — on a cost-plus-only instance that second case
|
|
94
|
+
// task 1002469 (superseded by task 1004781) — on a cost-plus-only instance that second case was a LIE. The
|
|
95
95
|
// per-task estimate is never paid, so credits_awarded is 0 on every shipped
|
|
96
96
|
// task, taskCreditValue's zero-fallback hands back the estimate, and the plain
|
|
97
97
|
// label then presents that estimate as the actual payout. So the "Est."
|
|
@@ -105,18 +105,25 @@
|
|
|
105
105
|
function taskCreditValue(task) {
|
|
106
106
|
if (!task) return null;
|
|
107
107
|
if (task.status === 'abandoned' || task.merged_into_task_id != null) return 0;
|
|
108
|
+
// task 1004781 — a settled task is worth what the ledger PAID for it and
|
|
109
|
+
// nothing else. It used to fall back to the estimate when the ledger held
|
|
110
|
+
// 0, and on a cost-plus-only instance that is every shipped task, so goal
|
|
111
|
+
// totals summed guesses as money (a goal read 3,373 for a builder paid
|
|
112
|
+
// 776). A settled 0 is the truth: the task itself paid nothing. Only
|
|
113
|
+
// unsettled work carries the estimate, and taskCreditLabel marks it "Est.".
|
|
114
|
+
// The decision the task asked for: estimates are KEPT, for live work only —
|
|
115
|
+
// they still rank claimable work (task-lib's best-value sort) and tell a
|
|
116
|
+
// builder roughly how big a task is — but they never reach a total.
|
|
108
117
|
if (taskSettled(task)) {
|
|
109
118
|
var awarded = Number(task.credits_awarded);
|
|
110
|
-
|
|
111
|
-
// ledger record (0 — legacy pre-ledger tasks, or reward-exempt), fall back
|
|
112
|
-
// to the estimate rather than showing a misleading "0".
|
|
113
|
-
if (isFinite(awarded) && awarded > 0) return awarded;
|
|
119
|
+
return isFinite(awarded) && awarded > 0 ? awarded : 0;
|
|
114
120
|
}
|
|
115
121
|
return task.credits_reward;
|
|
116
122
|
}
|
|
117
123
|
function taskCreditLabel(task, opts) {
|
|
124
|
+
// A settled figure is now always the actual payout (see taskCreditValue),
|
|
125
|
+
// so it takes the plain label on every reward mode; only an estimate is "Est.".
|
|
118
126
|
var base = currencyLabel(opts);
|
|
119
|
-
if (!perTaskCredit()) return base + ' Est.';
|
|
120
127
|
return taskSettled(task) ? base : base + ' Est.';
|
|
121
128
|
}
|
|
122
129
|
|
|
@@ -142,15 +142,16 @@
|
|
|
142
142
|
// The prototype read a snapshot (docs/design/mocks/sky/snapshot.js). Each
|
|
143
143
|
// adaptor maps one real read onto the same field names, so the world below
|
|
144
144
|
// is the prototype's world line for line.
|
|
145
|
+
const SETTLED = new Set(['confirmed', 'shipped']);
|
|
145
146
|
function goalFromSky(g) {
|
|
146
147
|
return {
|
|
147
148
|
id: String(g.goal_id), title: g.title || 'A private goal', subtitle: g.subtitle || null, version: g.version_id, status: g.status,
|
|
148
149
|
taskTotal: Number(g.task_total) || 0, taskDone: Number(g.task_done) || 0, createdAt: g.created_at || null,
|
|
149
150
|
crit: (g.criteria || []).map((c) => ({ id: String(c.id), text: c.criterion_md || '', satisfied: c.satisfied === true, tasks: (c.task_ids || []).map(String), pendingReview: c.pending_review === true, uat: c.uat_state || null, uatLabel: uatLabel(c.uat_state), backendOnly: c.backend_only === true })),
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
tasks: (g.tasks || []).map((t) => ({ id: String(t.id), title: t.title || 'a task', status: t.status, credits: t.status === 'abandoned' || t.credits_reward == null ?
|
|
151
|
+
// `credits` is what the ledger PAID — settled work only (task 1004781) — so a well's mass and a ring's weight are
|
|
152
|
+
// money paid. Live work carries its guess as `estimate`, shown "Est." and never added to a total. Abandoned (or
|
|
153
|
+
// merged-away, which is always abandoned — CHECK-constrained) work has neither (task 1004782).
|
|
154
|
+
tasks: (g.tasks || []).map((t) => ({ id: String(t.id), title: t.title || 'a task', status: t.status, credits: SETTLED.has(t.status) ? Number(t.credits_awarded) || 0 : 0, estimate: SETTLED.has(t.status) || t.status === 'abandoned' || t.credits_reward == null ? null : Number(t.credits_reward), createdAt: t.created_on || null, sourceRef: t.idea_id != null ? String(t.idea_id) : null })),
|
|
154
155
|
};
|
|
155
156
|
}
|
|
156
157
|
const taskRow = (t) => ({ id: String(t.task_id ?? t.id), title: t.title || 'a task', status: t.status, goalId: t.goal_id != null ? String(t.goal_id) : null, origin: t.origin || null });
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
<div class="facts">
|
|
99
99
|
${g ? fact('well', esc(g.title)) : ''}
|
|
100
100
|
${crit ? fact('gates the ring', esc(plain(crit.text).slice(0, 90))) : g ? fact('gates the ring', 'none — outer orbit') : ''}
|
|
101
|
-
${t.credits != null ? fact('credits', String(t.credits), true) : ''}
|
|
101
|
+
${t.estimate != null ? fact('credits', `${t.estimate} <span class="lbl">Est.</span>`, true) : t.credits != null ? fact('credits paid', String(t.credits), true) : ''}
|
|
102
102
|
${t.createdAt ? fact('born', esc(t.createdAt)) : ''}
|
|
103
103
|
${t.sourceRef ? fact('born of a thought', `<a href="${HALL}/idea/${esc(t.sourceRef)}" target="_blank" rel="noopener">${esc(ideaTitle(t.sourceRef))}</a>`) : ''}
|
|
104
104
|
</div>
|
|
@@ -171,6 +171,18 @@
|
|
|
171
171
|
});
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
// The worst-pages report (task 1004670): the pages with the most text, from
|
|
175
|
+
// the server's own list, most first. The panel only words the count.
|
|
176
|
+
function worstRows(pages) {
|
|
177
|
+
return ((pages && pages.worst_pages) || []).map((p) => ({
|
|
178
|
+
page_id: p.page_id,
|
|
179
|
+
title: titleOf(p),
|
|
180
|
+
surface: surfaceWord(p.surface),
|
|
181
|
+
size: `${num(p.text_lines)} ${num(p.text_lines) === 1 ? 'line' : 'lines'} of text`,
|
|
182
|
+
status: p.status_label || '',
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
|
|
174
186
|
function askedLine(n) {
|
|
175
187
|
const v = num(n);
|
|
176
188
|
if (v <= 0) return '';
|
|
@@ -241,7 +253,7 @@
|
|
|
241
253
|
|
|
242
254
|
const api = {
|
|
243
255
|
STUDIO_PAGE, PAGE_ID_RE, SURFACE_WORDS, surfaceWord, titleOf, numberWords, tallyParts, resumeDetail,
|
|
244
|
-
subParts, flowDetail, statusDetail, apexHost, landingHost, surfaceRows, inFlightRows, askedLine, pickLabel, matchLine, claimWords,
|
|
256
|
+
subParts, flowDetail, statusDetail, apexHost, landingHost, surfaceRows, inFlightRows, worstRows, askedLine, pickLabel, matchLine, claimWords,
|
|
245
257
|
skyAt, dayPart, STOCK_GREETING, clockGreets,
|
|
246
258
|
};
|
|
247
259
|
if (typeof window !== 'undefined') window.OTBStudioLib = api;
|
|
@@ -727,7 +727,15 @@
|
|
|
727
727
|
<span class="st-flight__state">${escapeHtml(f.state)}${f.age ? ` · ${escapeHtml(f.age)}` : ''}${f.stuck ? ' · stuck' : ''}</span>
|
|
728
728
|
</li>`).join('');
|
|
729
729
|
const inFlight = flight ? `<h3 class="st-flight__head">Where your pages are</h3><ul class="st-flights">${flight}</ul>` : '';
|
|
730
|
-
|
|
730
|
+
// task 1004670: the worst-pages report, the pages with the most text first.
|
|
731
|
+
const worst = lib.worstRows(home.pages).map((w) => `
|
|
732
|
+
<li class="st-flight">
|
|
733
|
+
<a class="st-flight__page" href="${escapeHtml(editorHref(w.page_id))}">${escapeHtml(w.title)}</a>
|
|
734
|
+
<span class="st-flight__where">${escapeHtml(w.surface)}</span>
|
|
735
|
+
<span class="st-flight__state">${escapeHtml(w.size)}${w.status ? ` · ${escapeHtml(w.status.toLowerCase())}` : ''}</span>
|
|
736
|
+
</li>`).join('');
|
|
737
|
+
const worstList = worst ? `<h3 class="st-flight__head">Most words to write</h3><ul class="st-flights st-worst">${worst}</ul>` : '';
|
|
738
|
+
return `<ul class="st-surfaces">${rows}</ul>${asked ? `<p class="st-asked">${escapeHtml(asked)}</p>` : ''}${inFlight}${worstList}`;
|
|
731
739
|
}
|
|
732
740
|
|
|
733
741
|
function siteHtml() {
|
package/modules/lifecycle/sky.js
CHANGED
|
@@ -44,6 +44,7 @@ const goalGraph = require('./goal-graph');
|
|
|
44
44
|
const goalCategory = require('./goal-category');
|
|
45
45
|
const { isPendingReview, uatState, uatReadColumnsSql } = require('./done-when');
|
|
46
46
|
const { TERMINAL_STATUSES } = require('./task-status');
|
|
47
|
+
const { CREDITS_AWARDED_SUBQUERY } = require('./db-tasks');
|
|
47
48
|
|
|
48
49
|
const SKY_CACHE_TTL_MS = 60 * 1000;
|
|
49
50
|
|
|
@@ -92,9 +93,12 @@ async function skyRows({ versionId = null, viewerId = null, viewerIsArchon = fal
|
|
|
92
93
|
// Compact by construction: no description, no value summary, no notes.
|
|
93
94
|
// created_at is truncated to the DAY in SQL so the payload carries ten
|
|
94
95
|
// characters per task, not twenty-seven, and the clock draws by day anyway.
|
|
96
|
+
// credits_awarded (task 1004781) is the shared settled-only payout subquery; its
|
|
97
|
+
// per-row lookup rides idx_credit_log_task (core_189), never a scan of the log.
|
|
95
98
|
`SELECT t.id::text AS id, t.goal_id::text AS goal_id, t.title, t.status,
|
|
96
99
|
t.credits_reward, t.created_at::date::text AS created_on,
|
|
97
|
-
CASE WHEN t.source_ref ~ '^idea:[0-9]+$' THEN substring(t.source_ref from 6) END AS idea_id
|
|
100
|
+
CASE WHEN t.source_ref ~ '^idea:[0-9]+$' THEN substring(t.source_ref from 6) END AS idea_id,
|
|
101
|
+
${CREDITS_AWARDED_SUBQUERY} AS credits_awarded
|
|
98
102
|
FROM tasks t
|
|
99
103
|
JOIN goals g ON g.id = t.goal_id
|
|
100
104
|
WHERE ($1::text IS NULL OR g.version_id = $1)
|
|
@@ -189,6 +193,9 @@ function shapeSky({ goals = [], edges = [], extras = [], criteria = [], tasks =
|
|
|
189
193
|
title: visible ? t.title : null,
|
|
190
194
|
status: t.status,
|
|
191
195
|
credits_reward: t.credits_reward == null ? null : Number(t.credits_reward),
|
|
196
|
+
// task 1004781 — what the ledger actually paid (0 until settled), so the
|
|
197
|
+
// sky sizes a well by money paid, never by the estimate.
|
|
198
|
+
credits_awarded: t.credits_awarded == null ? 0 : Number(t.credits_awarded),
|
|
192
199
|
created_on: t.created_on || null,
|
|
193
200
|
idea_id: t.idea_id != null ? String(t.idea_id) : null,
|
|
194
201
|
});
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.172",
|
|
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.172",
|
|
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.172",
|
|
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
|
@@ -10094,5 +10094,17 @@
|
|
|
10094
10094
|
"id": "1003806",
|
|
10095
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
10096
|
}
|
|
10097
|
+
],
|
|
10098
|
+
"1.21.171": [
|
|
10099
|
+
{
|
|
10100
|
+
"id": "1004781",
|
|
10101
|
+
"text": "Credit figures in the hall now show money that was actually paid. A finished task shows its real payout instead of the old guess, so goal totals, member stats and the goal map stop adding up estimates as if they were earnings."
|
|
10102
|
+
}
|
|
10103
|
+
],
|
|
10104
|
+
"1.21.172": [
|
|
10105
|
+
{
|
|
10106
|
+
"id": "1004670",
|
|
10107
|
+
"text": "The Studio now shows which pages have the most words to write: a short list of the five pages with the most text, each with how many lines it has and whether it has been tweaked, linked straight to the editor. The suggested ne"
|
|
10108
|
+
}
|
|
10097
10109
|
]
|
|
10098
10110
|
}
|
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.172'; // 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');
|
package/tests/goal_map_page.mjs
CHANGED
|
@@ -60,14 +60,14 @@ function well(over = {}) {
|
|
|
60
60
|
{ id: '71', criterion_id: 'c2', criterion_md: 'The second ring is open', satisfied: false, pending_review: false, task_ids: ['501', '502'] },
|
|
61
61
|
],
|
|
62
62
|
tasks: [
|
|
63
|
-
{ id: '500', title: 'The task', status: 'shipped', credits_reward: 60, created_on: '2026-07-01', idea_id: '10' },
|
|
63
|
+
{ id: '500', title: 'The task', status: 'shipped', credits_reward: 60, credits_awarded: 60, created_on: '2026-07-01', idea_id: '10' },
|
|
64
64
|
{ id: '501', title: 'A held task', status: 'active', credits_reward: 40, created_on: '2026-08-01', idea_id: null },
|
|
65
65
|
{ id: '502', title: 'A claimable task', status: 'ready', credits_reward: 30, created_on: '2026-08-02', idea_id: null },
|
|
66
66
|
],
|
|
67
67
|
...over,
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
const tasksOf = (prefix, n, status, created = '2026-05-01') => Array.from({ length: n }, (_, i) => ({ id: `${prefix}${i}`, title: `task ${prefix}${i}`, status, credits_reward: 20, created_on: created, idea_id: null }));
|
|
70
|
+
const tasksOf = (prefix, n, status, created = '2026-05-01') => Array.from({ length: n }, (_, i) => ({ id: `${prefix}${i}`, title: `task ${prefix}${i}`, status, credits_reward: 20, credits_awarded: status === 'shipped' ? 20 : 0, created_on: created, idea_id: null }));
|
|
71
71
|
// A quiet well: nothing held, nothing landed this week.
|
|
72
72
|
const quiet = (over = {}) => well({ goal_id: '9', title: 'A quiet well — nothing moves', task_total: 4, task_done: 4, criteria: [], tasks: tasksOf('9-', 4, 'shipped'), ...over });
|
|
73
73
|
const settled = (over = {}) => well({ goal_id: '8', title: 'A settled system — it finished', status: 'achieved', task_total: 3, task_done: 3, tasks: tasksOf('8-', 3, 'shipped'), created_at: '2026-03-01T00:00:00.000Z', ...over });
|
|
@@ -142,7 +142,7 @@ await test('a goal is a well sized by its credits; settled and collapsed goals k
|
|
|
142
142
|
assert.equal(o.mass, 60);
|
|
143
143
|
assert.ok(Math.abs(a.r - o.r * 0.8) < 1e-9, 'a settled system is 0.8× the well it would have been');
|
|
144
144
|
assert.ok(Math.abs(c.r - o.r * 0.5) < 1e-9, 'a collapsed goal is half');
|
|
145
|
-
const big = await boot({ sky: sky([well(same({ goal_id: '1' })), well({ ...same({ goal_id: '4' }), tasks: tasksOf('4-', 3, 'shipped').map((t) => ({ ...t, credits_reward: 400 })) })]) });
|
|
145
|
+
const big = await boot({ sky: sky([well(same({ goal_id: '1' })), well({ ...same({ goal_id: '4' }), tasks: tasksOf('4-', 3, 'shipped').map((t) => ({ ...t, credits_reward: 400, credits_awarded: 400 })) })]) });
|
|
146
146
|
assert.ok(goal(big, '4').r > goal(big, '1').r, 'more credits, a bigger well');
|
|
147
147
|
});
|
|
148
148
|
|
|
@@ -227,7 +227,7 @@ await test('EXPLORE IS ON BY DEFAULT: the sky asks where you begin, and the ledg
|
|
|
227
227
|
assert.equal(p.el('start').hidden, false, '"where do you begin" is up');
|
|
228
228
|
const opts = p.el('startOpts').querySelectorAll('button');
|
|
229
229
|
assert.equal(opts[0].textContent, 'where my thoughts are');
|
|
230
|
-
assert.deepEqual(opts.slice(1, -1).map((b) => b.dataset.start), ['
|
|
230
|
+
assert.deepEqual(opts.slice(1, -1).map((b) => b.dataset.start), ['9', '7'], 'one door per open well, biggest first (task 1004781: 7 weighs only the 60 it paid now)');
|
|
231
231
|
assert.equal(opts.at(-1).textContent, 'show me everything');
|
|
232
232
|
assert.equal(SKY.seen(goal(p, '7')), false, 'nothing is reached yet');
|
|
233
233
|
p.key('l');
|
|
@@ -283,7 +283,7 @@ await test('THE LEDGER IS THE ACCESSIBLE PATH: every goal is a row, both directi
|
|
|
283
283
|
assert.ok(rows.includes(SKY.esc(g.title)), `${g.title} is named whole`);
|
|
284
284
|
}
|
|
285
285
|
const rowOf = (id) => (new RegExp(`data-id="${id}"[\\s\\S]*?</button>`).exec(rows) || [''])[0];
|
|
286
|
-
assert.match(rowOf('7'), /alive · 1\/3 tasks ·
|
|
286
|
+
assert.match(rowOf('7'), /alive · 1\/3 tasks · 60 cr · 1\/2 rings · waits on 1/, 'the waiting goal (alive: a task is held) counts what it waits on');
|
|
287
287
|
assert.match(rowOf('9'), /waited on by 1/, 'the goal waited on counts it the other way');
|
|
288
288
|
assert.match(rowOf('8'), /settled/); assert.match(rowOf('6'), /collapsed/);
|
|
289
289
|
const filters = p.el('filters').querySelectorAll('button').map((b) => [b.dataset.f, b.textContent.replace(/\s*\d+$/, '').trim()]);
|
package/tests/hall_sky.mjs
CHANGED
|
@@ -92,7 +92,7 @@ function well(over = {}) {
|
|
|
92
92
|
{ id: '71', criterion_id: 'c2', criterion_md: 'The second ring is open', satisfied: false, pending_review: false, task_ids: ['501', '502'] },
|
|
93
93
|
],
|
|
94
94
|
tasks: [
|
|
95
|
-
{ id: '500', title: 'The task', status: 'shipped', credits_reward: 60, created_on: '2026-07-01', idea_id: '10' },
|
|
95
|
+
{ id: '500', title: 'The task', status: 'shipped', credits_reward: 60, credits_awarded: 6, created_on: '2026-07-01', idea_id: '10' },
|
|
96
96
|
{ id: '501', title: 'A held task', status: 'active', credits_reward: 40, created_on: '2026-08-01', idea_id: null },
|
|
97
97
|
{ id: '502', title: 'A claimable task', status: 'ready', credits_reward: 30, created_on: '2026-08-02', idea_id: null },
|
|
98
98
|
],
|
|
@@ -211,7 +211,18 @@ await test('the rings are the criteria, closing as each is satisfied and thick w
|
|
|
211
211
|
const g = p.S().goalById.get('7');
|
|
212
212
|
assert.equal(g.crit.length, 2);
|
|
213
213
|
same(g.crit.map((c) => c.satisfied), [true, false]);
|
|
214
|
-
|
|
214
|
+
// task 1004781 — a ring weighs what its tasks were PAID: the shipped task paid 6 (its 60 was a guess), and
|
|
215
|
+
// the held and claimable tasks have paid nothing yet, so their estimates stay off the ring.
|
|
216
|
+
same(g.crit.map((c) => c.credits), [6, 0], 'a ring\'s credits are what the tasks on it were paid');
|
|
217
|
+
assert.equal(g.tasks.find((t) => t.id === '500').estimate, null, 'a settled task carries no estimate');
|
|
218
|
+
assert.equal(g.tasks.find((t) => t.id === '501').estimate, 40, 'live work keeps its estimate, apart from the total');
|
|
219
|
+
const SKY = p.SKY();
|
|
220
|
+
SKY.pick({ kind: 'task', ref: g.tasks.find((t) => t.id === '501') }, false);
|
|
221
|
+
assert.match(p.panel(), /40 <span class="lbl">Est\.<\/span>/, 'the panel labels a live task\'s figure as an estimate');
|
|
222
|
+
assert.doesNotMatch(p.panel(), /credits paid/);
|
|
223
|
+
SKY.pick({ kind: 'task', ref: g.tasks.find((t) => t.id === '500') }, false);
|
|
224
|
+
assert.match(p.panel(), /credits paid[\s\S]*?6/, 'and a settled task\'s as what it was paid');
|
|
225
|
+
assert.doesNotMatch(p.panel(), /Est\./);
|
|
215
226
|
assert.equal(g.tasks.find((t) => t.id === '500').ci, 0, 'a task knows which ring it gates');
|
|
216
227
|
assert.equal(g.tasks.find((t) => t.id === '500').sourceRef, '10', 'and which thought it was born of');
|
|
217
228
|
});
|
|
@@ -230,8 +241,9 @@ await test('abandoned work is listed at zero and adds nothing to a well or its r
|
|
|
230
241
|
const g = p.S().goalById.get('7');
|
|
231
242
|
assert.equal(g.tasks.find((t) => t.id === '503').credits, 0, 'still listed, at zero');
|
|
232
243
|
assert.equal(g.tasks.find((t) => t.id === '504').credits, 0, 'a merged-away task is listed at zero too');
|
|
233
|
-
assert.equal(g.credits,
|
|
234
|
-
same(g.crit.map((c) => c.credits), [
|
|
244
|
+
assert.equal(g.credits, 6, 'the well\'s mass is what was paid only');
|
|
245
|
+
same(g.crit.map((c) => c.credits), [6, 0], 'and the ring it sat on is not thickened by it');
|
|
246
|
+
assert.equal(g.tasks.find((t) => t.id === '503').estimate, null, 'abandoned work carries no estimate either');
|
|
235
247
|
});
|
|
236
248
|
|
|
237
249
|
await test('a private goal keeps its structure and none of its prose', async () => {
|
|
@@ -360,6 +360,30 @@ try {
|
|
|
360
360
|
assert.ok(!s.html.classList.contains('st-open'));
|
|
361
361
|
});
|
|
362
362
|
|
|
363
|
+
// task 1004670: the worst-pages report, through the real composer into the
|
|
364
|
+
// real Studio panel: the pages with the most text, most first.
|
|
365
|
+
await test('Most words to write: the worst-pages report, most text first, linked to the editor', async () => {
|
|
366
|
+
const T = (key, text) => ({ key, section: 's', text, placement: 'placed' });
|
|
367
|
+
const readings = { pages: INV.pages.map((p, i) => ({ id: p.id, reading_hash: `r${i}`, lines: Array.from({ length: i + 2 }, (_, k) => T(`L${k}`, `${p.id} line ${k}`)) })) };
|
|
368
|
+
const pagesWithReadings = ps.composePages({ inventory: INV, readings, tasks: TASKS });
|
|
369
|
+
assert.ok(pagesWithReadings.worst_pages.length >= 1);
|
|
370
|
+
const top = pagesWithReadings.worst_pages[0];
|
|
371
|
+
assert.equal(top.page_id, INV.pages[INV.pages.length - 1].id, 'the page with the most text leads');
|
|
372
|
+
const rows = lib.worstRows(pagesWithReadings);
|
|
373
|
+
assert.equal(rows[0].size, `${top.text_lines} lines of text`);
|
|
374
|
+
const s = bootStudio({ answers: { ...HOME, 'GET /copy-desk/pages': ok(pagesWithReadings) } });
|
|
375
|
+
await s.settle();
|
|
376
|
+
await s.click('st-status');
|
|
377
|
+
const body = s.el('st-panel-body').innerHTML;
|
|
378
|
+
assert.match(body, /Most words to write/);
|
|
379
|
+
assert.match(body, new RegExp(`${top.text_lines} lines of text`));
|
|
380
|
+
assert.match(body, new RegExp(`href="/builders/tweak-editor\\?page=${top.page_id}"`));
|
|
381
|
+
const plain = bootStudio({ answers: HOME });
|
|
382
|
+
await plain.settle();
|
|
383
|
+
await plain.click('st-status');
|
|
384
|
+
assert.doesNotMatch(plain.el('st-panel-body').innerHTML, /Most words to write/, 'no readings, no report');
|
|
385
|
+
});
|
|
386
|
+
|
|
363
387
|
await test('the studio\'s words: a first click offers the studio page, and nothing is taken until the artist says so', async () => {
|
|
364
388
|
const s = bootStudio({ answers: { ...HOME, 'GET /copy-desk/pages/builders:studio/draft': ok(studioRead({ held: false })) } });
|
|
365
389
|
await s.settle();
|
|
@@ -136,14 +136,27 @@ function loadBrandApply(rewardBlock) {
|
|
|
136
136
|
const SHIPPED_TASK = { status: 'shipped', credits_awarded: 0, credits_reward: 60 };
|
|
137
137
|
const READY_TASK = { status: 'ready', credits_reward: 60 };
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
// task 1004781 — a settled task shows what it was PAID, never its estimate:
|
|
140
|
+
// the 60 is not a payout, so a shipped task with nothing in the ledger is 0,
|
|
141
|
+
// and that 0 is an actual figure (plain label). Only live work is "Est.".
|
|
142
|
+
test('hall: cost-plus-only shows a SHIPPED task as what it paid (0), not the 60 estimate', () => {
|
|
140
143
|
const B = loadBrandApply(CPO);
|
|
141
|
-
assert.equal(B.
|
|
142
|
-
|
|
144
|
+
assert.equal(B.taskCreditValue(SHIPPED_TASK), 0, 'the estimate must never stand in for a payout');
|
|
145
|
+
assert.equal(B.taskCreditLabel(SHIPPED_TASK), 'credits');
|
|
146
|
+
assert.equal(B.taskCreditValue({ ...SHIPPED_TASK, credits_awarded: 7 }), 7, 'a real payout shows as itself');
|
|
147
|
+
assert.equal(B.taskCreditValue(READY_TASK), 60, 'live work keeps its estimate');
|
|
143
148
|
assert.equal(B.taskCreditLabel(READY_TASK), 'credits Est.');
|
|
144
149
|
assert.equal(B.perTaskCredit(), false);
|
|
145
150
|
});
|
|
146
151
|
|
|
152
|
+
test('hall: a settled task never falls back to its estimate, on any reward mode', () => {
|
|
153
|
+
for (const mode of [CPO, PAYS, undefined]) {
|
|
154
|
+
const B = loadBrandApply(mode);
|
|
155
|
+
assert.equal(B.taskCreditValue(SHIPPED_TASK), 0);
|
|
156
|
+
assert.equal(B.taskCreditValue({ status: 'confirmed', credits_reward: 30 }), 0, 'no ledger figure ⇒ 0');
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
147
160
|
test('hall: a PAYING instance keeps the original settled/unsettled split', () => {
|
|
148
161
|
const B = loadBrandApply(PAYS);
|
|
149
162
|
assert.equal(B.taskCreditLabel(SHIPPED_TASK), 'credits', 'settled ⇒ plain label, unchanged');
|
package/tests/sky_read.mjs
CHANGED
|
@@ -56,14 +56,14 @@ await test('the shape: wells carry their criteria and their compact tasks; edges
|
|
|
56
56
|
assert.equal(w1.created_at, '2026-09-01T00:00:00.000Z');
|
|
57
57
|
// task 1004402: each ring also carries its UAT state + backend-only flag (ADR 0351).
|
|
58
58
|
assert.deepEqual(w1.criteria, [{ id: '10', criterion_id: 'slug-10', criterion_md: 'Criterion 10', satisfied: false, backend_only: false, task_ids: ['100', '101'], pending_review: false, uat_state: 'open' }]);
|
|
59
|
-
assert.deepEqual(w1.tasks[0], { id: '100', title: 'Task 100', status: 'shipped', credits_reward: 30, created_on: '2026-09-02', idea_id: '555' });
|
|
59
|
+
assert.deepEqual(w1.tasks[0], { id: '100', title: 'Task 100', status: 'shipped', credits_reward: 30, credits_awarded: 0, created_on: '2026-09-02', idea_id: '555' });
|
|
60
60
|
assert.equal(w1.tasks[1].idea_id, null, 'a task not born of an idea carries null, not an empty string');
|
|
61
61
|
assert.deepEqual(out.goals[1].criteria[0].task_ids, [], 'a criterion with no linked task is an empty ring, not a missing one');
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
await test('nothing but the compact fields rides on a task — no description, no value summary, no notes', () => {
|
|
65
65
|
const out = shapeSky({ goals: [g(1)], extras: [x(1)], tasks: [t(1, 1, { description: 'long prose', value_summary: 'more prose', notes: 'and more' })] });
|
|
66
|
-
assert.deepEqual(Object.keys(out.goals[0].tasks[0]).sort(), ['created_on', 'credits_reward', 'id', 'idea_id', 'status', 'title']);
|
|
66
|
+
assert.deepEqual(Object.keys(out.goals[0].tasks[0]).sort(), ['created_on', 'credits_awarded', 'credits_reward', 'id', 'idea_id', 'status', 'title']);
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
await test('a ring or a task under a goal that is not a well is dropped, not drawn to nowhere', () => {
|
|
@@ -177,6 +177,17 @@ await test('the criterion→task join is read ONCE for every goal-bearing criter
|
|
|
177
177
|
assert.match(crit[0].sql, /LEFT JOIN tasks t\s+ON t\.id = tc\.task_id/, 'a criterion with no task still arrives (the rollup\'s LEFT JOIN)');
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
+
// task 1004781 — the sky sizes wells by what the ledger PAID, so the read
|
|
181
|
+
// carries the payout (the shared subquery, settled-only) beside the estimate.
|
|
182
|
+
await test('a task carries what the ledger paid for it, from the shared payout subquery', async () => {
|
|
183
|
+
const out = shapeSky({ goals: [g(1)], extras: [x(1)], tasks: [t(100, 1, { status: 'shipped', credits_awarded: '7' })] });
|
|
184
|
+
assert.equal(out.goals[0].tasks[0].credits_awarded, 7);
|
|
185
|
+
const pool = fakePool();
|
|
186
|
+
await skyRows({}, { pool });
|
|
187
|
+
const tq = pool.calls.find((c) => /FROM tasks t\s+JOIN goals g/.test(c.sql));
|
|
188
|
+
assert.match(tq.sql, /FROM credit_log cl[\s\S]*AS credits_awarded/);
|
|
189
|
+
});
|
|
190
|
+
|
|
180
191
|
await test('the task query is compact by construction: no description, no value_summary, and the date is truncated in SQL', async () => {
|
|
181
192
|
const pool = fakePool();
|
|
182
193
|
await skyRows({}, { pool });
|