@bongos/core 1.19.652 → 1.19.653
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 +12 -12
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/fitness-checks-vocabulary.js +89 -8
- package/scripts/gds/fitness.js +4 -3
- package/src/module-api.js +1 -1
- package/tests/government_vocabulary.mjs +98 -3
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.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.653",
|
|
6
|
+
"core_contract": "1.19.653",
|
|
7
|
+
"source_commit": "eff58cdbf33eb107723b56ef2f119c578d628d5e",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-10T23:48:13.514Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 472,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 2606,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "f2c83acf6434a7c56482adc7efcd782b2577ba4f4d6092d74e2ea143b5c6f90c",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/backlog-review/SKILL.md",
|
|
@@ -2782,7 +2782,7 @@
|
|
|
2782
2782
|
{
|
|
2783
2783
|
"path": "docs/module-api-changelog.md",
|
|
2784
2784
|
"mode": "0000644",
|
|
2785
|
-
"sha256": "
|
|
2785
|
+
"sha256": "11da08cbbf3f33e0a95df12baddcfc0bddba2b4bb4cf7c77f2a818c7d1dbfba8"
|
|
2786
2786
|
},
|
|
2787
2787
|
{
|
|
2788
2788
|
"path": "docs/modules-contract.md",
|
|
@@ -7742,12 +7742,12 @@
|
|
|
7742
7742
|
{
|
|
7743
7743
|
"path": "package-lock.json",
|
|
7744
7744
|
"mode": "0000644",
|
|
7745
|
-
"sha256": "
|
|
7745
|
+
"sha256": "42dddf576e9fb7f8d33e39dff4244f6105bd4bf78b150cf700d819f763ab6b8d"
|
|
7746
7746
|
},
|
|
7747
7747
|
{
|
|
7748
7748
|
"path": "package.json",
|
|
7749
7749
|
"mode": "0000644",
|
|
7750
|
-
"sha256": "
|
|
7750
|
+
"sha256": "12d491b531085f6ed22cbda1b8f7485401c186fd10b1cb4170e8af6bed174d95"
|
|
7751
7751
|
},
|
|
7752
7752
|
{
|
|
7753
7753
|
"path": "public-docs/index.html",
|
|
@@ -8187,7 +8187,7 @@
|
|
|
8187
8187
|
{
|
|
8188
8188
|
"path": "scripts/gds/fitness-checks-vocabulary.js",
|
|
8189
8189
|
"mode": "0000644",
|
|
8190
|
-
"sha256": "
|
|
8190
|
+
"sha256": "f64905f88718e5617ee97584dada757367497eed350bfb41814d257c9d2ec680"
|
|
8191
8191
|
},
|
|
8192
8192
|
{
|
|
8193
8193
|
"path": "scripts/gds/fitness-checks-write-validation.js",
|
|
@@ -8207,7 +8207,7 @@
|
|
|
8207
8207
|
{
|
|
8208
8208
|
"path": "scripts/gds/fitness.js",
|
|
8209
8209
|
"mode": "0000644",
|
|
8210
|
-
"sha256": "
|
|
8210
|
+
"sha256": "b30c5c2b3bf6465a63a7124327ad40f94987088d1d87fcc110fec9281539a0a1"
|
|
8211
8211
|
},
|
|
8212
8212
|
{
|
|
8213
8213
|
"path": "scripts/gds/gate-review.js",
|
|
@@ -9502,7 +9502,7 @@
|
|
|
9502
9502
|
{
|
|
9503
9503
|
"path": "src/module-api.js",
|
|
9504
9504
|
"mode": "0000644",
|
|
9505
|
-
"sha256": "
|
|
9505
|
+
"sha256": "03201e0e94d7ba4d755c8d0efae051208913faa2a7f654b231d3cb9ded49bd9b"
|
|
9506
9506
|
},
|
|
9507
9507
|
{
|
|
9508
9508
|
"path": "src/module-loader/catalog.js",
|
|
@@ -10957,7 +10957,7 @@
|
|
|
10957
10957
|
{
|
|
10958
10958
|
"path": "tests/government_vocabulary.mjs",
|
|
10959
10959
|
"mode": "0000644",
|
|
10960
|
-
"sha256": "
|
|
10960
|
+
"sha256": "444f0638dccebc42e5a371820ed565c665d4373c198e1581389ab222bf267c5e"
|
|
10961
10961
|
},
|
|
10962
10962
|
{
|
|
10963
10963
|
"path": "tests/grade_aggregates_unavailable.mjs",
|
|
@@ -1753,5 +1753,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1753
1753
|
landed since 1.19.650 with no explicit bump. run 34526936330. (task 1002620)
|
|
1754
1754
|
1.19.652 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1755
1755
|
landed since 1.19.651 with no explicit bump. run 34529655648. (task 1002620)
|
|
1756
|
+
1.19.653 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1757
|
+
landed since 1.19.652 with no explicit bump. run 34543713846. (task 1002620)
|
|
1756
1758
|
---------------------------------------------------------------------------
|
|
1757
1759
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.653",
|
|
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.653",
|
|
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.653",
|
|
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",
|
|
@@ -48,11 +48,36 @@ const VOCAB_ALLOW = [
|
|
|
48
48
|
},
|
|
49
49
|
];
|
|
50
50
|
|
|
51
|
-
// A line CITING a
|
|
52
|
-
// docs/governance/ evidence folder,
|
|
53
|
-
//
|
|
54
|
-
// reference to one is a citation, not drift.
|
|
55
|
-
|
|
51
|
+
// A line CITING a durable artifact by its real name: an ADR filename, the
|
|
52
|
+
// docs/governance/ evidence folder, one of the already-applied migrations, or
|
|
53
|
+
// the cross-goal pass-event contract. These names are permanent (see the
|
|
54
|
+
// header), so a reference to one is a citation, not drift.
|
|
55
|
+
//
|
|
56
|
+
// The migration family is `govern(ance|ment)_NNN` on purpose. government_001
|
|
57
|
+
// RENAMED the governance_NNN family to government_NNN, so half the applied
|
|
58
|
+
// migrations on disk carry each spelling and BOTH are permanent — and the
|
|
59
|
+
// rename migration's own filename (`government_001_rename_from_governance.sql`)
|
|
60
|
+
// says the banned word while matching neither half of the old regex. That is
|
|
61
|
+
// not drift; it is the one filename that has to name what it renamed.
|
|
62
|
+
//
|
|
63
|
+
// docs/specs/board-room-pass-event-contract.md is here for the same reason a
|
|
64
|
+
// migration filename is: it is the agreed cross-goal name of the economy-side
|
|
65
|
+
// award (awardIdeaGovernancePassCredit) and the credit_log reason family
|
|
66
|
+
// idea.credit.governance_pass:<id>. Those are durable ledger rows and another
|
|
67
|
+
// goal's method signature — a line pointing at the contract is citing a name
|
|
68
|
+
// this surface does not own and cannot rename.
|
|
69
|
+
const VOCAB_HISTORICAL_REF = /docs\/adr\/|docs\/governance\/|docs\/session-logs\/|govern(?:ance|ment)_0\d\d|board-room-pass-event-contract/;
|
|
70
|
+
|
|
71
|
+
// Naming the RULE stays legal — a line that cites ADR 0174 or the task that
|
|
72
|
+
// built the wall is meta-commentary about the ban, and meta-commentary has to
|
|
73
|
+
// be able to say the banned word. Same principle as check 19b's
|
|
74
|
+
// GOAL_VOCAB_CITATION, and deliberately preferred over a per-file VOCAB_ALLOW
|
|
75
|
+
// entry: the exemption belongs to the CITATION, not to whichever file happens
|
|
76
|
+
// to hold it today. (Adding '.md' to VOCAB_EXTS brought the module's own
|
|
77
|
+
// nested CLAUDE.md into scope — the file that TEACHES this rule, which states
|
|
78
|
+
// it in the same breath. Three allowlist entries would have taken the cap of 5
|
|
79
|
+
// to its limit for lines whose only sin is quoting the rule correctly.)
|
|
80
|
+
const VOCAB_CITATION = /ADR 0174|task 1003008/;
|
|
56
81
|
|
|
57
82
|
const VOCAB_SURFACE = [
|
|
58
83
|
'modules/government',
|
|
@@ -67,7 +92,13 @@ const VOCAB_SURFACE = [
|
|
|
67
92
|
'modules/hall-ui/public/board-room.html',
|
|
68
93
|
'modules/hall-ui/public/board-room.css',
|
|
69
94
|
];
|
|
70
|
-
|
|
95
|
+
// '.md' is load-bearing, not tidiness (task 1003120). A nested CLAUDE.md is
|
|
96
|
+
// INSTRUCTION: it loads on demand for every agent working in the module, and
|
|
97
|
+
// the agent that reads the wrong vocabulary there goes on to write it into the
|
|
98
|
+
// code. Leaving markdown unscanned is how modules/government/CLAUDE.md came to
|
|
99
|
+
// assert the INVERSE of ADR 0174 and survive a criterion review (task 1003118).
|
|
100
|
+
// The doc surface is the higher-leverage one to guard, and it was the unguarded one.
|
|
101
|
+
const VOCAB_EXTS = new Set(['.js', '.json', '.html', '.css', '.md']);
|
|
71
102
|
const VOCAB_BANNED = [
|
|
72
103
|
{ re: /\brole/i, label: 'role', hint: '"role" is reserved for the crafts (Artist / Ideator / Builder). On this surface the noun is RANK.' },
|
|
73
104
|
{ re: /governance/i, label: 'governance', hint: 'the pre-ADR-0174 name of this surface. It is the GOVERNMENT now.' },
|
|
@@ -202,6 +233,9 @@ function checkVocabularyWall() {
|
|
|
202
233
|
// too, or the code could not point at the migration that created its own
|
|
203
234
|
// tables. Same principle, applied to the line rather than the file.
|
|
204
235
|
if (VOCAB_HISTORICAL_REF.test(scanLine)) continue;
|
|
236
|
+
// A line that CITES the rule is meta-commentary about the ban, not an
|
|
237
|
+
// instance of it. Costs no allowlist entry and travels with the sentence.
|
|
238
|
+
if (VOCAB_CITATION.test(scanLine)) continue;
|
|
205
239
|
const excused = VOCAB_ALLOW.some((a) => a.file === rel && scanLine.toLowerCase().includes(a.match));
|
|
206
240
|
if (excused) continue;
|
|
207
241
|
violations.push(`${rel}:${i + 1}: "${b.label}" — ${b.hint} → ${line.trim().slice(0, 90)}`);
|
|
@@ -214,8 +248,52 @@ function checkVocabularyWall() {
|
|
|
214
248
|
hardFail: violations.length > 0,
|
|
215
249
|
violations,
|
|
216
250
|
warnings: [],
|
|
217
|
-
note: `${scanned} government-surface file(s) scanned for "role"/"governance"; `
|
|
218
|
-
+ `${VOCAB_ALLOW.length} documented allowlist entr(y|ies)
|
|
251
|
+
note: `${scanned} government-surface file(s) scanned for "role"/"governance" (${[...VOCAB_EXTS].sort().join(' ')}); `
|
|
252
|
+
+ `${VOCAB_ALLOW.length} documented allowlist entr(y|ies), plus the citation rule. `
|
|
253
|
+
+ 'Historical migrations + docs/adr are excluded by construction, not suppressed.',
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ── Check 19c — a rename sweep that ate its own arrow (task 1003120) ─────────
|
|
258
|
+
//
|
|
259
|
+
// THE HOLE CHECK 19 CANNOT SEE. Checks 19 and 19b grep for a BANNED WORD, so
|
|
260
|
+
// they are structurally blind to the damage that actually happened in task
|
|
261
|
+
// 1003118: a find-and-replace ran over a doc that was DESCRIBING the rename, and
|
|
262
|
+
// swept BOTH sides of every rename pair. `governance` → `government` became
|
|
263
|
+
// `government` → `government`. Not one banned word survives that edit, so the
|
|
264
|
+
// wall passed — while the nested CLAUDE.md now taught the next agent that the
|
|
265
|
+
// old name and the new name were the same word, and asserted the inverse of
|
|
266
|
+
// ADR 0174 through a criterion review.
|
|
267
|
+
//
|
|
268
|
+
// An arrow between two identical names states nothing. It is only ever the
|
|
269
|
+
// fossil of a sweep, so flagging the SHAPE catches the defect on the day it
|
|
270
|
+
// lands, with no vocabulary to enumerate. Measured on the whole tree: the one
|
|
271
|
+
// hit outside this surface is ADR 0194's `open` → `open`, a real mapping
|
|
272
|
+
// sentence — which is why the check is scoped to the government surface (the
|
|
273
|
+
// files the rename actually swept) rather than run repo-wide.
|
|
274
|
+
const RENAME_IDENTITY_RE = /`([\w./-]+)` *(?:→|->) *`\1`/;
|
|
275
|
+
|
|
276
|
+
function checkGovernmentRenameIdentity() {
|
|
277
|
+
const violations = [];
|
|
278
|
+
let scanned = 0;
|
|
279
|
+
for (const { abs, rel } of vocabFilesToScan()) {
|
|
280
|
+
scanned += 1;
|
|
281
|
+
fs.readFileSync(abs, 'utf8').split('\n').forEach((line, i) => {
|
|
282
|
+
const m = RENAME_IDENTITY_RE.exec(line);
|
|
283
|
+
if (!m) return;
|
|
284
|
+
violations.push(`${rel}:${i + 1}: \`${m[1]}\` → \`${m[1]}\` — a rename arrow between two IDENTICAL names states nothing. `
|
|
285
|
+
+ 'A sweep ate one side of the pair (task 1003118); restore the name it renamed FROM.'
|
|
286
|
+
+ ` → ${line.trim().slice(0, 90)}`);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
name: 'a rename arrow names two different things (task 1003120)',
|
|
291
|
+
ok: violations.length === 0,
|
|
292
|
+
hardFail: violations.length > 0,
|
|
293
|
+
violations,
|
|
294
|
+
warnings: [],
|
|
295
|
+
note: `${scanned} government-surface file(s) scanned for \`X\` → \`X\` pairs; no allowlist — `
|
|
296
|
+
+ 'an arrow between identical names has no legitimate form to excuse.',
|
|
219
297
|
};
|
|
220
298
|
}
|
|
221
299
|
|
|
@@ -225,12 +303,15 @@ module.exports = {
|
|
|
225
303
|
GOAL_VOCAB_LINE_SCOPED,
|
|
226
304
|
GOAL_VOCAB_SURFACE,
|
|
227
305
|
LIFECYCLE_DB_FILES,
|
|
306
|
+
RENAME_IDENTITY_RE,
|
|
228
307
|
VOCAB_ALLOW,
|
|
229
308
|
VOCAB_BANNED,
|
|
309
|
+
VOCAB_CITATION,
|
|
230
310
|
VOCAB_EXTS,
|
|
231
311
|
VOCAB_HISTORICAL_REF,
|
|
232
312
|
VOCAB_SURFACE,
|
|
233
313
|
checkGoalMembershipVocabulary,
|
|
314
|
+
checkGovernmentRenameIdentity,
|
|
234
315
|
checkVocabularyWall,
|
|
235
316
|
vocabFilesToScan,
|
|
236
317
|
};
|
package/scripts/gds/fitness.js
CHANGED
|
@@ -36,7 +36,7 @@ const { resolveCoreRoot, resolveInstanceRoot } = require('../../src/instance-con
|
|
|
36
36
|
const { CORE_ROOT, INSTANCE_ROOT, MODULE_ROOTS, REPO_ROOT, rel, walkJs, walkJsAllRoots } = require('./fitness-lib.js');
|
|
37
37
|
const { IDENTITY_SCAN_ALLOWED, IDENTITY_SCAN_EXCLUDE, IDENTITY_SCAN_EXTRA_FILES, IDENTITY_SCAN_EXTS, IDENTITY_SCAN_ROOTS, IDENTITY_SCAN_SKIP_DIRS, PROMPT_IDENTITY_LITERALS, checkDesignContract, checkKernelPromptsNoHardcodedIdentity, checkNeutralConfigsNoHostIdentity, collectIdentityFiles, scanPromptIdentity, stripCommentsForIdentity } = require('./fitness-checks-identity.js');
|
|
38
38
|
const { WRITE_VALIDATION_BASELINE, WRITE_VALIDATION_EXEMPT, checkWriteRoutesValidated, scanUnvalidatedWriteRoutes } = require('./fitness-checks-write-validation.js');
|
|
39
|
-
const { GOAL_VOCAB_CITATION, GOAL_VOCAB_LINE_SCOPED, GOAL_VOCAB_SURFACE, LIFECYCLE_DB_FILES, VOCAB_ALLOW, VOCAB_BANNED, VOCAB_EXTS, VOCAB_HISTORICAL_REF, VOCAB_SURFACE, checkGoalMembershipVocabulary, checkVocabularyWall, vocabFilesToScan } = require('./fitness-checks-vocabulary.js');
|
|
39
|
+
const { GOAL_VOCAB_CITATION, GOAL_VOCAB_LINE_SCOPED, GOAL_VOCAB_SURFACE, LIFECYCLE_DB_FILES, RENAME_IDENTITY_RE, VOCAB_ALLOW, VOCAB_BANNED, VOCAB_CITATION, VOCAB_EXTS, VOCAB_HISTORICAL_REF, VOCAB_SURFACE, checkGoalMembershipVocabulary, checkGovernmentRenameIdentity, checkVocabularyWall, vocabFilesToScan } = require('./fitness-checks-vocabulary.js');
|
|
40
40
|
// ============================================================================
|
|
41
41
|
// Check 1 — core ↔ host/game import boundary (ADR 0062 §1 cut-line)
|
|
42
42
|
// ============================================================================
|
|
@@ -1374,6 +1374,7 @@ const CHECKS = [
|
|
|
1374
1374
|
checkErrorEnvelope, // Check 17 — BV1 R03 / task 1990: no route hand-rolls a flat error (ADR 0116)
|
|
1375
1375
|
checkVocabularyWall, // Check 19 — ADR 0174 / goal 1000068 R07: "role" is a craft, never a rank
|
|
1376
1376
|
checkGoalMembershipVocabulary, // Check 19b — ADR 0177 / task 1003013: the FOURTH "role" (goal membership)
|
|
1377
|
+
checkGovernmentRenameIdentity, // Check 19c — task 1003120: `X` → `X`, the rename arrow a sweep ate both sides of (the shape checks 19/19b are blind to)
|
|
1377
1378
|
checkWriteRoutesValidated, // Check 18 — BV1 R12 / task 1999: every body-reading write route validates (ADR 0118)
|
|
1378
1379
|
require('./fitness-ratchets.js').checkQualityRatchets, // Check 20 — task 1003129: quality budgets only tighten (mechanics + knip CI step live in fitness-ratchets.js)
|
|
1379
1380
|
require('./doc-cli-guard.js').checkDocNamesRealCli, // Check 22 — task 1003165 / G01: a doc that names a CLI invocation must match the real CLI
|
|
@@ -1434,8 +1435,8 @@ function main() {
|
|
|
1434
1435
|
if (require.main === module) main();
|
|
1435
1436
|
|
|
1436
1437
|
module.exports = {
|
|
1437
|
-
checkVocabularyWall, VOCAB_ALLOW,
|
|
1438
|
-
checkGoalMembershipVocabulary, GOAL_VOCAB_SURFACE,
|
|
1438
|
+
checkVocabularyWall, VOCAB_ALLOW, VOCAB_CITATION, VOCAB_EXTS, vocabFilesToScan,
|
|
1439
|
+
checkGoalMembershipVocabulary, GOAL_VOCAB_SURFACE, checkGovernmentRenameIdentity, RENAME_IDENTITY_RE, // 19c — task 1003120
|
|
1439
1440
|
runAll,
|
|
1440
1441
|
resolvesToHostGame,
|
|
1441
1442
|
checkCoreHostBoundary,
|
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.
|
|
74
|
+
const CORE_VERSION = '1.19.653'; // 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');
|
|
@@ -46,18 +46,113 @@ test('a planted "governance" on the government surface REDS the build', () => {
|
|
|
46
46
|
// Plant a line into a real government-surface file, run the check, restore.
|
|
47
47
|
// Uses the real file rather than a fixture so the test exercises the same path
|
|
48
48
|
// resolution the CI gate does.
|
|
49
|
-
function withPlantedLine(line) {
|
|
49
|
+
function withPlantedLine(line, relParts = ['modules', 'government', 'resolver.js'], check = 'checkVocabularyWall') {
|
|
50
50
|
const fs = require('node:fs');
|
|
51
|
-
const target = path.join(ROOT,
|
|
51
|
+
const target = path.join(ROOT, ...relParts);
|
|
52
52
|
const original = fs.readFileSync(target, 'utf8');
|
|
53
53
|
try {
|
|
54
54
|
fs.writeFileSync(target, `${original}\n${line}\n`);
|
|
55
|
-
return fitness
|
|
55
|
+
return fitness[check]().violations;
|
|
56
56
|
} finally {
|
|
57
57
|
fs.writeFileSync(target, original);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
const MD = ['modules', 'government', 'CLAUDE.md'];
|
|
62
|
+
|
|
63
|
+
// ---------- markdown is inside the wall (task 1003120) ----------
|
|
64
|
+
//
|
|
65
|
+
// The gap this closes: a nested CLAUDE.md is INSTRUCTION — it loads on demand
|
|
66
|
+
// for every agent working in the module, and the agent that reads the wrong
|
|
67
|
+
// vocabulary there writes it into the code next. It sat outside the very rule
|
|
68
|
+
// it describes, which is how it came to teach the INVERSE of ADR 0174 and
|
|
69
|
+
// survive a criterion review (task 1003118).
|
|
70
|
+
|
|
71
|
+
test('the wall scans markdown — the doc that TEACHES the rule is inside it', () => {
|
|
72
|
+
assert.ok(fitness.VOCAB_EXTS.has('.md'),
|
|
73
|
+
'markdown must be scanned: the highest-leverage vocabulary surface is the nested '
|
|
74
|
+
+ 'CLAUDE.md an agent reads before writing code, not the code itself');
|
|
75
|
+
const scanned = fitness.vocabFilesToScan().map((f) => f.rel);
|
|
76
|
+
assert.ok(scanned.includes('modules/government/CLAUDE.md'),
|
|
77
|
+
`the module's own nested doc must be in scope; scanned: ${scanned.join(', ')}`);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('a planted "role" in a government MARKDOWN file REDS the build', () => {
|
|
81
|
+
const planted = withPlantedLine('A custom role may hold this permission.', MD);
|
|
82
|
+
assert.ok(planted.some((v) => /CLAUDE\.md.*"role"/.test(v)),
|
|
83
|
+
'a doc that teaches the banned vocabulary must be caught — otherwise adding '
|
|
84
|
+
+ '.md to VOCAB_EXTS bought nothing');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// ---------- the citation rule is an exemption, not a hole ----------
|
|
88
|
+
//
|
|
89
|
+
// Preferred over three more VOCAB_ALLOW entries (which would have taken the cap
|
|
90
|
+
// of 5 to its limit) because the exemption belongs to the CITATION, not to
|
|
91
|
+
// whichever file happens to hold it. So it has to be narrow, and both halves of
|
|
92
|
+
// that are load-bearing: it lets the rule be NAMED, and nothing else.
|
|
93
|
+
|
|
94
|
+
test('a line that CITES the rule may say the banned word', () => {
|
|
95
|
+
const planted = withPlantedLine('On this surface "role" is banned — see ADR 0174.', MD);
|
|
96
|
+
assert.ok(!planted.some((v) => /CLAUDE\.md/.test(v)),
|
|
97
|
+
'meta-commentary about the ban has to be able to name it, or the doc could '
|
|
98
|
+
+ 'not state the rule it enforces');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('the citation rule does NOT excuse the rest of the line', () => {
|
|
102
|
+
// Same file, same banned word, no citation: still caught. Without this the
|
|
103
|
+
// exemption could be silently widened to "this file is fine" and stay green.
|
|
104
|
+
const planted = withPlantedLine('A custom role may hold this permission.', MD);
|
|
105
|
+
assert.ok(planted.some((v) => /CLAUDE\.md/.test(v)),
|
|
106
|
+
'a line with no citation must still red — an exemption that covers the file '
|
|
107
|
+
+ 'rather than the sentence is an allowlist wearing a regex');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('the allowlist did not grow to absorb the markdown surface', () => {
|
|
111
|
+
// The whole point of the citation rule: bringing .md into scope surfaced three
|
|
112
|
+
// legitimate lines, and NONE of them cost an allowlist slot.
|
|
113
|
+
assert.ok(fitness.VOCAB_ALLOW.length <= 4,
|
|
114
|
+
`the allowlist grew to ${fitness.VOCAB_ALLOW.length} when .md came into scope. `
|
|
115
|
+
+ 'The citation rule exists so it would not have to.');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ---------- check 19c: the rename arrow a sweep ate both sides of ----------
|
|
119
|
+
//
|
|
120
|
+
// The defect checks 19 and 19b are structurally blind to. Task 1003118 ran a
|
|
121
|
+
// find-and-replace over a doc DESCRIBING the rename and swept both sides of
|
|
122
|
+
// every pair: `governance` → `government` became `government` → `government`.
|
|
123
|
+
// No banned word survives that edit, so the wall passed while the doc taught
|
|
124
|
+
// the next agent that the old and new names were the same word.
|
|
125
|
+
|
|
126
|
+
test('the government surface carries no identity rename arrows today', () => {
|
|
127
|
+
const r = fitness.checkGovernmentRenameIdentity();
|
|
128
|
+
assert.equal(r.ok, true,
|
|
129
|
+
`an \`X\` → \`X\` arrow is on the surface:\n${r.violations.join('\n')}`);
|
|
130
|
+
assert.ok(r.note.includes('file(s) scanned'), 'the check must report what it scanned');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('a planted `X` → `X` pair REDS the build', () => {
|
|
134
|
+
const planted = withPlantedLine('The sweep renamed `ranks.js` → `ranks.js` here.', MD, 'checkGovernmentRenameIdentity');
|
|
135
|
+
assert.ok(planted.some((v) => /ranks\.js/.test(v)),
|
|
136
|
+
'an arrow between two identical names states nothing and is only ever the '
|
|
137
|
+
+ 'fossil of a sweep — it must be caught');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('a REAL rename arrow is left alone', () => {
|
|
141
|
+
// The check must not punish the sentence it exists to protect: a doc
|
|
142
|
+
// describing the rename correctly names two different things.
|
|
143
|
+
const planted = withPlantedLine('The sweep renamed `governance_001` → `government_001` here.', MD, 'checkGovernmentRenameIdentity');
|
|
144
|
+
assert.equal(planted.length, 0,
|
|
145
|
+
`a legitimate A → B rename must pass, or the rule teaches docs not to describe `
|
|
146
|
+
+ `renames at all:\n${planted.join('\n')}`);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('the rename-identity check has no allowlist to erode', () => {
|
|
150
|
+
const r = fitness.checkGovernmentRenameIdentity();
|
|
151
|
+
assert.ok(/no allowlist/.test(r.note),
|
|
152
|
+
'an arrow between identical names has no legitimate form, so there is nothing '
|
|
153
|
+
+ 'to excuse — and no slot for a future exemption to grow into');
|
|
154
|
+
});
|
|
155
|
+
|
|
61
156
|
// ---------- the allowlist is small, reasoned, and real ----------
|
|
62
157
|
|
|
63
158
|
test('every allowlist entry carries a written reason', () => {
|