@bongos/core 1.19.691 → 1.19.693

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.
@@ -3,9 +3,9 @@
3
3
  "generator": "scripts/gds/copy-inventory.js",
4
4
  "goal": 1000074,
5
5
  "counts": {
6
- "strings": 1761,
7
- "unique_ids": 1523,
8
- "certain": 1761,
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": 1282
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": 107,
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": 586,
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": 629,
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": 611,
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": 550,
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": 167,
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": 585,
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": 119,
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": 145,
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": 99,
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": 144,
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": 621,
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": 890,
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": 138,
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": 528,
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": 127,
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": 1232,
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": 532,
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": 867,
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": 91,
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": 1297,
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": 1223,
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": 106,
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": 87,
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": 322,
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": 130,
12382
+ "line": 127,
12401
12383
  "origin": "html-attr:placeholder",
12402
12384
  "confidence": "certain"
12403
12385
  },
@@ -1841,5 +1841,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
1841
1841
  landed since 1.19.689 with no explicit bump. run 34727464797. (task 1002620)
1842
1842
  1.19.691 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1843
1843
  landed since 1.19.690 with no explicit bump. run 34729018766. (task 1002620)
1844
+ 1.19.692 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
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)
1844
1848
  ---------------------------------------------------------------------------
1845
1849
  ```
@@ -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
- { id: 'f-goal-kind', label: 'Kind', options: [{ value: '', label: '— any —' },
1032
- 'feature', 'bug', 'infra', 'refactor', 'spike', 'learning-capture',
1033
- 'cleanup', 'decision', 'blocker-resolution', 'unclassified'] },
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
- id: 'f-kind',
1369
- label: 'Kind',
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
@@ -56,6 +56,10 @@ const { branding } = api;
56
56
  // above the 20 MB decoded ceiling. The decoded bytes are re-checked in the
57
57
  // handler (the JSON `limit` only bounds the ENCODED payload — SR-17 posture).
58
58
  const PUBLISH_BODY_LIMIT = '32mb';
59
+ // The grade body is committed_files (one entry per changed path) + the panel's findings.
60
+ // 1 MB covers a repo-wide change with room to spare and still bounds the route; the global
61
+ // 64 KB cap is skipped for this path in src/bongos/routes.js. See task 1003850.
62
+ const GRADE_BODY_LIMIT = '1mb';
59
63
  const PUBLISH_BUNDLE_B64_MAX = 30 * 1024 * 1024; // validator guard on the b64 string
60
64
  const PUBLISH_BUNDLE_MAX_BYTES = 20 * 1024 * 1024; // 20 MB decoded bundle
61
65
 
@@ -881,6 +885,11 @@ module.exports = function buildTasksRouter() {
881
885
  }
882
886
  });
883
887
 
888
+ // Declared ABOVE the doc-comment on purpose: gen-api-docs.js reads only the `//`
889
+ // lines immediately preceding `router.post(`, so a const wedged between them blanks
890
+ // this endpoint's whole description in docs/api-reference.md and openapi.json.
891
+ const gradeUpload = express.json({ limit: GRADE_BODY_LIMIT });
892
+
884
893
  // GDS-V3 task V3.R18 (#209). Subagent grader credits gate.
885
894
  //
886
895
  // ship.js (or any caller) spawns an Opus subagent against the diff + task
@@ -905,7 +914,7 @@ module.exports = function buildTasksRouter() {
905
914
  // rank: any-builder — grader writes a row + auto-promotes only if grade clears
906
915
  // threshold; the grader subagent is the objectivity guarantee. Supervisor hot
907
916
  // path (preserved per CRITICAL §4 — no requireRank).
908
- router.post('/tasks/:id/grade', auth.requireBuilder, async (req, res) => {
917
+ router.post('/tasks/:id/grade', auth.requireBuilder, gradeUpload, async (req, res) => {
909
918
  // Permissive on subagent_output type — the grader handles both object AND
910
919
  // JSON-string forms (see tests/grader.mjs "accepts JSON-string input").
911
920
  // Only enforce presence + light shape on signals (extras pass through).
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.691",
3
+ "version": "1.19.693",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.691",
9
+ "version": "1.19.693",
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.691",
3
+ "version": "1.19.693",
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",
@@ -111,10 +111,34 @@ function computeChangedFiles(baseline) {
111
111
  return out ? out.split('\n').map((s) => s.trim()).filter(Boolean) : [];
112
112
  }
113
113
 
114
+ // How much diff we are willing to CAPTURE. Distinct from GRADER_DIFF_CHAR_CAP, which is
115
+ // how much we are willing to SHOW the panel: the capture has to succeed first, and a
116
+ // capture that FAILS is indistinguishable from "nothing changed" unless we look.
117
+ const DIFF_CAPTURE_MAX_BYTES = 512 * 1024 * 1024;
118
+
114
119
  function computeDiffText(baseline) {
115
120
  if (!baseline) return '';
116
121
  // -M to track renames; --no-color so the subagent gets clean diff lines.
117
- const diff = gitOk(`git diff --no-color -M ${baseline}...HEAD`);
122
+ //
123
+ // maxBuffer is set EXPLICITLY, and that is the whole point of this function. execSync
124
+ // defaults to 1 MB and gitOk swallows the resulting ENOBUFS as null, so a large change
125
+ // silently produced an EMPTY diff: the panel then graded a zero-character string and
126
+ // would have returned a verdict on nothing at all, reporting diff_chars=0 in a log line
127
+ // that reads like an ordinary stat rather than an error. A 1,141-file rename (task
128
+ // 1003705) measured 9,808,703 chars and is what surfaced it; every big change had the
129
+ // same hole. The GRADER_DIFF_CHAR_CAP truncation below still bounds what the subagent is
130
+ // asked to read — this only stops the capture itself from failing.
131
+ const diff = gitOk(`git diff --no-color -M ${baseline}...HEAD`, { maxBuffer: DIFF_CAPTURE_MAX_BYTES });
132
+ if (diff === null) {
133
+ // Capture failed even at that size. Fall back to the stat summary so the panel gets the
134
+ // SHAPE of the change and can Read any file in its post-resolve state — strictly better
135
+ // than an empty string, which reads as "no changes" and grades as a pass.
136
+ // gitOk returns null ONLY on failure; a genuinely empty diff comes back as ''.
137
+ const stat = gitOk(`git diff --no-color -M --stat ${baseline}...HEAD`, { maxBuffer: 64 * 1024 * 1024 });
138
+ return stat
139
+ ? `[diff too large to capture in full — stat summary only. Read any file for its post-resolve state.]\n\n${stat}`
140
+ : '';
141
+ }
118
142
  if (!diff) return '';
119
143
  if (diff.length > GRADER_DIFF_CHAR_CAP) {
120
144
  const head = diff.slice(0, GRADER_DIFF_CHAR_CAP);
@@ -147,11 +171,25 @@ function diffHash(rawDiff) {
147
171
  }
148
172
 
149
173
  // Compute the hash of the full (untruncated) diff against baseline. Returns
150
- // null when baseline is unavailable so callers can treat null as "unknown".
174
+ // null when baseline is unavailable, OR when the diff cannot be captured, so
175
+ // callers can treat null as "unknown".
176
+ //
177
+ // SAME BUFFER BUG AS computeDiffText, AND A WORSE CONSEQUENCE. This ran gitOk at
178
+ // execSync's default 1 MB, so a large diff came back null and the old
179
+ // `diffHash(raw || '')` hashed the EMPTY STRING — the same fixed SHA-256 for every
180
+ // large diff. shouldReuseGrade compares that hash against the stored grade's
181
+ // diff_hash, so two genuinely different large diffs on one task collided and a stale
182
+ // PASS could be reused on changed code, skipping the panel outright. That is the
183
+ // silent-pass failure this task exists to remove, reappearing in the reuse path.
184
+ //
185
+ // Returning null on a failed capture is the fail-safe answer, not a cosmetic one:
186
+ // shouldReuseGrade already refuses a falsy currentDiffHash, so "unknown" now means
187
+ // "run the panel", where "" meant "matches every other unknown".
151
188
  function computeRawDiffHash(baseline) {
152
189
  if (!baseline) return null;
153
- const raw = gitOk(`git diff --no-color -M ${baseline}...HEAD`);
154
- return diffHash(raw || '');
190
+ const raw = gitOk(`git diff --no-color -M ${baseline}...HEAD`, { maxBuffer: DIFF_CAPTURE_MAX_BYTES });
191
+ if (raw === null) return null; // capture failed — never collapse that to hash('')
192
+ return diffHash(raw);
155
193
  }
156
194
 
157
195
  // Pure predicate: should we reuse the stored grade instead of running the
@@ -433,6 +433,14 @@ module.exports = function buildMeRouter() {
433
433
  // builder is uncapped (no cap set — the default; ADR 0085), in which case
434
434
  // level is always 'ok'. The whole object is null only if the read errored.
435
435
  cost: costStatus,
436
+ // task 1003453: the task-kind vocabulary, so a surface that offers a kind
437
+ // filter BUILDS it from the enum instead of retyping it. The board and the
438
+ // goals page each carried their own hardcoded copy, and both had gone stale
439
+ // against the DB — 'verify' existed, was claimable, and could not be
440
+ // filtered for. The lifecycle port's VALID_KINDS is the SSOT; [] when the
441
+ // lifecycle module is off, which reads as "no vocabulary served" and leaves
442
+ // the filter with just its '— any —' row rather than a wrong list.
443
+ task_kinds: lc() ? lc().TASK_KINDS : [],
436
444
  // task 1268: the per-builder "wandering" knob, resolved to an effective
437
445
  // level (clamped by rank) + its behavior contract. The Conductor + the
438
446
  // /settings UI read this; the builder-settings port's describeWandering is
@@ -94,7 +94,15 @@ function buildGdsRouter() {
94
94
  // task 1003109: the ship-time task visual — a base64 image up to ~5.3 MB
95
95
  // encoded (4 MB decoded). Same dynamic-:id pattern as publish-branch above;
96
96
  // modules/lifecycle/routes/task-visuals.js mounts its own 8 MB parser.
97
- /^\/tasks\/\d+\/visual$/.test(req.path)
97
+ /^\/tasks\/\d+\/visual$/.test(req.path) ||
98
+ // task 1003850: the grade body carries committed_files — EVERY changed path, tens of
99
+ // KB on its own for a mass change — plus the panel's findings. A 1,141-file rename
100
+ // (task 1003705) produced a body over the 64 KB cap, so the grade could not be
101
+ // recorded at all (413 payload_too_large) and fully verified work stranded at
102
+ // 'completed'. The list is NOT trimmable to fit: modules/grading/grader-score.js runs
103
+ // the server-side permission-path pre-pass against it (SR-9 / SEC #863), so a short
104
+ // list would quietly weaken a security check rather than merely lose detail.
105
+ /^\/tasks\/\d+\/grade$/.test(req.path)
98
106
  ) return next();
99
107
  return globalJson(req, res, next);
100
108
  });
package/src/module-api.js CHANGED
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
71
71
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
72
72
  // the entry to that file. Look for a version's history there, not here.
73
73
  // ---------------------------------------------------------------------------
74
- const CORE_VERSION = '1.19.691'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.693'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
75
75
 
76
76
  // A namespaced logger so a module's log lines are attributable + consistent.
77
77
  // Usage: const log = api.logger('dev-box'); log.info('mounted');