@bongos/core 1.19.692 → 1.19.694
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/adr/0177-goal-membership-kind.md +2 -2
- package/docs/adr/0191-majority-and-the-author-who-may-not-decide-alone.md +4 -4
- package/docs/adr/0250-strict-versioning-the-version-boundary-is-the-scope-gate.md +1 -1
- package/docs/copy-inventory.md +29 -31
- package/docs/copy-registry.json +30 -48
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/dom-utils.js +43 -0
- package/modules/hall-ui/public/goals-page.js +10 -3
- package/modules/hall-ui/public/ideas.html +6 -9
- package/modules/hall-ui/public/ideas.js +15 -0
- package/modules/hall-ui/public/work.js +7 -7
- package/modules/lifecycle/lifecycle.js +6 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/docs-entropy.js +10 -0
- package/scripts/gds/linkify-refs.js +81 -4
- package/src/bongos/routes/me.js +8 -0
- package/src/module-api.js +1 -1
- package/tests/linkify_refs.mjs +39 -1
- package/tests/stop_refcheck.mjs +20 -1
- package/tests/task_classifier_vocab.mjs +54 -0
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": 1759,
|
|
7
|
+
"unique_ids": 1521,
|
|
8
|
+
"certain": 1759,
|
|
9
9
|
"heuristic": 0,
|
|
10
10
|
"duplicate_ids": 165
|
|
11
11
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
15
|
"files": 81,
|
|
16
|
-
"strings":
|
|
16
|
+
"strings": 1280
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -2056,7 +2056,7 @@
|
|
|
2056
2056
|
"surface": "builders-hall",
|
|
2057
2057
|
"text": "A Full Idea answers five questions, so that someone else can deliver against it without having to interview you first. Open to every role — the score below is the only filter, and it counts substance, not who you are.",
|
|
2058
2058
|
"file": "modules/hall-ui/public/ideas.html",
|
|
2059
|
-
"line":
|
|
2059
|
+
"line": 104,
|
|
2060
2060
|
"origin": "html-text",
|
|
2061
2061
|
"confidence": "certain"
|
|
2062
2062
|
},
|
|
@@ -2416,7 +2416,7 @@
|
|
|
2416
2416
|
"surface": "builders-hall",
|
|
2417
2417
|
"text": "Answer the five questions and this spark becomes a Full Idea.",
|
|
2418
2418
|
"file": "modules/hall-ui/public/ideas.js",
|
|
2419
|
-
"line":
|
|
2419
|
+
"line": 601,
|
|
2420
2420
|
"origin": "js-text-assign",
|
|
2421
2421
|
"confidence": "certain"
|
|
2422
2422
|
},
|
|
@@ -2726,15 +2726,6 @@
|
|
|
2726
2726
|
"origin": "js-notice",
|
|
2727
2727
|
"confidence": "certain"
|
|
2728
2728
|
},
|
|
2729
|
-
{
|
|
2730
|
-
"id": "f0576990e9fc",
|
|
2731
|
-
"surface": "builders-hall",
|
|
2732
|
-
"text": "Blocker resolution",
|
|
2733
|
-
"file": "modules/hall-ui/public/ideas.html",
|
|
2734
|
-
"line": 81,
|
|
2735
|
-
"origin": "html-text",
|
|
2736
|
-
"confidence": "certain"
|
|
2737
|
-
},
|
|
2738
2729
|
{
|
|
2739
2730
|
"id": "118026595c16",
|
|
2740
2731
|
"surface": "builders-hall",
|
|
@@ -3748,7 +3739,7 @@
|
|
|
3748
3739
|
"surface": "builders-hall",
|
|
3749
3740
|
"text": "Could not load idea {…}.",
|
|
3750
3741
|
"file": "modules/hall-ui/public/ideas.js",
|
|
3751
|
-
"line":
|
|
3742
|
+
"line": 644,
|
|
3752
3743
|
"origin": "js-text-assign",
|
|
3753
3744
|
"confidence": "certain"
|
|
3754
3745
|
},
|
|
@@ -3793,7 +3784,7 @@
|
|
|
3793
3784
|
"surface": "builders-hall",
|
|
3794
3785
|
"text": "Could not load the idea template. Reload, or file from the terminal with capture.js.",
|
|
3795
3786
|
"file": "modules/hall-ui/public/ideas.js",
|
|
3796
|
-
"line":
|
|
3787
|
+
"line": 626,
|
|
3797
3788
|
"origin": "js-text-assign",
|
|
3798
3789
|
"confidence": "certain"
|
|
3799
3790
|
},
|
|
@@ -3820,7 +3811,7 @@
|
|
|
3820
3811
|
"surface": "builders-hall",
|
|
3821
3812
|
"text": "Could not load the queue just now.",
|
|
3822
3813
|
"file": "modules/hall-ui/public/ideas.js",
|
|
3823
|
-
"line":
|
|
3814
|
+
"line": 565,
|
|
3824
3815
|
"origin": "js-markup",
|
|
3825
3816
|
"confidence": "certain"
|
|
3826
3817
|
},
|
|
@@ -4225,7 +4216,7 @@
|
|
|
4225
4216
|
"surface": "builders-hall",
|
|
4226
4217
|
"text": "Counted, not judged: each question is worth the same, and earns its share in proportion to how much real text it carries. Whitespace and padding score nothing.",
|
|
4227
4218
|
"file": "modules/hall-ui/public/ideas.html",
|
|
4228
|
-
"line":
|
|
4219
|
+
"line": 164,
|
|
4229
4220
|
"origin": "html-text",
|
|
4230
4221
|
"confidence": "certain"
|
|
4231
4222
|
},
|
|
@@ -4351,7 +4342,7 @@
|
|
|
4351
4342
|
"surface": "builders-hall",
|
|
4352
4343
|
"text": "Develop this idea",
|
|
4353
4344
|
"file": "modules/hall-ui/public/ideas.js",
|
|
4354
|
-
"line":
|
|
4345
|
+
"line": 600,
|
|
4355
4346
|
"origin": "js-text-assign",
|
|
4356
4347
|
"confidence": "certain"
|
|
4357
4348
|
},
|
|
@@ -4360,7 +4351,7 @@
|
|
|
4360
4351
|
"surface": "builders-hall",
|
|
4361
4352
|
"text": "Developing a Big Idea Spark",
|
|
4362
4353
|
"file": "modules/hall-ui/public/ideas.html",
|
|
4363
|
-
"line":
|
|
4354
|
+
"line": 116,
|
|
4364
4355
|
"origin": "html-text",
|
|
4365
4356
|
"confidence": "certain"
|
|
4366
4357
|
},
|
|
@@ -4396,7 +4387,7 @@
|
|
|
4396
4387
|
"surface": "builders-hall",
|
|
4397
4388
|
"text": "Discard draft",
|
|
4398
4389
|
"file": "modules/hall-ui/public/ideas.html",
|
|
4399
|
-
"line":
|
|
4390
|
+
"line": 142,
|
|
4400
4391
|
"origin": "html-text",
|
|
4401
4392
|
"confidence": "certain"
|
|
4402
4393
|
},
|
|
@@ -4828,7 +4819,7 @@
|
|
|
4828
4819
|
"surface": "builders-hall",
|
|
4829
4820
|
"text": "File it",
|
|
4830
4821
|
"file": "modules/hall-ui/public/ideas.html",
|
|
4831
|
-
"line":
|
|
4822
|
+
"line": 96,
|
|
4832
4823
|
"origin": "html-text",
|
|
4833
4824
|
"confidence": "certain"
|
|
4834
4825
|
},
|
|
@@ -4855,7 +4846,7 @@
|
|
|
4855
4846
|
"surface": "builders-hall",
|
|
4856
4847
|
"text": "File this idea",
|
|
4857
4848
|
"file": "modules/hall-ui/public/ideas.html",
|
|
4858
|
-
"line":
|
|
4849
|
+
"line": 141,
|
|
4859
4850
|
"origin": "html-text",
|
|
4860
4851
|
"confidence": "certain"
|
|
4861
4852
|
},
|
|
@@ -4954,7 +4945,7 @@
|
|
|
4954
4945
|
"surface": "builders-hall",
|
|
4955
4946
|
"text": "Five questions, and a score that counts how much of each you actually answered.",
|
|
4956
4947
|
"file": "modules/hall-ui/public/ideas.js",
|
|
4957
|
-
"line":
|
|
4948
|
+
"line": 636,
|
|
4958
4949
|
"origin": "js-text-assign",
|
|
4959
4950
|
"confidence": "certain"
|
|
4960
4951
|
},
|
|
@@ -5188,7 +5179,7 @@
|
|
|
5188
5179
|
"surface": "builders-hall",
|
|
5189
5180
|
"text": "Goals could not be loaded. Refresh to try again.",
|
|
5190
5181
|
"file": "modules/hall-ui/public/goals-page.js",
|
|
5191
|
-
"line":
|
|
5182
|
+
"line": 896,
|
|
5192
5183
|
"origin": "js-text-assign",
|
|
5193
5184
|
"confidence": "certain"
|
|
5194
5185
|
},
|
|
@@ -5930,15 +5921,6 @@
|
|
|
5930
5921
|
"origin": "js-text-assign",
|
|
5931
5922
|
"confidence": "certain"
|
|
5932
5923
|
},
|
|
5933
|
-
{
|
|
5934
|
-
"id": "19ad2430937d",
|
|
5935
|
-
"surface": "builders-hall",
|
|
5936
|
-
"text": "Learning capture",
|
|
5937
|
-
"file": "modules/hall-ui/public/ideas.html",
|
|
5938
|
-
"line": 78,
|
|
5939
|
-
"origin": "html-text",
|
|
5940
|
-
"confidence": "certain"
|
|
5941
|
-
},
|
|
5942
5924
|
{
|
|
5943
5925
|
"id": "7c9b7a89353b",
|
|
5944
5926
|
"surface": "builders-hall",
|
|
@@ -6115,7 +6097,7 @@
|
|
|
6115
6097
|
"surface": "builders-hall",
|
|
6116
6098
|
"text": "Loading the five questions…",
|
|
6117
6099
|
"file": "modules/hall-ui/public/ideas.html",
|
|
6118
|
-
"line":
|
|
6100
|
+
"line": 135,
|
|
6119
6101
|
"origin": "html-text",
|
|
6120
6102
|
"confidence": "certain"
|
|
6121
6103
|
},
|
|
@@ -6970,7 +6952,7 @@
|
|
|
6970
6952
|
"surface": "builders-hall",
|
|
6971
6953
|
"text": "Nothing is waiting. A spark appears here when its author offers a quick idea up for development.",
|
|
6972
6954
|
"file": "modules/hall-ui/public/ideas.js",
|
|
6973
|
-
"line":
|
|
6955
|
+
"line": 543,
|
|
6974
6956
|
"origin": "js-markup",
|
|
6975
6957
|
"confidence": "certain"
|
|
6976
6958
|
},
|
|
@@ -7060,7 +7042,7 @@
|
|
|
7060
7042
|
"surface": "builders-hall",
|
|
7061
7043
|
"text": "One line. This is what the idea is called everywhere else.",
|
|
7062
7044
|
"file": "modules/hall-ui/public/ideas.html",
|
|
7063
|
-
"line":
|
|
7045
|
+
"line": 124,
|
|
7064
7046
|
"origin": "html-text",
|
|
7065
7047
|
"confidence": "certain"
|
|
7066
7048
|
},
|
|
@@ -7888,7 +7870,7 @@
|
|
|
7888
7870
|
"surface": "builders-hall",
|
|
7889
7871
|
"text": "Recent history could not be loaded right now.",
|
|
7890
7872
|
"file": "modules/hall-ui/public/work.js",
|
|
7891
|
-
"line":
|
|
7873
|
+
"line": 1236,
|
|
7892
7874
|
"origin": "js-markup",
|
|
7893
7875
|
"confidence": "certain"
|
|
7894
7876
|
},
|
|
@@ -8707,7 +8689,7 @@
|
|
|
8707
8689
|
"surface": "builders-hall",
|
|
8708
8690
|
"text": "Showing {…} of {…}.",
|
|
8709
8691
|
"file": "modules/hall-ui/public/ideas.js",
|
|
8710
|
-
"line":
|
|
8692
|
+
"line": 547,
|
|
8711
8693
|
"origin": "js-markup",
|
|
8712
8694
|
"confidence": "certain"
|
|
8713
8695
|
},
|
|
@@ -8752,7 +8734,7 @@
|
|
|
8752
8734
|
"surface": "builders-hall",
|
|
8753
8735
|
"text": "Sign in to filter to your goals",
|
|
8754
8736
|
"file": "modules/hall-ui/public/goals-page.js",
|
|
8755
|
-
"line":
|
|
8737
|
+
"line": 873,
|
|
8756
8738
|
"origin": "js-text-assign",
|
|
8757
8739
|
"confidence": "certain"
|
|
8758
8740
|
},
|
|
@@ -9022,7 +9004,7 @@
|
|
|
9022
9004
|
"surface": "builders-hall",
|
|
9023
9005
|
"text": "Suggested as you type; leave it on \"no goal\" and it waits for triage.",
|
|
9024
9006
|
"file": "modules/hall-ui/public/ideas.html",
|
|
9025
|
-
"line":
|
|
9007
|
+
"line": 88,
|
|
9026
9008
|
"origin": "html-text",
|
|
9027
9009
|
"confidence": "certain"
|
|
9028
9010
|
},
|
|
@@ -9715,7 +9697,7 @@
|
|
|
9715
9697
|
"surface": "builders-hall",
|
|
9716
9698
|
"text": "The task list could not be loaded right now.",
|
|
9717
9699
|
"file": "modules/hall-ui/public/work.js",
|
|
9718
|
-
"line":
|
|
9700
|
+
"line": 1301,
|
|
9719
9701
|
"origin": "js-markup",
|
|
9720
9702
|
"confidence": "certain"
|
|
9721
9703
|
},
|
|
@@ -10255,7 +10237,7 @@
|
|
|
10255
10237
|
"surface": "builders-hall",
|
|
10256
10238
|
"text": "Welcome, {…}. Claim and release tasks and follow work in progress — no terminal needed.",
|
|
10257
10239
|
"file": "modules/hall-ui/public/work.js",
|
|
10258
|
-
"line":
|
|
10240
|
+
"line": 1227,
|
|
10259
10241
|
"origin": "js-text-assign",
|
|
10260
10242
|
"confidence": "certain"
|
|
10261
10243
|
},
|
|
@@ -10597,7 +10579,7 @@
|
|
|
10597
10579
|
"surface": "builders-hall",
|
|
10598
10580
|
"text": "Write a Full Idea",
|
|
10599
10581
|
"file": "modules/hall-ui/public/ideas.html",
|
|
10600
|
-
"line":
|
|
10582
|
+
"line": 103,
|
|
10601
10583
|
"origin": "html-text",
|
|
10602
10584
|
"confidence": "certain"
|
|
10603
10585
|
},
|
|
@@ -12055,7 +12037,7 @@
|
|
|
12055
12037
|
"surface": "builders-hall",
|
|
12056
12038
|
"text": "repro steps, context, anything that helps…",
|
|
12057
12039
|
"file": "modules/hall-ui/public/ideas.html",
|
|
12058
|
-
"line":
|
|
12040
|
+
"line": 84,
|
|
12059
12041
|
"origin": "html-attr:placeholder",
|
|
12060
12042
|
"confidence": "certain"
|
|
12061
12043
|
},
|
|
@@ -12361,7 +12343,7 @@
|
|
|
12361
12343
|
"surface": "builders-hall",
|
|
12362
12344
|
"text": "via goal",
|
|
12363
12345
|
"file": "modules/hall-ui/public/dom-utils.js",
|
|
12364
|
-
"line":
|
|
12346
|
+
"line": 364,
|
|
12365
12347
|
"origin": "js-markup",
|
|
12366
12348
|
"confidence": "certain"
|
|
12367
12349
|
},
|
|
@@ -12397,7 +12379,7 @@
|
|
|
12397
12379
|
"surface": "builders-hall",
|
|
12398
12380
|
"text": "what the idea is, in one line",
|
|
12399
12381
|
"file": "modules/hall-ui/public/ideas.html",
|
|
12400
|
-
"line":
|
|
12382
|
+
"line": 127,
|
|
12401
12383
|
"origin": "html-attr:placeholder",
|
|
12402
12384
|
"confidence": "certain"
|
|
12403
12385
|
},
|
|
@@ -1843,5 +1843,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1843
1843
|
landed since 1.19.690 with no explicit bump. run 34729018766. (task 1002620)
|
|
1844
1844
|
1.19.692 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1845
1845
|
landed since 1.19.691 with no explicit bump. run 34730821372. (task 1002620)
|
|
1846
|
+
1.19.693 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1847
|
+
landed since 1.19.692 with no explicit bump. run 34731402927. (task 1002620)
|
|
1848
|
+
1.19.694 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1849
|
+
landed since 1.19.693 with no explicit bump. run 34731674808. (task 1002620)
|
|
1846
1850
|
---------------------------------------------------------------------------
|
|
1847
1851
|
```
|
|
@@ -120,6 +120,48 @@
|
|
|
120
120
|
return list && list.length ? list.slice() : ['engineer', 'ideator'];
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// Fill a Kind <select> from the vocabulary the SERVER serves (GET /me
|
|
124
|
+
// `task_kinds`, sourced from the lifecycle port's VALID_KINDS) — task 1003453.
|
|
125
|
+
// The board and the goals page each carried their own hardcoded copy and both
|
|
126
|
+
// had gone stale against the DB CHECK: 'verify' tasks existed, were claimable,
|
|
127
|
+
// and could not be filtered for on either. One filler here is what stops a
|
|
128
|
+
// third copy being the easiest option, the same reasoning `disciplines()`
|
|
129
|
+
// above was written on.
|
|
130
|
+
//
|
|
131
|
+
// Leading '— any —' is the page's own markup and is KEPT (`sel.length = 1`,
|
|
132
|
+
// the populateGoalFilter idiom). An absent or empty list leaves the select
|
|
133
|
+
// with just that row rather than a wrong list — an older server that serves no
|
|
134
|
+
// vocabulary should offer no lens, not a stale one.
|
|
135
|
+
// `keepFirst` is how many markup options survive: 1 for a FILTER, whose leading
|
|
136
|
+
// '— any —' belongs to the page, and 0 for a FILING select, which is cleared and
|
|
137
|
+
// rebuilt — its markup option is the degraded fallback that still lets someone
|
|
138
|
+
// file if the read never lands. `label` names a kind for a reader (the filters
|
|
139
|
+
// show the raw value, as their literals did; the filing select shows kindLabel).
|
|
140
|
+
function fillKindFilter(sel, kinds, { keepFirst = 1, label = (k) => k, selected = null } = {}) {
|
|
141
|
+
if (!sel) return;
|
|
142
|
+
const list = Array.isArray(kinds) ? kinds.filter((k) => typeof k === 'string' && k) : [];
|
|
143
|
+
if (!list.length) return; // no vocabulary served → leave the markup alone
|
|
144
|
+
const cur = sel.value;
|
|
145
|
+
sel.length = keepFirst;
|
|
146
|
+
for (const k of list) {
|
|
147
|
+
const opt = document.createElement('option');
|
|
148
|
+
opt.value = k;
|
|
149
|
+
opt.textContent = label(k);
|
|
150
|
+
sel.appendChild(opt);
|
|
151
|
+
}
|
|
152
|
+
// A refill must not drop the reader's pick; `selected` is the filing default.
|
|
153
|
+
const want = (cur && list.includes(cur)) ? cur : (list.includes(selected) ? selected : null);
|
|
154
|
+
if (want) sel.value = want;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// How a kind is named in a picker — 'blocker-resolution' → 'Blocker resolution'.
|
|
158
|
+
// The same shape disciplineLabel takes below, and it reproduces every label the
|
|
159
|
+
// hand-written <option> list in ideas.html carried, so no copy changes.
|
|
160
|
+
function kindLabel(k) {
|
|
161
|
+
const s = String(k || '').replace(/-/g, ' ');
|
|
162
|
+
return s ? s.charAt(0).toUpperCase() + s.slice(1) : '';
|
|
163
|
+
}
|
|
164
|
+
|
|
123
165
|
// How a craft is named in a picker. An acronym stays upper-case ('ui' → 'UI');
|
|
124
166
|
// anything else is Title Case, so a craft a module adds later needs no new entry.
|
|
125
167
|
function disciplineLabel(d) {
|
|
@@ -324,6 +366,7 @@
|
|
|
324
366
|
|
|
325
367
|
window.OTB = {
|
|
326
368
|
escapeHtml, $, fmtNum, num, fmtInt, fmtCompact, fmtDate, rankLabel, toast, moduleOn, disciplines, disciplineLabel,
|
|
369
|
+
fillKindFilter, kindLabel, // task 1003453 — one Kind filler, all three surfaces
|
|
327
370
|
errCode, errMessage, errDetails, // task 1002462 — ADR 0116 envelope readers
|
|
328
371
|
contributeSettingsPanel, settingsPanels,
|
|
329
372
|
contributeHallWidget, hallWidgets, hallWidgetIds,
|
|
@@ -862,6 +862,12 @@
|
|
|
862
862
|
}
|
|
863
863
|
try {
|
|
864
864
|
state.me = await getJSON(`${API}/me`).catch(() => null);
|
|
865
|
+
// task 1003453 — the Kind lens is built from the served vocabulary.
|
|
866
|
+
// Optional-called on purpose: a browser holding an older cached dom-utils.js
|
|
867
|
+
// must lose one LENS, not the whole page. boot() is a single try block, so an
|
|
868
|
+
// unguarded call here throws before anything renders — which is exactly what
|
|
869
|
+
// it did to every goals render until this guard went in.
|
|
870
|
+
window.OTB.fillKindFilter?.($('#f-goal-kind'), state.me && state.me.task_kinds);
|
|
865
871
|
// task 1828 — "My goals" needs an identity; disable it when signed out.
|
|
866
872
|
const mineSel = $('#f-goal-mine');
|
|
867
873
|
if (mineSel && !state.me) { mineSel.disabled = true; mineSel.title = 'Sign in to filter to your goals'; }
|
|
@@ -1028,9 +1034,10 @@
|
|
|
1028
1034
|
] },
|
|
1029
1035
|
],
|
|
1030
1036
|
overflow: [
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1037
|
+
// task 1003453: filled from /me.task_kinds once boot lands it, never typed
|
|
1038
|
+
// here — this copy had gone stale against the DB CHECK exactly as the
|
|
1039
|
+
// board's had, so 'verify' work could not be filtered for on either.
|
|
1040
|
+
{ id: 'f-goal-kind', label: 'Kind', options: [{ value: '', label: '— any —' }] },
|
|
1034
1041
|
{ id: 'f-goal-discipline', label: 'Discipline', options: [{ value: '', label: '— any —' }, 'engineer', 'artist', 'ideator', 'ui', 'unclassified'] },
|
|
1035
1042
|
{ id: 'f-goal-newcomer', label: 'Newcomer', options: [{ value: '', label: '— any —' }, { value: 'yes', label: 'newcomer-friendly only' }] },
|
|
1036
1043
|
{ id: 'f-goal-fit', label: 'Run mode', options: [{ value: '', label: '— any —' },
|
|
@@ -69,17 +69,14 @@
|
|
|
69
69
|
placeholder="what's wrong, or what you'd like to see">
|
|
70
70
|
|
|
71
71
|
<label class="fi-label" for="fq-kind">Kind</label>
|
|
72
|
+
<!-- task 1003453: the options are FILLED from /me.task_kinds at boot
|
|
73
|
+
(ideas.js), not written here. This markup used to spell the whole
|
|
74
|
+
vocabulary and had gone stale against the DB CHECK exactly as the
|
|
75
|
+
two board filters had — a third copy nobody was watching. The one
|
|
76
|
+
option left is the degraded fallback: if the read never lands, a
|
|
77
|
+
bug can still be filed. -->
|
|
72
78
|
<select id="fq-kind" class="fi-input">
|
|
73
79
|
<option value="bug" selected>Bug</option>
|
|
74
|
-
<option value="feature">Feature</option>
|
|
75
|
-
<option value="infra">Infra</option>
|
|
76
|
-
<option value="refactor">Refactor</option>
|
|
77
|
-
<option value="spike">Spike</option>
|
|
78
|
-
<option value="learning-capture">Learning capture</option>
|
|
79
|
-
<option value="cleanup">Cleanup</option>
|
|
80
|
-
<option value="decision">Decision</option>
|
|
81
|
-
<option value="blocker-resolution">Blocker resolution</option>
|
|
82
|
-
<option value="unclassified">Unclassified</option>
|
|
83
80
|
</select>
|
|
84
81
|
|
|
85
82
|
<label class="fi-label" for="fq-body">Details <span class="fi-hint">(optional)</span></label>
|
|
@@ -508,6 +508,21 @@
|
|
|
508
508
|
// The picker degrades to "homeless only" — filing itself never depends
|
|
509
509
|
// on this read succeeding.
|
|
510
510
|
}
|
|
511
|
+
|
|
512
|
+
// task 1003453 — the kind vocabulary, so this select is not a third hardcoded
|
|
513
|
+
// copy. Its OWN try, deliberately not folded into the Promise.all above: the
|
|
514
|
+
// goal picker is what this form is FOR and the kind list is cosmetic, so a
|
|
515
|
+
// vocabulary read must never be able to take the picker down with it. (It can:
|
|
516
|
+
// put it in that Promise.all and one rejection strands buildingVersionIds and
|
|
517
|
+
// allOpenGoals in the shared catch — two suites caught exactly that.)
|
|
518
|
+
// keepFirst 0 clears the markup's fallback and rebuilds with the served list,
|
|
519
|
+
// keeping 'bug' as the default this form has always opened on.
|
|
520
|
+
try {
|
|
521
|
+
const meRes = await api.me.getMe({});
|
|
522
|
+
window.OTB.fillKindFilter?.(fqKindEl(), meRes.ok && meRes.data && meRes.data.task_kinds, {
|
|
523
|
+
keepFirst: 0, label: window.OTB.kindLabel, selected: 'bug',
|
|
524
|
+
});
|
|
525
|
+
} catch (_) { /* the markup's Bug option stands — a report can still be filed */ }
|
|
511
526
|
}
|
|
512
527
|
|
|
513
528
|
// ---------------------------------------------------------------------------
|
|
@@ -1217,6 +1217,10 @@
|
|
|
1217
1217
|
throw err;
|
|
1218
1218
|
}
|
|
1219
1219
|
viewer.id = me?.builder?.id ?? null;
|
|
1220
|
+
// task 1003453 — the Kind lens, from the served vocabulary. Optional-called so an
|
|
1221
|
+
// older cached dom-utils.js costs one lens, not the board; see OTB.fillKindFilter.
|
|
1222
|
+
window.OTB.fillKindFilter?.($('#f-kind'), me?.task_kinds);
|
|
1223
|
+
restoreUrlFilter('f-kind'); // a shared URL's kind applies once the options exist
|
|
1220
1224
|
const sub = $('#work-sub');
|
|
1221
1225
|
if (sub) {
|
|
1222
1226
|
const name = me?.builder?.display_name || me?.builder?.github_login || 'builder';
|
|
@@ -1364,13 +1368,9 @@
|
|
|
1364
1368
|
],
|
|
1365
1369
|
overflow: [
|
|
1366
1370
|
{ id: 'f-goal', label: 'Goal', options: [{ value: '', label: '— all goals —' }] },
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
options: [{ value: '', label: '— any —' },
|
|
1371
|
-
'feature', 'bug', 'infra', 'refactor', 'spike', 'learning-capture',
|
|
1372
|
-
'cleanup', 'decision', 'blocker-resolution', 'unclassified'],
|
|
1373
|
-
},
|
|
1371
|
+
// task 1003453: FILLED from /me.task_kinds at boot, never typed here — the
|
|
1372
|
+
// literal was stale against the DB CHECK, so 'verify' was unfilterable.
|
|
1373
|
+
{ id: 'f-kind', label: 'Kind', options: [{ value: '', label: '— any —' }] },
|
|
1374
1374
|
{
|
|
1375
1375
|
id: 'f-newcomer',
|
|
1376
1376
|
label: 'Newcomer',
|
|
@@ -37,6 +37,12 @@ module.exports = {
|
|
|
37
37
|
// (consumer: core cascade-dispatch.js, the dormant System-3 lib).
|
|
38
38
|
get VOTABLE_TASK_STATUSES() { return classifier.VOTABLE_TASK_STATUSES; },
|
|
39
39
|
classifyTaskKind: (...a) => classifier.classifyTaskKind(...a),
|
|
40
|
+
// TASK_KINDS: the kind vocabulary, as an ordered array (consumer: GET /me,
|
|
41
|
+
// which hands it to the hall so a filter can be BUILT from it instead of
|
|
42
|
+
// retyped — task 1003453). VALID_KINDS is a Set and Sets do not survive
|
|
43
|
+
// JSON.stringify, so the port hands over the array rather than making every
|
|
44
|
+
// caller remember to spread it.
|
|
45
|
+
get TASK_KINDS() { return [...classifier.VALID_KINDS]; },
|
|
40
46
|
// suggestGoalForCapture: ONE goal for a filing that named none, or null (task
|
|
41
47
|
// 1003067). Consumer: the discord #bugs vector, which files through the ideas
|
|
42
48
|
// routing path and needs a home to offer it. Lives behind the port because
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.694",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.694",
|
|
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.19.
|
|
3
|
+
"version": "1.19.694",
|
|
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",
|
|
@@ -160,6 +160,16 @@ function parseFile(absPath) {
|
|
|
160
160
|
const href = m[2].trim();
|
|
161
161
|
// Skip external URLs, mailto, and obvious non-paths (e.g. ellipsis placeholders)
|
|
162
162
|
if (/^(https?:|mailto:|ftp:)/.test(href)) continue;
|
|
163
|
+
// The hall deep-link resolver in its ROOT-RELATIVE form (task 1003542). The
|
|
164
|
+
// absolute form is already skipped above as external; linkify-refs.js emits
|
|
165
|
+
// this one whenever the instance origin is absent or loopback — which is now
|
|
166
|
+
// the normal case on a core checkout, since writing http://localhost:3000 into
|
|
167
|
+
// a committed doc is the worse bug. It is an APP ROUTE, not a file path, so
|
|
168
|
+
// counting it as a stale link is a false positive: 28 of them appeared the
|
|
169
|
+
// moment the linkifier could see this instance's 7-digit ids.
|
|
170
|
+
// Deliberately NOT a blanket `^/` skip — a genuinely wrong root-relative file
|
|
171
|
+
// link should still be reported as rot.
|
|
172
|
+
if (/^\/builders#/.test(href)) continue;
|
|
163
173
|
if (/^\.{2,}$/.test(href)) continue; // skip ... or .. used as placeholders
|
|
164
174
|
links.push({ href, line: lineNo });
|
|
165
175
|
}
|
|
@@ -57,12 +57,45 @@ const path = require('node:path');
|
|
|
57
57
|
// `https://amazonprimea.com`). When branding is unreadable the base is the
|
|
58
58
|
// root-relative '/builders#/…' — never a hardcoded founder host. Reading branding is
|
|
59
59
|
// a sync file read (no network), safe at module scope for this token-free CI gate.
|
|
60
|
+
// A LOOPBACK origin is treated exactly like an unreadable one — root-relative,
|
|
61
|
+
// never written out (task 1003542; the committed-doc half of task 1002975).
|
|
62
|
+
//
|
|
63
|
+
// The header above promises a per-dev localhost override "must never leak into a
|
|
64
|
+
// committed doc link", and the API_BASE half of that holds. But the BRANDING half
|
|
65
|
+
// did not: config/branding.neutral.json — the neutral core's own default, used by
|
|
66
|
+
// every checkout with no instance branding — sets buildersOrigin to
|
|
67
|
+
// http://localhost:3000. So `--apply` on a core checkout rewrote bare refs into
|
|
68
|
+
// http://localhost:3000/builders#/task/N and committed them.
|
|
69
|
+
//
|
|
70
|
+
// That was survivable only while the gate was blind: it matched \d{1,5} and this
|
|
71
|
+
// instance's ids are 7 digits, so --apply found nothing to rewrite. Widening the
|
|
72
|
+
// width in this task ARMS it — the first honest run wanted to rewrite 33 refs
|
|
73
|
+
// across 10 committed docs, ADRs included. Hence this guard lands with the widening
|
|
74
|
+
// rather than after it.
|
|
75
|
+
//
|
|
76
|
+
// Root-relative is the right fallback, not a guess: it is what the catch below
|
|
77
|
+
// already returns, and it resolves correctly on whatever host actually serves the
|
|
78
|
+
// hall — which is more than a baked absolute origin can promise.
|
|
79
|
+
function isLoopbackOrigin(origin) {
|
|
80
|
+
try {
|
|
81
|
+
// URL keeps an IPv6 literal in its brackets ('[::1]'), so strip them before
|
|
82
|
+
// comparing — a caught-by-its-own-test detail, not a hypothetical.
|
|
83
|
+
const host = new URL(origin).hostname.replace(/^\[|\]$/g, '');
|
|
84
|
+
return host === 'localhost' || host === '::1' || host === '0.0.0.0'
|
|
85
|
+
|| host.endsWith('.localhost')
|
|
86
|
+
|| /^127\./.test(host); // the whole 127.0.0.0/8 loopback block, not just .0.1
|
|
87
|
+
} catch (_) {
|
|
88
|
+
return false; // not a parseable absolute URL → not a loopback origin
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
60
92
|
function resolverHallRoot() {
|
|
61
93
|
try {
|
|
62
94
|
const d = (require('../../src/branding').branding().domains) || {};
|
|
63
95
|
const origin = (typeof d.buildersOrigin === 'string' && d.buildersOrigin.trim())
|
|
64
96
|
|| (typeof d.publicOrigin === 'string' && d.publicOrigin.trim())
|
|
65
97
|
|| '';
|
|
98
|
+
if (!origin || isLoopbackOrigin(origin)) return '/builders#';
|
|
66
99
|
return `${String(origin).replace(/\/+$/, '')}/builders#`;
|
|
67
100
|
} catch (_) {
|
|
68
101
|
return '/builders#';
|
|
@@ -94,8 +127,24 @@ function discoverDocs() {
|
|
|
94
127
|
}
|
|
95
128
|
};
|
|
96
129
|
for (const root of ['CLAUDE.md', 'README.md', 'docs']) walk(root);
|
|
130
|
+
// The skip marker matched only the literal "GENERATED FILE" — and NO generator in
|
|
131
|
+
// this repo writes that (task 1003542). gen-api-docs.js heads both of its outputs
|
|
132
|
+
// with "GENERATED by scripts/gds/gen-api-docs.js", so docs/api-reference.md and
|
|
133
|
+
// docs/routes-permissions.md were being linkified despite the rule above: their
|
|
134
|
+
// rewrites survive exactly until the next regen, and meanwhile ship-check reports
|
|
135
|
+
// api-docs STALE. Two gates fighting over one file, each correctly.
|
|
136
|
+
//
|
|
137
|
+
// Latent until now for the same reason as the loopback leak: the matcher was too
|
|
138
|
+
// narrow to rewrite anything, so nothing was ever written into a generated file.
|
|
139
|
+
// Widening it in this task is what surfaced this.
|
|
140
|
+
//
|
|
141
|
+
// NOTE docs/copy-inventory.md heads with "**Generated** by" — bolded, so not
|
|
142
|
+
// contiguous and still unmatched here. Left alone deliberately: it carries no bare
|
|
143
|
+
// refs today, and inventing a looser pattern risks skipping a hand-authored doc
|
|
144
|
+
// that merely mentions being generated. Fix its marker at the generator if it ever
|
|
145
|
+
// grows one.
|
|
97
146
|
return out.filter((rel) =>
|
|
98
|
-
!/GENERATED FILE/i.test(fs.readFileSync(path.resolve(process.cwd(), rel), 'utf8').slice(0, 200))
|
|
147
|
+
!/GENERATED (?:FILE|by)\b/i.test(fs.readFileSync(path.resolve(process.cwd(), rel), 'utf8').slice(0, 200))
|
|
99
148
|
);
|
|
100
149
|
}
|
|
101
150
|
|
|
@@ -131,7 +180,27 @@ const LIST_GLUE_RE = /^[\s,–—-]*(?:and|&|\+|or|to|through)?[\s,–—-]*$/i;
|
|
|
131
180
|
// '/builders#/…', so the mislink-cleanup path still recognizes a link a PRIOR run
|
|
132
181
|
// committed under a DIFFERENT host — the legacy `amazonprimea.com` links, a
|
|
133
182
|
// co-tenant's, or this instance's own — not only the current RESOLVER_BASE host.
|
|
134
|
-
|
|
183
|
+
// How many digits a bare "#NNN" may carry — ONE source of truth, because the
|
|
184
|
+
// width had drifted out of date in five places at once (task 1003542).
|
|
185
|
+
//
|
|
186
|
+
// It was {1,5}, a PRE-IMPORT id width. This instance renumbered imported tasks
|
|
187
|
+
// +1,000,000, so every live id is SEVEN digits — and both enforcement points
|
|
188
|
+
// silently stopped enforcing: `linkify-refs.js --check` (the CI gate) passed
|
|
189
|
+
// trivially on a doc containing a bare #1003365, and .claude/hooks/stop-refcheck.js
|
|
190
|
+
// fast-path-exited before it even fetched the allow-list. A guard that matches
|
|
191
|
+
// nothing reports clean, which is indistinguishable from a guard that works.
|
|
192
|
+
//
|
|
193
|
+
// 7 keeps the property {1,5} was chosen for — not over-matching PR-shaped numbers.
|
|
194
|
+
// GitHub PR numbers on this repo are 3 digits, well inside the old range, so the
|
|
195
|
+
// bound was never what separated them; classifyKind's `PR #NNN` left-context test
|
|
196
|
+
// is, and it is untouched here.
|
|
197
|
+
//
|
|
198
|
+
// Exported as the SOURCE STRING, not a RegExp: a /g/ regex carries lastIndex
|
|
199
|
+
// between calls, so a shared instance would skip matches depending on who scanned
|
|
200
|
+
// last. Each site builds its own.
|
|
201
|
+
const REF_DIGITS = '\\d{1,7}';
|
|
202
|
+
|
|
203
|
+
const RESOLVER_LINK_RE = new RegExp(`\\[#(${REF_DIGITS})\\]\\((?:https?:\\/\\/[^/)\\s]+)?\\/builders#\\/task\\/(${REF_DIGITS})\\)`, 'g');
|
|
135
204
|
|
|
136
205
|
// Classify a ref by its left context (+ list-glue carry). Returns one of
|
|
137
206
|
// 'github' | 'idea' | 'blocker' | 'entity' | null (null = candidate task ref).
|
|
@@ -194,7 +263,7 @@ function unlinkEntitySegment(segment, leftContext, stats) {
|
|
|
194
263
|
// {tasks, ideas, blockers} Sets. Returns the transformed segment; mutates `stats`.
|
|
195
264
|
function linkifySegment(segment, ids, leftContext, stats) {
|
|
196
265
|
let out = '';
|
|
197
|
-
const re =
|
|
266
|
+
const re = new RegExp(`#(${REF_DIGITS})`, 'g');
|
|
198
267
|
let m;
|
|
199
268
|
let last = 0;
|
|
200
269
|
let prevEnd = 0; // index in `segment` just past the previous match
|
|
@@ -303,7 +372,7 @@ function upgradeLineCodeSpans(line, ids, stats) {
|
|
|
303
372
|
let prevEnd = 0;
|
|
304
373
|
let hadPrev = false;
|
|
305
374
|
let prevKind = null; // only 'idea' | 'blocker' carry here
|
|
306
|
-
const re =
|
|
375
|
+
const re = new RegExp('`#(' + REF_DIGITS + ')`', 'g');
|
|
307
376
|
let m;
|
|
308
377
|
while ((m = re.exec(line)) !== null) {
|
|
309
378
|
const num = m[1];
|
|
@@ -496,4 +565,12 @@ module.exports = {
|
|
|
496
565
|
linkifyText, linkifySegment, unlinkEntitySegment, upgradeLineCodeSpans,
|
|
497
566
|
classifyKind, normalizeIdSets, discoverDocs,
|
|
498
567
|
RESOLVER_BASE, RESOLVER_BASE_IDEA, RESOLVER_BASE_BLOCKER,
|
|
568
|
+
// task 1003542: the bare-ref digit width, exported so the OTHER enforcement
|
|
569
|
+
// point (.claude/hooks/stop-refcheck.js) reads it here instead of keeping its
|
|
570
|
+
// own copy — the drift that let 7-digit ids past both guards at once.
|
|
571
|
+
REF_DIGITS,
|
|
572
|
+
// task 1003542: the loopback test behind the resolver base. Exported because
|
|
573
|
+
// RESOLVER_BASE is computed once at module load, so the DECISION is the only
|
|
574
|
+
// part a unit test can reach without re-requiring under a mocked branding.
|
|
575
|
+
isLoopbackOrigin,
|
|
499
576
|
};
|