@bongos/core 1.21.24 → 1.21.26
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 +13 -13
- package/.claude/skills/idea-triage/SKILL.md +3 -3
- package/docs/module-api-changelog.md +4 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/gds/triage.js +14 -8
- package/src/module-api.js +1 -1
- package/tests/idea_triage.mjs +40 -0
package/.bongos-core.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.21.
|
|
6
|
-
"core_contract": "1.21.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.21.26",
|
|
6
|
+
"core_contract": "1.21.26",
|
|
7
|
+
"source_commit": "a0fe60e1a8c588d4e9913bcdffda9edae3267a4a",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-10-
|
|
9
|
+
"built_at": "2026-10-03T02:56:29.128Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 580,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 3506,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "bf858332458c31c371fdb0605eb745807f1f5ae5b4c42f85c6b015f7a7a36a4d",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -192,7 +192,7 @@
|
|
|
192
192
|
{
|
|
193
193
|
"path": ".claude/skills/idea-triage/SKILL.md",
|
|
194
194
|
"mode": "0000644",
|
|
195
|
-
"sha256": "
|
|
195
|
+
"sha256": "628a9880309ba370151becbebd7497972006716ec955d06bb51a79d335dbd54f"
|
|
196
196
|
},
|
|
197
197
|
{
|
|
198
198
|
"path": ".claude/skills/merge-mode/SKILL.md",
|
|
@@ -2832,7 +2832,7 @@
|
|
|
2832
2832
|
{
|
|
2833
2833
|
"path": "docs/module-api-changelog.md",
|
|
2834
2834
|
"mode": "0000644",
|
|
2835
|
-
"sha256": "
|
|
2835
|
+
"sha256": "8b98f13ef9c22f6d73ab2065a1563410301cb24edcc3d83b81a34f453a814c6c"
|
|
2836
2836
|
},
|
|
2837
2837
|
{
|
|
2838
2838
|
"path": "docs/modules-contract.md",
|
|
@@ -9787,12 +9787,12 @@
|
|
|
9787
9787
|
{
|
|
9788
9788
|
"path": "package-lock.json",
|
|
9789
9789
|
"mode": "0000644",
|
|
9790
|
-
"sha256": "
|
|
9790
|
+
"sha256": "b89ac1d6344bc2814145aee2e1a10552fb7bc4b24da346f4dcca305f47a355b7"
|
|
9791
9791
|
},
|
|
9792
9792
|
{
|
|
9793
9793
|
"path": "package.json",
|
|
9794
9794
|
"mode": "0000644",
|
|
9795
|
-
"sha256": "
|
|
9795
|
+
"sha256": "13606c58a4cce9dbc00d018a3f3067e43d0b9d1ee553fb6069ed9b7493166659"
|
|
9796
9796
|
},
|
|
9797
9797
|
{
|
|
9798
9798
|
"path": "public-docs/index.html",
|
|
@@ -9812,7 +9812,7 @@
|
|
|
9812
9812
|
{
|
|
9813
9813
|
"path": "release-notes.json",
|
|
9814
9814
|
"mode": "0000644",
|
|
9815
|
-
"sha256": "
|
|
9815
|
+
"sha256": "270e78b9cc46481cf3aa2fb599bcbdc4bca8e4c0f5876371861e68610353f537"
|
|
9816
9816
|
},
|
|
9817
9817
|
{
|
|
9818
9818
|
"path": "scripts/bongos-mcp.js",
|
|
@@ -11462,7 +11462,7 @@
|
|
|
11462
11462
|
{
|
|
11463
11463
|
"path": "scripts/gds/triage.js",
|
|
11464
11464
|
"mode": "0000644",
|
|
11465
|
-
"sha256": "
|
|
11465
|
+
"sha256": "0e03060897a7d708ad54936d9942e130be4c2f22be01821ab98e7cf30ee4ef11"
|
|
11466
11466
|
},
|
|
11467
11467
|
{
|
|
11468
11468
|
"path": "scripts/gds/turn-end-sound.js",
|
|
@@ -12037,7 +12037,7 @@
|
|
|
12037
12037
|
{
|
|
12038
12038
|
"path": "src/module-api.js",
|
|
12039
12039
|
"mode": "0000644",
|
|
12040
|
-
"sha256": "
|
|
12040
|
+
"sha256": "91b122a9d3facf064489524a4ea1fa610466b29b855c98841de21c0e4c05e042"
|
|
12041
12041
|
},
|
|
12042
12042
|
{
|
|
12043
12043
|
"path": "src/module-loader/catalog.js",
|
|
@@ -14717,7 +14717,7 @@
|
|
|
14717
14717
|
{
|
|
14718
14718
|
"path": "tests/idea_triage.mjs",
|
|
14719
14719
|
"mode": "0000644",
|
|
14720
|
-
"sha256": "
|
|
14720
|
+
"sha256": "6960c9628f6b41ef78db6d09bcbb00ca4afb6b93c4bf57a6c39e86f6e09c4c66"
|
|
14721
14721
|
},
|
|
14722
14722
|
{
|
|
14723
14723
|
"path": "tests/idea_triage_grade.mjs",
|
|
@@ -58,7 +58,7 @@ Bug reports from Discord have their own channel, `#bugs`, which does route.
|
|
|
58
58
|
rather than just promoting past: it usually means the filer named no goal, and the
|
|
59
59
|
capture-time suggester (R08/R09/R11 surfaces) is where the fix belongs.
|
|
60
60
|
|
|
61
|
-
> **This skill is no longer the only way to triage.** Since task 1002447 a Metic+ builder can promote, discard and merge from the **hall** — the Home "Needs your attention" card's idea rows each carry a *Triage* verb that opens the record overlay, where all three forms live. No Claude session involved. What this skill adds over the hall is the **parallel development pass** (Step 1.
|
|
61
|
+
> **This skill is no longer the only way to triage.** Since task 1002447 a Metic+ builder can promote, discard and merge from the **hall** — the Home "Needs your attention" card's idea rows each carry a *Triage* verb that opens the record overlay, where all three forms live. No Claude session involved. What this skill adds over the hall is the **parallel development pass** (Step 1.5b) — an idea fleshed out into open questions/risks before the verdict — so reach for it when the queue needs judgment, and use the hall when it just needs clearing.
|
|
62
62
|
|
|
63
63
|
## Rank gate — Metic+ only
|
|
64
64
|
|
|
@@ -119,7 +119,7 @@ show the same idea the same way.
|
|
|
119
119
|
|
|
120
120
|
**Step 1.5a — batch near-duplicate ideas BEFORE developing anything (task 1001465).**
|
|
121
121
|
|
|
122
|
-
Run `clusterIdeas` (`scripts/gds/triage.js` — pure, no I/O): it groups open quick
|
|
122
|
+
Run `clusterIdeas` (`scripts/gds/triage.js` — pure, no I/O): it groups open quick ideas (not Full Ideas, not sparks) that share a `kind` and a high title/body word overlap. For each cluster it returns, offer the user ONE decision covering the whole group instead of walking each member — this is the actual fix for "triage costs a ton of credits" (idea 377): a backlog of near-identical asks used to cost one full development + verdict PER idea, and now costs one:
|
|
123
123
|
|
|
124
124
|
> Cluster of 3 likely duplicates: #101 "Add dark mode toggle", #145 "Dark mode support", #203 "Support a dark theme". **Keep #101 and merge the rest, discard all, walk them individually, or skip?**
|
|
125
125
|
|
|
@@ -165,7 +165,7 @@ Present each idea with:
|
|
|
165
165
|
- title
|
|
166
166
|
- body_md (full)
|
|
167
167
|
- captured_at + captured_by
|
|
168
|
-
- **its developed block** (from Step 1.
|
|
168
|
+
- **its developed block** (from Step 1.5b): summary, integration surfaces, open questions, challenges, risks, rough effort, and the subagent's recommendation
|
|
169
169
|
|
|
170
170
|
Then ask the user (concise prompt — Lars is an EE, not a software engineer; frame in product/outcome terms). Lead with the developed open questions, since those are what the Archon actually needs to decide:
|
|
171
171
|
|
|
@@ -2875,5 +2875,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2875
2875
|
landed since 1.21.22 with no explicit bump. run 37078725783. (task 1002620)
|
|
2876
2876
|
1.21.24 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2877
2877
|
landed since 1.21.23 with no explicit bump. run 37080562697. (task 1002620)
|
|
2878
|
+
1.21.25 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2879
|
+
landed since 1.21.24 with no explicit bump. run 37089254828. (task 1002620)
|
|
2880
|
+
1.21.26 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2881
|
+
landed since 1.21.25 with no explicit bump. run 37091481670. (task 1002620)
|
|
2878
2882
|
---------------------------------------------------------------------------
|
|
2879
2883
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.26",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.26",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.26",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -8900,5 +8900,17 @@
|
|
|
8900
8900
|
"id": "1002830",
|
|
8901
8901
|
"text": "Sessions now has a Read button on each row that opens the full conversation for that session. Only the session's owner or an administrator can read it, and administrator reads are logged."
|
|
8902
8902
|
}
|
|
8903
|
+
],
|
|
8904
|
+
"1.21.25": [
|
|
8905
|
+
{
|
|
8906
|
+
"id": "1004533",
|
|
8907
|
+
"text": "Twelve goal 1000045 tasks that were counted toward other goals' criteria now count toward their own, so that goal's progress reads true."
|
|
8908
|
+
}
|
|
8909
|
+
],
|
|
8910
|
+
"1.21.26": [
|
|
8911
|
+
{
|
|
8912
|
+
"id": "1003623",
|
|
8913
|
+
"text": "Fixed idea-triage cluster handling: 'keep #N' and 'merge #N' now parse; skipped clusters count toward the summary; spark ideas excluded from batch clustering; stale Step 1.5 refs now point at 1.5b. Tests added in tests/idea_tr"
|
|
8914
|
+
}
|
|
8903
8915
|
]
|
|
8904
8916
|
}
|
package/scripts/gds/triage.js
CHANGED
|
@@ -400,10 +400,12 @@ function clusterRecords(records, { textOf, kindOf, timeOf, threshold = 0.34, min
|
|
|
400
400
|
|
|
401
401
|
// The idea-shaped call: Full Ideas are never eligible (they get their own
|
|
402
402
|
// shape recommendation, SKILL.md Step 2.5, and batching one away would skip
|
|
403
|
-
// that judgment),
|
|
404
|
-
//
|
|
403
|
+
// that judgment), nor are sparks (not triage's to convert, SKILL.md); the rest
|
|
404
|
+
// cluster on title+body_md / kind / captured_at. Same signature as before the
|
|
405
|
+
// clusterRecords extraction.
|
|
406
|
+
const NON_CLUSTER_GRADES = new Set(['full', 'spark']);
|
|
405
407
|
function clusterIdeas(ideas, { threshold = 0.34, minShared = 2 } = {}) {
|
|
406
|
-
const eligible = ideas.filter((i) => (i.grade || 'quick')
|
|
408
|
+
const eligible = ideas.filter((i) => !NON_CLUSTER_GRADES.has(i.grade || 'quick'));
|
|
407
409
|
return clusterRecords(eligible, {
|
|
408
410
|
textOf: (i) => `${i.title || ''} ${i.body_md || ''}`,
|
|
409
411
|
kindOf: (i) => i.kind || null,
|
|
@@ -431,7 +433,7 @@ async function handleCluster(rl, cluster, clusteredIds, state) {
|
|
|
431
433
|
while (!handled) {
|
|
432
434
|
const ans = (await prompt(rl, ` [k]eep #${cluster[0].id} & merge the rest / [d]iscard all / [i]ndividually / [s]kip cluster > `)).trim().toLowerCase();
|
|
433
435
|
|
|
434
|
-
if (ans === 's' || ans === 'skip' || ans === '') { handled = true; break; }
|
|
436
|
+
if (ans === 's' || ans === 'skip' || ans === '') { state.summary.skipped += cluster.length; handled = true; break; }
|
|
435
437
|
|
|
436
438
|
if (ans === 'i' || ans === 'individually') {
|
|
437
439
|
for (const m of cluster) clusteredIds.delete(m.id);
|
|
@@ -458,7 +460,7 @@ async function handleCluster(rl, cluster, clusteredIds, state) {
|
|
|
458
460
|
handled = true; break;
|
|
459
461
|
}
|
|
460
462
|
|
|
461
|
-
if (
|
|
463
|
+
if (/^k(eep)?(\s|#|$)/.test(ans)) {
|
|
462
464
|
let canonicalId = cluster[0].id;
|
|
463
465
|
const inline = ans.replace(/^k(eep)?(\s|#)*/, '').trim();
|
|
464
466
|
if (inline) {
|
|
@@ -535,6 +537,9 @@ async function main() {
|
|
|
535
537
|
await handleCluster(rl, cluster, clusteredIds, state);
|
|
536
538
|
}
|
|
537
539
|
}
|
|
540
|
+
// Cluster verdicts bump the summary too, but those ideas are already out of
|
|
541
|
+
// `ideas`; remember the tally so "unseen" only counts the individual walk.
|
|
542
|
+
const clusterHandled = summary.promoted + summary.discarded + summary.merged + summary.skipped;
|
|
538
543
|
ideas = ideas.filter((i) => !clusteredIds.has(i.id));
|
|
539
544
|
if (ideas.length === 0) {
|
|
540
545
|
console.log('\nnothing left to walk individually — every open idea was resolved in a batch cluster verdict.');
|
|
@@ -593,10 +598,10 @@ async function main() {
|
|
|
593
598
|
break;
|
|
594
599
|
}
|
|
595
600
|
|
|
596
|
-
if (
|
|
601
|
+
if (/^m(erge)?(\s|#|$)/.test(ans)) {
|
|
597
602
|
// Accept "m 12" or "m #12" inline, otherwise prompt.
|
|
598
603
|
let target = null;
|
|
599
|
-
const inline = ans.replace(/^m(\s|#)*/, '').trim();
|
|
604
|
+
const inline = ans.replace(/^m(erge)?(\s|#)*/, '').trim();
|
|
600
605
|
if (inline) target = Number(inline.replace(/^#/, ''));
|
|
601
606
|
if (!Number.isFinite(target) || target <= 0) {
|
|
602
607
|
const a = (await prompt(rl, ' merge into idea #: ')).trim().replace(/^#/, '');
|
|
@@ -700,7 +705,7 @@ async function main() {
|
|
|
700
705
|
rl.close();
|
|
701
706
|
}
|
|
702
707
|
|
|
703
|
-
const walked = summary.promoted + summary.discarded + summary.merged + summary.skipped;
|
|
708
|
+
const walked = summary.promoted + summary.discarded + summary.merged + summary.skipped - clusterHandled;
|
|
704
709
|
const remaining = ideas.length - walked;
|
|
705
710
|
|
|
706
711
|
console.log('\n' + '═'.repeat(72));
|
|
@@ -730,6 +735,7 @@ module.exports = {
|
|
|
730
735
|
loadDevelopment,
|
|
731
736
|
formatDevelopment,
|
|
732
737
|
clusterIdeas,
|
|
738
|
+
handleCluster,
|
|
733
739
|
clusterRecords,
|
|
734
740
|
significantWords,
|
|
735
741
|
jaccard,
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.26'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
package/tests/idea_triage.mjs
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
// Run: node tests/idea_triage.mjs
|
|
19
19
|
|
|
20
20
|
import { strict as assert } from 'node:assert';
|
|
21
|
+
import fs from 'node:fs';
|
|
21
22
|
import { createRequire } from 'node:module';
|
|
22
23
|
|
|
23
24
|
const require = createRequire(import.meta.url);
|
|
@@ -162,5 +163,44 @@ await test('listOpenIdeas selects the triage-provenance columns', async () => {
|
|
|
162
163
|
assert.match(select.sql, /triage_note/);
|
|
163
164
|
});
|
|
164
165
|
|
|
166
|
+
// ── task 1003623: batch-cluster follow-ups ─────────────────────────────────
|
|
167
|
+
const triage = require('../scripts/gds/triage.js');
|
|
168
|
+
const fakeRl = (answers) => {
|
|
169
|
+
const asked = [];
|
|
170
|
+
return { asked, question: (q, cb) => { asked.push(q); cb(answers.shift() ?? 's'); } };
|
|
171
|
+
};
|
|
172
|
+
const pair = [
|
|
173
|
+
{ id: 1, title: 'a', kind: 'bug' },
|
|
174
|
+
{ id: 2, title: 'b', kind: 'bug' },
|
|
175
|
+
];
|
|
176
|
+
const freshState = () => ({ summary: { promoted: 0, discarded: 0, merged: 0, skipped: 0 }, discardedIds: [], mergedIds: [] });
|
|
177
|
+
|
|
178
|
+
await test('cluster verdict accepts the natural "keep #N" phrasing', async () => {
|
|
179
|
+
// A one-member cluster keeps the verdict free of API writes: nothing to merge.
|
|
180
|
+
const rl = fakeRl(['keep #1', 's']);
|
|
181
|
+
const state = freshState();
|
|
182
|
+
await triage.handleCluster(rl, [pair[0]], new Set([1]), state);
|
|
183
|
+
assert.equal(rl.asked.length, 1, 'keep #1 was understood, not re-prompted as unknown');
|
|
184
|
+
assert.equal(state.summary.skipped, 0);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await test('skipping a cluster counts every member as skipped', async () => {
|
|
188
|
+
const rl = fakeRl(['s']);
|
|
189
|
+
const state = freshState();
|
|
190
|
+
await triage.handleCluster(rl, pair, new Set([1, 2]), state);
|
|
191
|
+
assert.equal(state.summary.skipped, 2);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
await test('spark-grade ideas are not eligible for batch clustering', async () => {
|
|
195
|
+
const mk = (id, grade) => ({ id, grade, kind: 'idea', title: 'dashboard filter export widget', body_md: 'dashboard filter export widget', captured_at: '2026-01-01' });
|
|
196
|
+
assert.equal(triage.clusterIdeas([mk(1, 'spark'), mk(2, 'spark')]).clusters.length, 0);
|
|
197
|
+
assert.equal(triage.clusterIdeas([mk(1, 'quick'), mk(2, 'quick')]).clusters.length, 1);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
await test('SKILL.md has no stale bare "Step 1.5" cross-references', async () => {
|
|
201
|
+
const md = fs.readFileSync(new URL('../.claude/skills/idea-triage/SKILL.md', import.meta.url), 'utf8');
|
|
202
|
+
assert.ok(!/Step 1\.5(?![ab])/.test(md));
|
|
203
|
+
});
|
|
204
|
+
|
|
165
205
|
console.log(`\nIdea triage tests: ${passed} passed, ${failed} failed`);
|
|
166
206
|
process.exit(failed === 0 ? 0 : 1);
|