@bongos/core 1.19.918 → 1.19.919
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 +53 -28
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +6 -0
- package/clients/bongos-client/index.cjs +6 -0
- package/clients/bongos-client/index.d.ts +8 -0
- package/clients/bongos-client/index.mjs +6 -0
- package/docs/adr/0319-a-removal-verdict-is-a-row-beside-the-question.md +69 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +134 -3
- package/docs/api-reference.md +8 -1
- package/docs/copy-inventory.md +15 -11
- package/docs/copy-registry.json +48 -12
- package/docs/module-api-changelog.md +2 -0
- package/modules/hall-ui/public/goal-inbox.js +102 -0
- package/modules/hall-ui/public/style.css +12 -0
- package/modules/lifecycle/cascade.js +9 -1
- package/modules/lifecycle/migrations/lifecycle_009_removal_verdicts.sql +111 -0
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/removal-proposals.js +259 -0
- package/modules/lifecycle/routes/removal-proposals.js +140 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/hall-preview/refresh-fixtures.js +6 -0
- package/src/module-api.js +1 -1
- package/tests/blockers_hall.mjs +16 -2
- package/tests/removal_verdict.mjs +311 -0
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
|
+
**1832 strings** across 3 surfaces · 1589 distinct pieces of copy · 1832 certain, 0 heuristic · 166 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` | 84 |
|
|
13
|
+
| `builders-hall` | `modules/hall-ui/public` | 84 | 1342 |
|
|
14
14
|
| `status-dashboard` | `modules/status-ui/public` | 4 | 100 |
|
|
15
15
|
| `landing` | `modules/public-landing/public` | 5 | 390 |
|
|
16
16
|
|
|
@@ -78,7 +78,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
78
78
|
| `996edffc1966` | 2x | Access requests | modules/hall-ui/public/watch.html:46, modules/hall-ui/public/watch.html:61 |
|
|
79
79
|
| … | | _126 more in the JSON registry_ | |
|
|
80
80
|
|
|
81
|
-
## `builders-hall` —
|
|
81
|
+
## `builders-hall` — 1342 strings
|
|
82
82
|
|
|
83
83
|
| id | conf | text | where |
|
|
84
84
|
|---|---|---|---|
|
|
@@ -125,6 +125,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
125
125
|
| `5723baf26b2d` | certain | A quick idea is a thought filed fast. It is not an unfinished Full Idea — only its author can offer it up for | `modules/hall-ui/public/idea.js:260` |
|
|
126
126
|
| `23dc5028183a` | certain | A quick report — title, what's going on, and where it belongs. Pick a goal and it lands there as a task the mo | `modules/hall-ui/public/ideas.html:60` |
|
|
127
127
|
| `a1f06e5c11ae` | certain | A quiet place to tend the words and art the project shows people. | `modules/hall-ui/public/studio.html:38` |
|
|
128
|
+
| `08c1f66fd572` | certain | A refactor, a move, or work already superseded | `modules/hall-ui/public/goal-inbox.js:336` |
|
|
128
129
|
| `244ea2a3564e` | certain | A role says how you work. A speciality says what you work on — and how you, as someone who knows the subject, | `modules/hall-ui/public/settings.html:253` |
|
|
129
130
|
| `eff6b977b1d2` | certain | A self-drawing map of the codebase: what each module is, and what depends on it. Redrawn from the code on ever | `modules/hall-ui/public/atlas.html:31` |
|
|
130
131
|
| `f3d6ed7c0d2c` | certain | A shipped task cannot be retired — it is immutable history now. | `modules/hall-ui/public/task.js:751` |
|
|
@@ -462,7 +463,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
462
463
|
| `4794a991de3d` | certain | Getting started | `modules/hall-ui/public/hall-widgets.js:59` |
|
|
463
464
|
| `a28b1a567f1a` | certain | GitHub username | `modules/hall-ui/public/hall-render.js:294` |
|
|
464
465
|
| `8e52f0649662` | certain | Give the category a label. | `modules/hall-ui/public/project-settings.js:189` |
|
|
465
|
-
| `79934b879a12` | certain | Give the nod | `modules/hall-ui/public/goal-inbox.js:
|
|
466
|
+
| `79934b879a12` | certain | Give the nod | `modules/hall-ui/public/goal-inbox.js:258` |
|
|
466
467
|
| `988e2c60df3d` | certain | Goal archived. | `modules/hall-ui/public/goals-render.js:1048` |
|
|
467
468
|
| `9e70797b7e97` | certain | Goal id | `modules/hall-ui/public/goals-render.js:830` |
|
|
468
469
|
| `529460e82058` | certain | Goal invitations to you | `modules/hall-ui/public/goal-inbox.js:113` |
|
|
@@ -621,7 +622,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
621
622
|
| `31e91ee6ce03` | certain | Name must be {…} characters or fewer. | `modules/hall-ui/public/settings.js:766` |
|
|
622
623
|
| `5e8199266a8a` | certain | Name unvouched | `modules/hall-ui/public/watch.js:374` |
|
|
623
624
|
| `3970d299c95a` | certain | Needs work | `modules/hall-ui/public/studio.js:162` |
|
|
624
|
-
| `7def635421ea` | certain | Needs your attention | `modules/hall-ui/public/goal-inbox.js:
|
|
625
|
+
| `7def635421ea` | certain | Needs your attention | `modules/hall-ui/public/goal-inbox.js:557` |
|
|
625
626
|
| `87f09d983f6c` | certain | New category label | `modules/hall-ui/public/project-settings.js:138` |
|
|
626
627
|
| `d427d9dab01c` | certain | New claims are paused: {…} confirmed {…} can't land yet, so {…} reward is pending. Rebase and re-ship, or rele | `modules/hall-ui/public/hall-render.js:674` |
|
|
627
628
|
| `7fcc2583c372` | certain | New custom rank | `modules/hall-ui/public/government.html:79` |
|
|
@@ -952,6 +953,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
952
953
|
| `282c3fa2d456` | certain | That is the wording it already has. Change something, or file a flag instead if you only want to mark it. | `modules/hall-ui/public/copy-desk.js:438` |
|
|
953
954
|
| `c0e28f30524d` | certain | That project's own standing, as reported by that project. | `modules/hall-ui/public/profile.js:314` |
|
|
954
955
|
| `e99d4a811e92` | certain | That request is no longer available. | `modules/hall-ui/public/profile-connections.js:270` |
|
|
956
|
+
| `34017119ce22` | certain | That work is genuinely gone | `modules/hall-ui/public/goal-inbox.js:336` |
|
|
955
957
|
| `6e5a324b2972` | certain | That's you | `modules/hall-ui/public/watch.js:803` |
|
|
956
958
|
| `50198c465225` | certain | The 16-value rank enum in three bands, which ranks are live, and the permissions each rank holds. | `modules/hall-ui/public/diagrams.html:59` |
|
|
957
959
|
| `c5e6bdd32fe3` | certain | The Board Room is open to the builders this project’s constitution seats on its board. | `modules/hall-ui/public/board-room.js:436` |
|
|
@@ -1266,7 +1268,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1266
1268
|
| `99f88f2af2ee` | certain | of work — a tangent, an investigation, an unprompted suggestion — instead of staying on your claimed task. It | `modules/hall-ui/public/settings.html:316` |
|
|
1267
1269
|
| `5e7a2220c6a9` | certain | of {…} | `modules/hall-ui/public/roadmap.js:288` |
|
|
1268
1270
|
| `759c12b5f983` | certain | oldest first | `modules/hall-ui/public/hall-widgets.js:134` |
|
|
1269
|
-
| `cbaf58cdbe35` | certain | open the queue | `modules/hall-ui/public/goal-inbox.js:
|
|
1271
|
+
| `cbaf58cdbe35` | certain | open the queue | `modules/hall-ui/public/goal-inbox.js:521` |
|
|
1270
1272
|
| `af3a8e0fe6a8` | certain | open your goals | `modules/hall-ui/public/goal-inbox.js:121` |
|
|
1271
1273
|
| `4f279d68175f` | certain | open, on the Security tab | `modules/hall-ui/public/watch.html:47` |
|
|
1272
1274
|
| `7b85e6494596` | certain | opens a new tab | `modules/hall-ui/public/palette.js:190` |
|
|
@@ -1375,7 +1377,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1375
1377
|
| `b3757ba3bb46` | certain | {…} · Stored encrypted; never shown again after saving. | `modules/hall-ui/public/settings.js:990` |
|
|
1376
1378
|
| `a6e4ed98fbea` | certain | {…} · revoke it any time under Active sessions. | `modules/hall-ui/public/settings.js:922` |
|
|
1377
1379
|
| `d73256fbc395` | certain | {…} · who votes, and how it {…} | `modules/hall-ui/public/board-room.js:357` |
|
|
1378
|
-
| `cab2a4296aec` | certain | {…} · {…} · {…}{…} | `modules/hall-ui/public/goal-inbox.js:
|
|
1380
|
+
| `cab2a4296aec` | certain | {…} · {…} · {…}{…} | `modules/hall-ui/public/goal-inbox.js:420` |
|
|
1379
1381
|
| `085f93f3f775` | certain | {…} — see all standings | `modules/hall-ui/public/hall-render.js:80` |
|
|
1380
1382
|
| `f75ee96a7511` | certain | {…} — total payout from shipped work. | `modules/hall-ui/public/profile.js:94` |
|
|
1381
1383
|
| `2a2728a4ec4f` | certain | {…}% of tasks complete | `modules/hall-ui/public/roadmap.js:63` |
|
|
@@ -1387,6 +1389,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1387
1389
|
| `499916045b19` | certain | {…}{…} asks to join | `modules/hall-ui/public/goal-inbox.js:69` |
|
|
1388
1390
|
| `97e27ab169ae` | certain | {…}{…} session{…} across {…} builder{…}. | `modules/hall-ui/public/sessions.js:338` |
|
|
1389
1391
|
| `3d74668f69e5` | certain | {…}{…} · {…} | `modules/hall-ui/public/task.js:653` |
|
|
1392
|
+
| `7f79bedecf60` | certain | {…}{…} — open it to see what went | `modules/hall-ui/public/goal-inbox.js:336` |
|
|
1390
1393
|
| `9588652a7aff` | certain | · follows your profile setting | `modules/hall-ui/public/settings.js:566` |
|
|
1391
1394
|
| `4a5ff4f662df` | certain | — ${…} of ${…} this month ({…}%). {…} | `modules/hall-ui/public/hall-render.js:517` |
|
|
1392
1395
|
| `a6ae9cbdee30` | certain | — all builders — | `modules/hall-ui/public/builders.js:713` |
|
|
@@ -1396,14 +1399,15 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1396
1399
|
| `da9962dc1729` | certain | — or open any diagram fullscreen. | `modules/hall-ui/public/diagrams.html:33` |
|
|
1397
1400
|
| `e5c5e94152b3` | certain | — that's all the tooling you need up front. | `modules/hall-ui/public/hall-render.js:890` |
|
|
1398
1401
|
| `392afc533191` | certain | — type | `modules/hall-ui/public/hall-render.js:825` |
|
|
1399
|
-
| `ff7514f48a7b` | certain | — untouched {…}+ days | `modules/hall-ui/public/goal-inbox.js:
|
|
1402
|
+
| `ff7514f48a7b` | certain | — untouched {…}+ days | `modules/hall-ui/public/goal-inbox.js:457` |
|
|
1400
1403
|
| `d3afa6d6dd9e` | certain | — your words, in your voice. Needed for a note or a follow-up. | `modules/hall-ui/public/studio.js:162` |
|
|
1401
1404
|
| `68b547166d48` | certain | — {…} confirmed {…} can't land yet, so {…} reward is pending (it lands on ship). Rebase + re-ship or release { | `modules/hall-ui/public/hall-render.js:558` |
|
|
1402
1405
|
| `5b9fa7cb4fb0` | certain | …and {…} more waiting — | `modules/hall-ui/public/goal-inbox.js:121` |
|
|
1403
|
-
| `b2a04abfead0` | certain | …{…} more awaiting a nod. | `modules/hall-ui/public/goal-inbox.js:
|
|
1404
|
-
| `
|
|
1406
|
+
| `b2a04abfead0` | certain | …{…} more awaiting a nod. | `modules/hall-ui/public/goal-inbox.js:278` |
|
|
1407
|
+
| `f16717b3e122` | certain | …{…} more awaiting an answer. | `modules/hall-ui/public/goal-inbox.js:358` |
|
|
1408
|
+
| `616fb9adf3cf` | certain | …{…} more going quiet. | `modules/hall-ui/public/goal-inbox.js:447` |
|
|
1405
1409
|
| `e35f11bdc5d7` | certain | …{…} more open ideas — triage clears them one at a time. | `modules/hall-ui/public/builders.js:895` |
|
|
1406
|
-
| `5f1d914ad45e` | certain | …{…} more — | `modules/hall-ui/public/goal-inbox.js:
|
|
1410
|
+
| `5f1d914ad45e` | certain | …{…} more — | `modules/hall-ui/public/goal-inbox.js:521` |
|
|
1407
1411
|
| `de8fa5223aee` | certain | ‹ Prev | `modules/hall-ui/public/hall-kit.js:74` |
|
|
1408
1412
|
| `d4b84b7fbdeb` | certain | ← All goals | `modules/hall-ui/public/goals.html:80` |
|
|
1409
1413
|
| `7926e30955d2` | certain | ← Back to goal | `modules/hall-ui/public/goals.html:184` |
|
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": 1832,
|
|
7
|
+
"unique_ids": 1589,
|
|
8
|
+
"certain": 1832,
|
|
9
9
|
"heuristic": 0,
|
|
10
10
|
"duplicate_ids": 166
|
|
11
11
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
15
|
"files": 84,
|
|
16
|
-
"strings":
|
|
16
|
+
"strings": 1342
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -2164,6 +2164,15 @@
|
|
|
2164
2164
|
"origin": "html-text",
|
|
2165
2165
|
"confidence": "certain"
|
|
2166
2166
|
},
|
|
2167
|
+
{
|
|
2168
|
+
"id": "08c1f66fd572",
|
|
2169
|
+
"surface": "builders-hall",
|
|
2170
|
+
"text": "A refactor, a move, or work already superseded",
|
|
2171
|
+
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
2172
|
+
"line": 336,
|
|
2173
|
+
"origin": "js-markup",
|
|
2174
|
+
"confidence": "certain"
|
|
2175
|
+
},
|
|
2167
2176
|
{
|
|
2168
2177
|
"id": "244ea2a3564e",
|
|
2169
2178
|
"surface": "builders-hall",
|
|
@@ -5202,7 +5211,7 @@
|
|
|
5202
5211
|
"surface": "builders-hall",
|
|
5203
5212
|
"text": "Give the nod",
|
|
5204
5213
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
5205
|
-
"line":
|
|
5214
|
+
"line": 258,
|
|
5206
5215
|
"origin": "js-markup",
|
|
5207
5216
|
"confidence": "certain"
|
|
5208
5217
|
},
|
|
@@ -6633,7 +6642,7 @@
|
|
|
6633
6642
|
"surface": "builders-hall",
|
|
6634
6643
|
"text": "Needs your attention",
|
|
6635
6644
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
6636
|
-
"line":
|
|
6645
|
+
"line": 557,
|
|
6637
6646
|
"origin": "js-markup",
|
|
6638
6647
|
"confidence": "certain"
|
|
6639
6648
|
},
|
|
@@ -9607,6 +9616,15 @@
|
|
|
9607
9616
|
"origin": "js-notice",
|
|
9608
9617
|
"confidence": "certain"
|
|
9609
9618
|
},
|
|
9619
|
+
{
|
|
9620
|
+
"id": "34017119ce22",
|
|
9621
|
+
"surface": "builders-hall",
|
|
9622
|
+
"text": "That work is genuinely gone",
|
|
9623
|
+
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
9624
|
+
"line": 336,
|
|
9625
|
+
"origin": "js-markup",
|
|
9626
|
+
"confidence": "certain"
|
|
9627
|
+
},
|
|
9610
9628
|
{
|
|
9611
9629
|
"id": "6e5a324b2972",
|
|
9612
9630
|
"surface": "builders-hall",
|
|
@@ -12438,7 +12456,7 @@
|
|
|
12438
12456
|
"surface": "builders-hall",
|
|
12439
12457
|
"text": "open the queue",
|
|
12440
12458
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
12441
|
-
"line":
|
|
12459
|
+
"line": 521,
|
|
12442
12460
|
"origin": "js-markup",
|
|
12443
12461
|
"confidence": "certain"
|
|
12444
12462
|
},
|
|
@@ -13419,7 +13437,7 @@
|
|
|
13419
13437
|
"surface": "builders-hall",
|
|
13420
13438
|
"text": "{…} · {…} · {…}{…}",
|
|
13421
13439
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13422
|
-
"line":
|
|
13440
|
+
"line": 420,
|
|
13423
13441
|
"origin": "js-markup",
|
|
13424
13442
|
"confidence": "certain"
|
|
13425
13443
|
},
|
|
@@ -13522,6 +13540,15 @@
|
|
|
13522
13540
|
"origin": "js-markup",
|
|
13523
13541
|
"confidence": "certain"
|
|
13524
13542
|
},
|
|
13543
|
+
{
|
|
13544
|
+
"id": "7f79bedecf60",
|
|
13545
|
+
"surface": "builders-hall",
|
|
13546
|
+
"text": "{…}{…} — open it to see what went",
|
|
13547
|
+
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13548
|
+
"line": 336,
|
|
13549
|
+
"origin": "js-markup",
|
|
13550
|
+
"confidence": "certain"
|
|
13551
|
+
},
|
|
13525
13552
|
{
|
|
13526
13553
|
"id": "9588652a7aff",
|
|
13527
13554
|
"surface": "builders-hall",
|
|
@@ -13608,7 +13635,7 @@
|
|
|
13608
13635
|
"surface": "builders-hall",
|
|
13609
13636
|
"text": "— untouched {…}+ days",
|
|
13610
13637
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13611
|
-
"line":
|
|
13638
|
+
"line": 457,
|
|
13612
13639
|
"origin": "js-markup",
|
|
13613
13640
|
"confidence": "certain"
|
|
13614
13641
|
},
|
|
@@ -13644,7 +13671,16 @@
|
|
|
13644
13671
|
"surface": "builders-hall",
|
|
13645
13672
|
"text": "…{…} more awaiting a nod.",
|
|
13646
13673
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13647
|
-
"line":
|
|
13674
|
+
"line": 278,
|
|
13675
|
+
"origin": "js-markup",
|
|
13676
|
+
"confidence": "certain"
|
|
13677
|
+
},
|
|
13678
|
+
{
|
|
13679
|
+
"id": "f16717b3e122",
|
|
13680
|
+
"surface": "builders-hall",
|
|
13681
|
+
"text": "…{…} more awaiting an answer.",
|
|
13682
|
+
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13683
|
+
"line": 358,
|
|
13648
13684
|
"origin": "js-markup",
|
|
13649
13685
|
"confidence": "certain"
|
|
13650
13686
|
},
|
|
@@ -13653,7 +13689,7 @@
|
|
|
13653
13689
|
"surface": "builders-hall",
|
|
13654
13690
|
"text": "…{…} more going quiet.",
|
|
13655
13691
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13656
|
-
"line":
|
|
13692
|
+
"line": 447,
|
|
13657
13693
|
"origin": "js-markup",
|
|
13658
13694
|
"confidence": "certain"
|
|
13659
13695
|
},
|
|
@@ -13671,7 +13707,7 @@
|
|
|
13671
13707
|
"surface": "builders-hall",
|
|
13672
13708
|
"text": "…{…} more —",
|
|
13673
13709
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13674
|
-
"line":
|
|
13710
|
+
"line": 521,
|
|
13675
13711
|
"origin": "js-markup",
|
|
13676
13712
|
"confidence": "certain"
|
|
13677
13713
|
},
|
|
@@ -2310,5 +2310,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2310
2310
|
landed since 1.19.916 with no explicit bump. run 35526196428. (task 1002620)
|
|
2311
2311
|
1.19.918 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2312
2312
|
landed since 1.19.917 with no explicit bump. run 35529342372. (task 1002620)
|
|
2313
|
+
1.19.919 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2314
|
+
landed since 1.19.918 with no explicit bump. run 35531198454. (task 1002620)
|
|
2313
2315
|
---------------------------------------------------------------------------
|
|
2314
2316
|
```
|
|
@@ -191,6 +191,20 @@
|
|
|
191
191
|
return;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
// task 1004095 (BV2.R16): confirming or dismissing a removal proposal. Like
|
|
195
|
+
// the two branches above it refreshes ITS OWN slice only — repainting the
|
|
196
|
+
// goals list would leave the answered row sitting there still asking (the
|
|
197
|
+
// task-1003066 lesson). The verdict is server-declared on the button rather
|
|
198
|
+
// than inferred from which one was pressed, so this branch needs to know
|
|
199
|
+
// nothing about the vocabulary beyond what to say afterwards.
|
|
200
|
+
const removal = e.target.closest('[data-removal-task]');
|
|
201
|
+
if (removal) {
|
|
202
|
+
const verdict = removal.dataset.removalVerdict;
|
|
203
|
+
act(() => postJSON(`${API}/removal-proposals/${encodeURIComponent(removal.dataset.removalTask)}/decide`, { verdict }),
|
|
204
|
+
REMOVAL_TOASTS[verdict] || 'Answered.', loadRemovals);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
194
208
|
const item = e.target.closest('.ginbox__item');
|
|
195
209
|
const goalId = item && item.dataset.goal;
|
|
196
210
|
if (!goalId) return;
|
|
@@ -275,6 +289,86 @@
|
|
|
275
289
|
.catch(() => { /* a non-triager's 403, or a hiccup: never break the hall */ });
|
|
276
290
|
}
|
|
277
291
|
|
|
292
|
+
// ---- removal proposals (task 1004095, BV2.R16) ----------------------------
|
|
293
|
+
//
|
|
294
|
+
// A ship that deleted files an earlier SHIPPED task put there files a QUESTION,
|
|
295
|
+
// never a verdict (task 1004090, ADR 0313): refactors, renames and file moves
|
|
296
|
+
// all look like deletions to a diff, so nothing is marked removed until a person
|
|
297
|
+
// says so. This slice is where they say so.
|
|
298
|
+
//
|
|
299
|
+
// WHY IT IS NOT A ROW IN THE NOD QUEUE, which is the obvious place. That queue is
|
|
300
|
+
// scoped to ROUTED FILINGS — `origin='route'` in ideas_task_lineage — and ADR 0299
|
|
301
|
+
// decided, on live numbers, that it stays that way and is honestly named for that
|
|
302
|
+
// population. A removal proposal has no idea lineage at all. Same CARD (the
|
|
303
|
+
// anti-accretion rule at the top of this file), same floor, separate slice.
|
|
304
|
+
//
|
|
305
|
+
// THE ANSWER IS NOT IN THIS ROW, and that is deliberate. Which files went and
|
|
306
|
+
// whose work they were is prose in the proposal's body, because the cascade that
|
|
307
|
+
// files it has no field for a target (ADR 0242). So the title LINKS to the task
|
|
308
|
+
// and the buttons sit beside it: a person who has not read the evidence should
|
|
309
|
+
// have to take one more step, not one fewer.
|
|
310
|
+
//
|
|
311
|
+
// No client-side rank gate, for the same reason the nod slice has none: GET
|
|
312
|
+
// /removal-proposals is Metic+ server-side, so a non-triager's fetch 403s and the
|
|
313
|
+
// slice renders nothing. One gate, on the server.
|
|
314
|
+
// What each answer says when it lands. Keyed by the SERVER's verdict name, the
|
|
315
|
+
// ROT_TOASTS convention, so a verdict added later degrades to a neutral
|
|
316
|
+
// confirmation instead of a blank toast. The wording states the CONSEQUENCE,
|
|
317
|
+
// because that is the half a person cannot see from the row they just cleared.
|
|
318
|
+
const REMOVAL_TOASTS = {
|
|
319
|
+
confirmed: 'Confirmed — the ideas behind that work will dim.',
|
|
320
|
+
dismissed: 'Dismissed — nothing dims.',
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// THE LABELS ARE ONE WORD EACH BECAUSE THE ROW IS A FLEX LINE AT EVERY WIDTH.
|
|
324
|
+
// `.ginbox__actions` is `flex: 0 0 auto` and nothing in the card wraps, so two
|
|
325
|
+
// buttons take their width straight off the body’s. Measured at 375px:
|
|
326
|
+
// "Confirm removal" + "Dismiss" left the title 46px wide and 440px tall — a
|
|
327
|
+
// column of single letters — where every other row on this card keeps 138–218px.
|
|
328
|
+
// One word each brings it back into that band; the sentence each answer means
|
|
329
|
+
// rides the `title`, and the head already says what the question is.
|
|
330
|
+
function removalRowHtml(r) {
|
|
331
|
+
const title = escapeHtml(r.title || `task #${r.proposal_task_id}`);
|
|
332
|
+
const from = r.ship_task_id
|
|
333
|
+
? `deleted by task #${escapeHtml(String(r.ship_task_id))}`
|
|
334
|
+
: 'from a ship whose id could not be read';
|
|
335
|
+
const goal = r.goal_title ? ` · ${escapeHtml(r.goal_title)}` : '';
|
|
336
|
+
return `<li class="ginbox__item ginbox__item--stacked">
|
|
337
|
+
<div class="ginbox__body">
|
|
338
|
+
<a class="ginbox__goal" href="/task/${encodeURIComponent(r.proposal_task_id)}">${title}</a>
|
|
339
|
+
<span class="ginbox__sub">${from}${goal} — open it to see what went</span>
|
|
340
|
+
</div>
|
|
341
|
+
<div class="ginbox__actions">
|
|
342
|
+
<button class="btn-ghost" type="button" data-removal-task="${escapeHtml(String(r.proposal_task_id))}"
|
|
343
|
+
data-removal-verdict="confirmed" title="That work is genuinely gone">Confirm</button>
|
|
344
|
+
<button class="btn-ghost" type="button" data-removal-task="${escapeHtml(String(r.proposal_task_id))}"
|
|
345
|
+
data-removal-verdict="dismissed" title="A refactor, a move, or work already superseded">Dismiss</button>
|
|
346
|
+
</div>
|
|
347
|
+
</li>`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function renderRemovals(data) {
|
|
351
|
+
const box = host && host.querySelector('#attention-removals');
|
|
352
|
+
if (!box) return;
|
|
353
|
+
const rows = (data && Array.isArray(data.pending) ? data.pending : []);
|
|
354
|
+
const count = data && data.count != null ? data.count : rows.length;
|
|
355
|
+
if (!rows.length) {
|
|
356
|
+
box.innerHTML = '';
|
|
357
|
+
} else {
|
|
358
|
+
const more = count > 5 ? `<p class="ginbox__pending">…${count - 5} more awaiting an answer.</p>` : '';
|
|
359
|
+
box.innerHTML = '<h3 class="ginbox__sec-h">Was this removed? '
|
|
360
|
+
+ `<span class="count-pill">${count}</span></h3>`
|
|
361
|
+
+ `<ul class="ginbox__list">${rows.slice(0, 5).map(removalRowHtml).join('')}</ul>${more}`;
|
|
362
|
+
}
|
|
363
|
+
window.OTBAttention?.refresh();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function loadRemovals() {
|
|
367
|
+
return getJSON(`${API}/removal-proposals`)
|
|
368
|
+
.then((data) => renderRemovals(data))
|
|
369
|
+
.catch(() => { /* a non-triager's 403, or an older server: never break the hall */ });
|
|
370
|
+
}
|
|
371
|
+
|
|
278
372
|
// ---- the rot queue (task 1003279, goal 1000072) ---------------------------
|
|
279
373
|
//
|
|
280
374
|
// "Untouched-for-X triggers a configured action rather than a silent decay."
|
|
@@ -484,6 +578,13 @@
|
|
|
484
578
|
// triage. Its rows carry the shared .ginbox__item class, so refresh()
|
|
485
579
|
// counts them with no change to the selector below.
|
|
486
580
|
'<div id="attention-nod"></div>' +
|
|
581
|
+
// task 1004095 (BV2.R16): removal proposals — a ship deleted files an
|
|
582
|
+
// earlier shipped task put there, and somebody has to say whether that work
|
|
583
|
+
// is genuinely gone or it was a refactor. Straight after nod because it is
|
|
584
|
+
// the same act at the same floor: a Metic+ answering a question the system
|
|
585
|
+
// filed, inside a goal. Before blockers because a blocker is waiting on the
|
|
586
|
+
// owner specifically, while this is waiting on whoever looks first.
|
|
587
|
+
'<div id="attention-removals"></div>' +
|
|
487
588
|
// task 1003455 (goal 1000089): open blockers — things stuck on the owner
|
|
488
589
|
// (modules/ideas/blockers.js). AFTER nod, BEFORE rot: like every slice
|
|
489
590
|
// above it, a blocker is genuinely waiting on a human, but unlike them it
|
|
@@ -499,6 +600,7 @@
|
|
|
499
600
|
host.addEventListener('click', onClick);
|
|
500
601
|
load();
|
|
501
602
|
loadNodQueue();
|
|
603
|
+
loadRemovals();
|
|
502
604
|
loadBlockers();
|
|
503
605
|
loadRot();
|
|
504
606
|
// task 1002802 (P06): the profile slice lives in its own file (builders.js is
|
|
@@ -3032,6 +3032,18 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
|
|
|
3032
3032
|
.ginbox__note { margin: 3px 0 0; font-size: 12.5px; color: var(--ink-soft); font-style: italic; }
|
|
3033
3033
|
.ginbox__actions { flex: 0 0 auto; display: flex; gap: 6px; }
|
|
3034
3034
|
.ginbox__pending { font-size: 12.5px; margin: 10px 0 0; color: var(--ink-soft); }
|
|
3035
|
+
/* task 1004095: a row whose actions are TWO verbs, not one. `.ginbox__actions` is
|
|
3036
|
+
`flex: 0 0 auto` and the card never wraps, so a second button takes its width
|
|
3037
|
+
straight off the title’s — measured at 375px, the removal row’s title fell to
|
|
3038
|
+
96px and 252px tall where every other row on this card keeps 138–218px. The
|
|
3039
|
+
modifier lets the actions drop under the words at phone width instead, which is
|
|
3040
|
+
the card’s own "a phone gets its own treatment, the document never widens"
|
|
3041
|
+
rule. Opt-in: an existing single-verb row is untouched. */
|
|
3042
|
+
@media (max-width: 640px) {
|
|
3043
|
+
.ginbox__item--stacked { flex-wrap: wrap; }
|
|
3044
|
+
.ginbox__item--stacked .ginbox__body { flex: 1 1 100%; }
|
|
3045
|
+
.ginbox__item--stacked .ginbox__actions { margin-top: 6px; }
|
|
3046
|
+
}
|
|
3035
3047
|
/* task 1003464: the system's notices are inbox LINES on Home (rebase owed, an
|
|
3036
3048
|
input the system needs, the monthly budget) — the placement rule, not a
|
|
3037
3049
|
banner inside the Standing card. A row is a kind in the label voice over one
|
|
@@ -113,6 +113,13 @@
|
|
|
113
113
|
// person. Kept as an explicit set rather than inferred, because "is this
|
|
114
114
|
// copy/visual work" is a judgement the rule should state out loud and a test
|
|
115
115
|
// should be able to pin — an inferred predicate is the kind that quietly widens.
|
|
116
|
+
// The removal rule's key, lifted out of its declaration so the SURFACE that
|
|
117
|
+
// answers a removal proposal (task 1004095 / BV2.R16) can find those proposals
|
|
118
|
+
// without hand-typing the string. The key is half of every generated row's
|
|
119
|
+
// `source_ref` (see cascadeSourceRef below), so a reader that re-types it is a
|
|
120
|
+
// reader that silently returns nothing the day the key changes.
|
|
121
|
+
const REMOVAL_RULE_KEY = 'removal-proposed-on-ship';
|
|
122
|
+
|
|
116
123
|
const COPY_VISUAL_MODULES = Object.freeze([
|
|
117
124
|
'copy-desk', // the copy registry + proposals (goal 1000074's own spine)
|
|
118
125
|
'hall-ui', // the builders' hall — the most-seen surface
|
|
@@ -234,7 +241,7 @@ const CASCADES = Object.freeze([
|
|
|
234
241
|
body: artistReviewBody,
|
|
235
242
|
}),
|
|
236
243
|
Object.freeze({
|
|
237
|
-
key:
|
|
244
|
+
key: REMOVAL_RULE_KEY,
|
|
238
245
|
// ENGINEER, not artist: confirming asks whether deleted code is genuinely
|
|
239
246
|
// gone or was moved, which is a question about the diff.
|
|
240
247
|
discipline: 'engineer',
|
|
@@ -567,6 +574,7 @@ async function runCascadesForShip(shipped, deps = {}) {
|
|
|
567
574
|
|
|
568
575
|
module.exports = {
|
|
569
576
|
CASCADES,
|
|
577
|
+
REMOVAL_RULE_KEY,
|
|
570
578
|
removalProposalBody,
|
|
571
579
|
IDEA_CASCADES,
|
|
572
580
|
TRIGGERS,
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
-- lifecycle_009_removal_verdicts.sql — the ANSWER to a removal proposal
|
|
2
|
+
-- (task 1004095 / BV2.R16, goal 1000095; ADR 0313 + ADR 0319).
|
|
3
|
+
--
|
|
4
|
+
-- WHY A TABLE AT ALL, when the proposal is already a task row. Because the two
|
|
5
|
+
-- answers are otherwise indistinguishable. A removal proposal is a question filed
|
|
6
|
+
-- at `backlog` by the `removal-proposed-on-ship` cascade (task 1004090); answering
|
|
7
|
+
-- it either way leaves nothing to build, so either way the task ends `abandoned`.
|
|
8
|
+
-- "Yes, that work is gone" and "no, that was a refactor" would then be the SAME
|
|
9
|
+
-- row, and brightness — the live share of a descent, the next task in this chain —
|
|
10
|
+
-- would have no way to tell the star it must dim from the one it must not. The
|
|
11
|
+
-- verdict is the fact the whole feature exists to produce; it cannot live in a
|
|
12
|
+
-- status that has one spelling for both answers.
|
|
13
|
+
--
|
|
14
|
+
-- WHY NOT A NEW TASK STATUS. ADR 0313 rejected exactly that: `shipped` is terminal
|
|
15
|
+
-- by design and much of the ledger leans on it, so "a removal is a fact about
|
|
16
|
+
-- work's fate, recorded BESIDE it, not a new terminal state that ship rollup would
|
|
17
|
+
-- have to learn". This table is that beside.
|
|
18
|
+
--
|
|
19
|
+
-- WHAT IS DELIBERATELY NOT HERE: the list of tasks whose work was removed. The
|
|
20
|
+
-- cascade files one task per rule per ship with no field for a target (ADR 0242
|
|
21
|
+
-- forbids widening the runner to fan out), so that list travels as prose in the
|
|
22
|
+
-- proposal's body — the owner's decision of 2026-09-20, recorded in
|
|
23
|
+
-- cascade.js:removalProposalBody. Brightness designs its own storage for targets
|
|
24
|
+
-- when it needs one. What this table owes brightness is the VERDICT and a
|
|
25
|
+
-- structured handle on the ship it was about; both are here, and neither is prose.
|
|
26
|
+
--
|
|
27
|
+
-- MODULE-OWNED and `lifecycle_`-prefixed so fitness check 11 permits it (ADR 0083
|
|
28
|
+
-- §Decision #5) — the lifecycle_004..008 convention. It only REFERENCES the core
|
|
29
|
+
-- `tasks` / `builders` tables; a module migration may not ALTER them.
|
|
30
|
+
--
|
|
31
|
+
-- Module migrations are ADDITIVE and NAMESPACED: no down-migration, idempotent
|
|
32
|
+
-- (safe to re-run).
|
|
33
|
+
|
|
34
|
+
BEGIN;
|
|
35
|
+
|
|
36
|
+
CREATE TABLE IF NOT EXISTS lifecycle_removal_verdicts (
|
|
37
|
+
-- The PROPOSAL task — the question that was answered. PRIMARY KEY, not just an
|
|
38
|
+
-- index: one proposal has exactly one answer, ever. A second decide is refused
|
|
39
|
+
-- at the route rather than upserted, because silently overwriting a person's
|
|
40
|
+
-- verdict would lose the only record that they gave a different one.
|
|
41
|
+
proposal_task_id bigint PRIMARY KEY REFERENCES tasks(id) ON DELETE CASCADE,
|
|
42
|
+
|
|
43
|
+
-- The SHIP the proposal was about — the task whose merge deleted the files.
|
|
44
|
+
-- Denormalised out of the proposal's `source_ref`, which encodes it as
|
|
45
|
+
-- `cascade-removal-proposed-on-ship-<id>`: a string nothing can join on, the
|
|
46
|
+
-- same complaint ideas_007 makes about the same field. Nullable because a
|
|
47
|
+
-- proposal whose ref cannot be parsed must still be answerable — the verdict is
|
|
48
|
+
-- the point, and refusing to record it over a malformed ref would be the tail
|
|
49
|
+
-- wagging the dog.
|
|
50
|
+
ship_task_id bigint REFERENCES tasks(id) ON DELETE SET NULL,
|
|
51
|
+
|
|
52
|
+
-- The answer. An ALLOW-LIST, not a deny-list: a value added to the vocabulary
|
|
53
|
+
-- later has to be added here too, rather than being admitted by default.
|
|
54
|
+
-- 'confirmed' — the work is genuinely gone. This is what brightness reads.
|
|
55
|
+
-- 'dismissed' — a refactor, a move the rename detection missed, or work
|
|
56
|
+
-- already superseded. Nothing dims.
|
|
57
|
+
verdict text NOT NULL CHECK (verdict IN ('confirmed', 'dismissed')),
|
|
58
|
+
|
|
59
|
+
-- WHO answered, and WHEN. A verdict with no author is a rumour: this is the
|
|
60
|
+
-- record that a PERSON decided, which is the entire difference between this
|
|
61
|
+
-- design and the derived-only one ADR 0313 rejected. Nullable only so a
|
|
62
|
+
-- deleted builder does not take the verdict with them.
|
|
63
|
+
decided_by bigint REFERENCES builders(id) ON DELETE SET NULL,
|
|
64
|
+
decided_at timestamptz NOT NULL DEFAULT now(),
|
|
65
|
+
|
|
66
|
+
-- Optional free text. Not NOT NULL, unlike lifecycle_008's `reason`: there the
|
|
67
|
+
-- row exists only when somebody overrode a rule and the "why" is the whole
|
|
68
|
+
-- record, whereas here the common case is an obvious yes or no and demanding
|
|
69
|
+
-- prose for it would only breed empty ceremony.
|
|
70
|
+
note text
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
-- Brightness reads the CONFIRMED rows and only those, so the partial index is the
|
|
74
|
+
-- shape of the one hot query rather than a general-purpose one on a two-value
|
|
75
|
+
-- column, which would never be worth its write cost.
|
|
76
|
+
CREATE INDEX IF NOT EXISTS idx_lifecycle_removal_verdicts_confirmed
|
|
77
|
+
ON lifecycle_removal_verdicts (ship_task_id)
|
|
78
|
+
WHERE verdict = 'confirmed';
|
|
79
|
+
|
|
80
|
+
-- SAFETY NET — a migration that cannot prove its own effect must not report
|
|
81
|
+
-- success (the lifecycle_007 / ideas_007 precedent). Read-only, no DDL. If the
|
|
82
|
+
-- table came back without the single-column primary key, one proposal could carry
|
|
83
|
+
-- two contradictory verdicts with nothing to arbitrate; if the CHECK is missing,
|
|
84
|
+
-- a typo'd verdict would be stored and read as neither answer.
|
|
85
|
+
DO $$
|
|
86
|
+
DECLARE pk_cols text; has_verdict_check boolean;
|
|
87
|
+
BEGIN
|
|
88
|
+
SELECT string_agg(a.attname, ',' ORDER BY a.attname) INTO pk_cols
|
|
89
|
+
FROM pg_constraint c
|
|
90
|
+
JOIN unnest(c.conkey) AS k(attnum) ON true
|
|
91
|
+
JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = k.attnum
|
|
92
|
+
WHERE c.conrelid = 'lifecycle_removal_verdicts'::regclass
|
|
93
|
+
AND c.contype = 'p';
|
|
94
|
+
IF pk_cols IS DISTINCT FROM 'proposal_task_id' THEN
|
|
95
|
+
RAISE EXCEPTION
|
|
96
|
+
'lifecycle_009: lifecycle_removal_verdicts primary key is (%) — expected (proposal_task_id), so one proposal holds one verdict.', COALESCE(pk_cols, 'none');
|
|
97
|
+
END IF;
|
|
98
|
+
|
|
99
|
+
SELECT EXISTS (
|
|
100
|
+
SELECT 1 FROM pg_constraint
|
|
101
|
+
WHERE conrelid = 'lifecycle_removal_verdicts'::regclass
|
|
102
|
+
AND contype = 'c'
|
|
103
|
+
AND pg_get_constraintdef(oid) ILIKE '%verdict%'
|
|
104
|
+
) INTO has_verdict_check;
|
|
105
|
+
IF NOT has_verdict_check THEN
|
|
106
|
+
RAISE EXCEPTION
|
|
107
|
+
'lifecycle_009: the verdict CHECK is missing — an unrecognised verdict would store as neither answer.';
|
|
108
|
+
END IF;
|
|
109
|
+
END $$;
|
|
110
|
+
|
|
111
|
+
COMMIT;
|