@bongos/core 1.21.180 → 1.21.181
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 +18 -13
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/bongos/repo-metrics.js +50 -33
- package/src/module-api.js +1 -1
- package/tests/repo_metrics_dead_exports.mjs +57 -0
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/bongos/package-core.js",
|
|
5
|
-
"core_version": "1.21.
|
|
6
|
-
"core_contract": "1.21.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.21.181",
|
|
6
|
+
"core_contract": "1.21.181",
|
|
7
|
+
"source_commit": "677ee2b818cf829dddd5434301b158be3408fbd5",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-10-
|
|
9
|
+
"built_at": "2026-10-26T01:16:25.738Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 616,
|
|
13
13
|
"agent_docs_stubbed": 29,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 3539,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 4185,
|
|
20
|
+
"tree_sha256": "e69b48d53a056c99114f3c5904e69828363e91f5f0df70b7b599e61e8e299d1d",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2937,7 +2937,7 @@
|
|
|
2937
2937
|
{
|
|
2938
2938
|
"path": "docs/module-api-changelog.md",
|
|
2939
2939
|
"mode": "0000644",
|
|
2940
|
-
"sha256": "
|
|
2940
|
+
"sha256": "4835f21d9d6cf7eaa82821e1bb5eb4c79493267300ef852ad1ca1c05b4a41e65"
|
|
2941
2941
|
},
|
|
2942
2942
|
{
|
|
2943
2943
|
"path": "docs/modules-contract.md",
|
|
@@ -10827,12 +10827,12 @@
|
|
|
10827
10827
|
{
|
|
10828
10828
|
"path": "package-lock.json",
|
|
10829
10829
|
"mode": "0000644",
|
|
10830
|
-
"sha256": "
|
|
10830
|
+
"sha256": "4d3ecd3d1a0b7e6602da53e83e88c204ab82a45ca83ae3d24979746970f15ac9"
|
|
10831
10831
|
},
|
|
10832
10832
|
{
|
|
10833
10833
|
"path": "package.json",
|
|
10834
10834
|
"mode": "0000644",
|
|
10835
|
-
"sha256": "
|
|
10835
|
+
"sha256": "2a01fd05d918fc38ad18d1cc6305681129b6115a4eab33f7d1fa4d34b8da2e44"
|
|
10836
10836
|
},
|
|
10837
10837
|
{
|
|
10838
10838
|
"path": "public-docs/index.html",
|
|
@@ -10852,7 +10852,7 @@
|
|
|
10852
10852
|
{
|
|
10853
10853
|
"path": "release-notes.json",
|
|
10854
10854
|
"mode": "0000644",
|
|
10855
|
-
"sha256": "
|
|
10855
|
+
"sha256": "2a6e6fa89b09fd51aa8fae09c92eec30b6d0823dcdf3eaeda63826e7269a46ea"
|
|
10856
10856
|
},
|
|
10857
10857
|
{
|
|
10858
10858
|
"path": "scripts/bongos/CLAUDE.md",
|
|
@@ -12052,7 +12052,7 @@
|
|
|
12052
12052
|
{
|
|
12053
12053
|
"path": "scripts/bongos/repo-metrics.js",
|
|
12054
12054
|
"mode": "0000644",
|
|
12055
|
-
"sha256": "
|
|
12055
|
+
"sha256": "15dee800bf6fc5aca8a9d332854825b1d0081f2269ccdf69c2b5d3e1010009cb"
|
|
12056
12056
|
},
|
|
12057
12057
|
{
|
|
12058
12058
|
"path": "scripts/bongos/retired-core-skills.json",
|
|
@@ -14677,7 +14677,7 @@
|
|
|
14677
14677
|
{
|
|
14678
14678
|
"path": "src/module-api.js",
|
|
14679
14679
|
"mode": "0000644",
|
|
14680
|
-
"sha256": "
|
|
14680
|
+
"sha256": "c48862d3a5d62afff6c50b8de356b68ec2ef4f22ced0c0caa36a99d5f135747a"
|
|
14681
14681
|
},
|
|
14682
14682
|
{
|
|
14683
14683
|
"path": "src/module-loader/catalog.js",
|
|
@@ -19604,6 +19604,11 @@
|
|
|
19604
19604
|
"mode": "0000644",
|
|
19605
19605
|
"sha256": "5c964c162b87f2e73f6dc3343b1c4be886b7d3e0d29c01d51280dc790fc218e7"
|
|
19606
19606
|
},
|
|
19607
|
+
{
|
|
19608
|
+
"path": "tests/repo_metrics_dead_exports.mjs",
|
|
19609
|
+
"mode": "0000644",
|
|
19610
|
+
"sha256": "f98a3d9635557d050f428c5ad3258ff1ef7652bf0f4fba87830cc09481b76e9b"
|
|
19611
|
+
},
|
|
19607
19612
|
{
|
|
19608
19613
|
"path": "tests/reward_gate.mjs",
|
|
19609
19614
|
"mode": "0000644",
|
|
@@ -3197,5 +3197,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3197
3197
|
landed since 1.21.178 with no explicit bump. run 40250732318. (task 1002620)
|
|
3198
3198
|
1.21.180 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3199
3199
|
landed since 1.21.179 with no explicit bump. run 40251859318. (task 1002620)
|
|
3200
|
+
1.21.181 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3201
|
+
landed since 1.21.180 with no explicit bump. run 40253320267. (task 1002620)
|
|
3200
3202
|
---------------------------------------------------------------------------
|
|
3201
3203
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.181",
|
|
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.181",
|
|
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.181",
|
|
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
|
@@ -10356,5 +10356,11 @@
|
|
|
10356
10356
|
"id": "1004875",
|
|
10357
10357
|
"text": "The unused-code check now covers modules/, the largest part of the codebase, and fails the build when someone adds a dead file or an unused export there. Five dead exports were removed along the way."
|
|
10358
10358
|
}
|
|
10359
|
+
],
|
|
10360
|
+
"1.21.181": [
|
|
10361
|
+
{
|
|
10362
|
+
"id": "1004892",
|
|
10363
|
+
"text": "The repo health dashboard's dead-code number now counts the same way the CI check does, so it no longer jumps by about 1,800 false alarms from modules/."
|
|
10364
|
+
}
|
|
10359
10365
|
]
|
|
10360
10366
|
}
|
|
@@ -37,6 +37,7 @@ const { execSync } = require('node:child_process');
|
|
|
37
37
|
const fs = require('node:fs');
|
|
38
38
|
const path = require('node:path');
|
|
39
39
|
const { hasFlag } = require('./cli-lib');
|
|
40
|
+
const { countKnipFindings } = require('./fitness-ratchets');
|
|
40
41
|
|
|
41
42
|
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
42
43
|
const TOP_N_LARGEST = 15;
|
|
@@ -151,36 +152,44 @@ function scanDeadCode() {
|
|
|
151
152
|
return { dead_exports: null, dead_files: null, knip_status: 'unparseable' };
|
|
152
153
|
}
|
|
153
154
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
const scored = scoreKnipReport(report);
|
|
156
|
+
if (!scored) return { dead_exports: null, dead_files: null, knip_status: 'unexpected_shape' };
|
|
157
|
+
return { ...scored, knip_status: 'ok' };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// knip JSON -> { dead_exports, dead_files }, or null on a shape we cannot read.
|
|
161
|
+
//
|
|
162
|
+
// dead_exports goes through the CI ratchet's own scorer, countKnipFindings, fed only
|
|
163
|
+
// the exports/types findings (task 1004892). Counting knip's raw list instead put
|
|
164
|
+
// ~1,800 exports under modules/ on the dashboard that ARE used: knip does not credit
|
|
165
|
+
// a CommonJS namespace use (`lib.x`), and modules/ is written that way. The scorer's
|
|
166
|
+
// name filter drops those, so this number and the ratchet's agree. `opts` passes
|
|
167
|
+
// through to the scorer (a test injects `wordIndex`).
|
|
168
|
+
//
|
|
169
|
+
// COST: the scorer builds its word index once per call (codeWordIndex walks the
|
|
170
|
+
// repo's .js/.mjs/.cjs/.html/.sh text) — measured ~2.3 s on this repo, beside a knip
|
|
171
|
+
// run that already takes far longer. It is deliberately NOT fed from
|
|
172
|
+
// gatherFileSizes()'s reads: that list is git-tracked .js/.mjs only, while the
|
|
173
|
+
// ratchet's index also reads .cjs/.html/.sh and skips docs/ — a different file set
|
|
174
|
+
// gives a different count, and matching CI's number is the whole point.
|
|
175
|
+
function scoreKnipReport(report, opts = {}) {
|
|
176
|
+
if (!report || typeof report !== 'object') return null;
|
|
177
|
+
const hasIssues = Array.isArray(report.issues);
|
|
178
|
+
if (!hasIssues && !Array.isArray(report.files)) return null;
|
|
159
179
|
let deadExports = null;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
(acc, issue) => acc + (issue.files === true || (Array.isArray(issue.files) && issue.files.length) ? 1 : 0),
|
|
170
|
-
0
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
// Some reporter modes additionally surface a flat unused-file list.
|
|
174
|
-
if (Array.isArray(report.files)) {
|
|
175
|
-
deadFiles = (deadFiles ?? 0) + report.files.length;
|
|
176
|
-
}
|
|
177
|
-
if (deadExports == null && deadFiles == null) {
|
|
178
|
-
return { dead_exports: null, dead_files: null, knip_status: 'unexpected_shape' };
|
|
179
|
-
}
|
|
180
|
-
} catch (_) {
|
|
181
|
-
return { dead_exports: null, dead_files: null, knip_status: 'unexpected_shape' };
|
|
180
|
+
let deadFiles = null;
|
|
181
|
+
if (hasIssues) {
|
|
182
|
+
const exportsOnly = report.issues.map((issue) => ({ file: issue.file, exports: issue.exports, types: issue.types }));
|
|
183
|
+
deadExports = countKnipFindings({ issues: exportsOnly }, opts);
|
|
184
|
+
deadFiles = report.issues.reduce(
|
|
185
|
+
// knip 6 gives an ARRAY here ([{ name }]), not a bool (task 1004875).
|
|
186
|
+
(acc, issue) => acc + (issue.files === true || (Array.isArray(issue.files) && issue.files.length) ? 1 : 0),
|
|
187
|
+
0
|
|
188
|
+
);
|
|
182
189
|
}
|
|
183
|
-
|
|
190
|
+
// Some reporter modes additionally surface a flat unused-file list.
|
|
191
|
+
if (Array.isArray(report.files)) deadFiles = (deadFiles ?? 0) + report.files.length;
|
|
192
|
+
return { dead_exports: deadExports, dead_files: deadFiles };
|
|
184
193
|
}
|
|
185
194
|
|
|
186
195
|
// ---------- branch hygiene ----------
|
|
@@ -287,7 +296,7 @@ async function upsertSnapshot(pool, snap) {
|
|
|
287
296
|
|
|
288
297
|
// ---------- main ----------
|
|
289
298
|
|
|
290
|
-
|
|
299
|
+
async function main() {
|
|
291
300
|
const args = process.argv.slice(2);
|
|
292
301
|
const asJson = hasFlag('--json', args);
|
|
293
302
|
const noWrite = hasFlag('--no-write', args);
|
|
@@ -360,7 +369,15 @@ async function upsertSnapshot(pool, snap) {
|
|
|
360
369
|
}
|
|
361
370
|
|
|
362
371
|
process.exit(0);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
module.exports = { scoreKnipReport };
|
|
375
|
+
|
|
376
|
+
// CLI only when run directly, so a test can require this file without it
|
|
377
|
+
// scanning the repo or writing to the DB (task 1004892).
|
|
378
|
+
if (require.main === module) {
|
|
379
|
+
main().catch((err) => {
|
|
380
|
+
console.error('fatal:', err);
|
|
381
|
+
process.exit(1);
|
|
382
|
+
});
|
|
383
|
+
}
|
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.181'; // 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');
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// tests/repo_metrics_dead_exports.mjs — repo-metrics scores dead exports the way the
|
|
2
|
+
// CI knip ratchet does (task 1004892).
|
|
3
|
+
//
|
|
4
|
+
// Once knip scanned modules/ (task 1004875), repo-metrics' raw export count jumped by
|
|
5
|
+
// ~1,800: knip does not credit a CommonJS namespace use (`lib.x`), and modules/ is
|
|
6
|
+
// written that way. The ratchet's scorer (countKnipFindings) filters those by name;
|
|
7
|
+
// repo-metrics must use the same scorer, so the dashboard number matches CI's.
|
|
8
|
+
// Hermetic: synthetic knip JSON and an injected word index, no knip run.
|
|
9
|
+
import assert from 'node:assert/strict';
|
|
10
|
+
import { test } from 'node:test';
|
|
11
|
+
import { createRequire } from 'node:module';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
17
|
+
const rm = require(path.join(ROOT, 'scripts/bongos/repo-metrics.js'));
|
|
18
|
+
const fr = require(path.join(ROOT, 'scripts/bongos/fitness-ratchets.js'));
|
|
19
|
+
|
|
20
|
+
const issue = (file, extra) => ({ file, files: [], exports: [], types: [], dependencies: [], unlisted: [], ...extra });
|
|
21
|
+
|
|
22
|
+
test('a modules/ export used through a namespace elsewhere is not a dead export', () => {
|
|
23
|
+
const wordIndex = new Map([
|
|
24
|
+
['usedViaNamespace', true], // spelled in 2+ files
|
|
25
|
+
['onlyHere', 'modules/x/lib.js'], // spelled only in its own file
|
|
26
|
+
]);
|
|
27
|
+
const report = {
|
|
28
|
+
issues: [
|
|
29
|
+
issue('modules/x/lib.js', {
|
|
30
|
+
exports: [{ name: 'usedViaNamespace' }, { name: 'onlyHere' }],
|
|
31
|
+
types: [{ name: 'neverSpelled' }],
|
|
32
|
+
}),
|
|
33
|
+
issue('src/y.js', { exports: [{ name: 'usedViaNamespace' }] }), // outside modules/: knip's verdict stands
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
const scored = rm.scoreKnipReport(report, { wordIndex });
|
|
37
|
+
assert.equal(scored.dead_exports, 3); // onlyHere + neverSpelled + the src/ one
|
|
38
|
+
// ...and that is exactly the ratchet scorer's count for the same export/type findings.
|
|
39
|
+
assert.equal(scored.dead_exports, fr.countKnipFindings(report, { wordIndex }));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('non-export findings (deps, unlisted) do not leak into dead_exports; dead files still count', () => {
|
|
43
|
+
const report = {
|
|
44
|
+
issues: [
|
|
45
|
+
issue('src/a.js', { files: [{ name: 'src/a.js' }], dependencies: [{ name: 'left-pad' }], exports: [{ name: 'x' }] }),
|
|
46
|
+
issue('src/b.js', { unlisted: [{ name: 'chalk' }] }),
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
const scored = rm.scoreKnipReport(report, { wordIndex: new Map() });
|
|
50
|
+
assert.equal(scored.dead_exports, 1);
|
|
51
|
+
assert.equal(scored.dead_files, 1);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('a report with no issues list is an unexpected shape (null), not zero', () => {
|
|
55
|
+
assert.equal(rm.scoreKnipReport({}, { wordIndex: new Map() }), null);
|
|
56
|
+
assert.equal(rm.scoreKnipReport(null, { wordIndex: new Map() }), null);
|
|
57
|
+
});
|