@bongos/core 1.21.171 → 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 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.171",
6
- "core_contract": "1.21.171",
7
- "source_commit": "418b7346b2799fc4dabcb37cdec6ee5e35bfe89d",
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:36:25.226Z",
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": "5c91b3622476a3b85881740bd79ae9654ed7c2580f183150aa266e127b023ede",
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": "6342b5c569de415d86db07c9e557eee201292149a4fe69d65bbee1b66eee2a70"
2415
+ "sha256": "eec10b588ffe284a8e52e0f5e167b24ffee253b336454d980906d1d299f789c2"
2416
2416
  },
2417
2417
  {
2418
2418
  "path": "docs/copy-registry.json",
2419
2419
  "mode": "0000644",
2420
- "sha256": "e2c97496bcaef11d81aba576f2dc6d50128ec1f04a5023ef02d7a67aabce07a0"
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": "681bd3bde29fddb39088d942d359acd61ebf692b57b2e1bdd46ab9317a25b80a"
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": "57d09e6a2963858978e3028ddf8335d2712d1cad381264340fd51036cfade08c"
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": "bf482381365aac38d983048f87597cc7d498ff320915502e4d5cb8ac3387d3e4"
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": "d82db6db97b22a88a83b4cf9bac7724a411dc7831b08cacd5548c0ac367ad3b9"
4555
+ "sha256": "29faf19e0c0c99cb749a048d4fb73c33dfdd9e363ee46fa6e00465b286cfb096"
4556
4556
  },
4557
4557
  {
4558
4558
  "path": "modules/copy-desk/tests/copy_page_status.mjs",
@@ -6627,7 +6627,7 @@
6627
6627
  {
6628
6628
  "path": "modules/hall-ui/public/studio-lib.js",
6629
6629
  "mode": "0000644",
6630
- "sha256": "2973743795c05e42824338c8b6ae9dbd046d112ab2bf3c10590d1ce478dba414"
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": "dec03b8694432f5add99b8dea4b304d02813fb29d59e116216bb7ae99cd77b6c"
6645
+ "sha256": "2b1adabd2d219c418ea335ad348668df75cc1f49673df3c5472c4107e8595691"
6646
6646
  },
6647
6647
  {
6648
6648
  "path": "modules/hall-ui/public/studio.states.json",
@@ -10602,12 +10602,12 @@
10602
10602
  {
10603
10603
  "path": "package-lock.json",
10604
10604
  "mode": "0000644",
10605
- "sha256": "829b2b61f5ea654c0e6b28e1a8afb189f1c0144277a36d1296a0d8adab46667b"
10605
+ "sha256": "11e80d0bbd4cbe40847b4b7b3185243caca27ca1eb62a23d16af9244aedf7d5f"
10606
10606
  },
10607
10607
  {
10608
10608
  "path": "package.json",
10609
10609
  "mode": "0000644",
10610
- "sha256": "ddc6efda55ff0a4c999375dd67b7018f906d14104d932d745d607d9b43df9ff4"
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": "f91ffba352e1b52333a16db4f2468e2f32ae7372a14292175b66272f5731dbd2"
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": "12e9bdeef58e00243274a7711fc7a93f21c838698dafeb8a3b7680a572271bfd"
13020
+ "sha256": "d91f9c6725d2c369f3af694a4e9b1ecada83994a2208961414c7814f0bf65b48"
13021
13021
  },
13022
13022
  {
13023
13023
  "path": "src/module-loader/catalog.js",
@@ -15772,7 +15772,7 @@
15772
15772
  {
15773
15773
  "path": "tests/hall_studio_home.mjs",
15774
15774
  "mode": "0000644",
15775
- "sha256": "6b6e7c34151808c223cc6cd95a01eee05038061dfbd71a7aa01500f90db757e3"
15775
+ "sha256": "2f8bb467008b2a8007627a50e7b9432924364688c24f9a2d8ef38f8966034fef"
15776
15776
  },
15777
15777
  {
15778
15778
  "path": "tests/hall_studio_world.mjs",
@@ -4,13 +4,13 @@
4
4
  > Deterministic on a given tree — regenerate and commit rather than hand-editing.
5
5
  > Machine-readable twin: [`copy-registry.json`](copy-registry.json).
6
6
 
7
- **2793 strings** across 3 surfaces · 2374 distinct pieces of copy · 2793 certain, 0 heuristic · 243 shown in more than one place.
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 | 1973 |
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` — 1973 strings
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:960` |
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:959` |
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:738` |
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:835` |
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:906` |
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:836` |
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` |
@@ -3,9 +3,9 @@
3
3
  "generator": "scripts/gds/copy-inventory.js",
4
4
  "goal": 1000074,
5
5
  "counts": {
6
- "strings": 2793,
7
- "unique_ids": 2374,
8
- "certain": 2793,
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": 1973
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": 960,
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": 959,
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": 738,
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": 835,
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": 906,
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": 836,
18449
+ "line": 844,
18441
18450
  "origin": "js-markup",
18442
18451
  "confidence": "certain"
18443
18452
  },
@@ -3179,5 +3179,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
3179
3179
  landed since 1.21.169 with no explicit bump. run 39994339257. (task 1002620)
3180
3180
  1.21.171 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
3181
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)
3182
3184
  ---------------------------------------------------------------------------
3183
3185
  ```
@@ -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": "a072c3fdf156d639",
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},
@@ -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
- 'landing:index', 'builders:ideas', 'status:explore',
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 (surface order), tweaked last', () => {
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);
@@ -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
- return `<ul class="st-surfaces">${rows}</ul>${asked ? `<p class="st-asked">${escapeHtml(asked)}</p>` : ''}${inFlight}`;
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/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.21.171",
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.171",
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.171",
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",
@@ -10100,5 +10100,11 @@
10100
10100
  "id": "1004781",
10101
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
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
+ }
10103
10109
  ]
10104
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.171'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
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');
@@ -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();