@bongos/core 1.20.6 → 1.20.8

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.
@@ -0,0 +1,141 @@
1
+ // tests/craft_mode.mjs — the claimed craft's pack pointer is INJECTED, not
2
+ // printed (task 1002992).
3
+ //
4
+ // Four altitudes: the server helper over the real registry, the /me wiring, the
5
+ // Conductor's worktree-scoped relay, and claim.js — which now prints one role line
6
+ // but must still land a ui claim's module-owned skills (the regression this task
7
+ // was most likely to ship). The hook itself is run end to end, against the
8
+ // helper's real output, in tests/conductor_main_e2e.mjs.
9
+ //
10
+ // Run: node tests/craft_mode.mjs
11
+
12
+ import { strict as assert } from 'node:assert';
13
+ import { createRequire } from 'node:module';
14
+ import fs from 'node:fs';
15
+ import os from 'node:os';
16
+ import path from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+ import { makeRunner } from './helpers.mjs';
19
+
20
+ const require = createRequire(import.meta.url);
21
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
22
+ const craft = require('../src/bongos/craft-mode.js');
23
+ const conductor = require('../.claude/hooks/conductor.js');
24
+ const { test, summary } = makeRunner();
25
+
26
+ const REGISTRY = path.join(ROOT, 'scripts', 'gds', 'discipline-modes.json');
27
+ const MODES = craft.readModes();
28
+
29
+ // ---- the helper, over the ONE registry ---------------------------------------
30
+ await test('the server reads the same registry file claim.js and fitness read', () => {
31
+ assert.deepEqual(MODES, JSON.parse(fs.readFileSync(REGISTRY, 'utf8')).modes);
32
+ });
33
+
34
+ await test('every registry craft yields a contract naming its pack or skill, with its directive', () => {
35
+ const names = Object.keys(MODES);
36
+ assert.ok(names.length >= 3, 'the registry was read at all');
37
+ for (const d of names) {
38
+ const m = craft.describeCraftMode(d, MODES);
39
+ assert.ok(m && m.inject, `${d} injects`);
40
+ assert.equal(m.discipline, d);
41
+ const pointer = MODES[d].pack || `/${MODES[d].skill}`;
42
+ assert.ok(m.contract.includes(pointer), `${d} names ${pointer}`);
43
+ assert.ok(m.contract.includes(MODES[d].directive.slice(0, 40)), `${d} carries its directive`);
44
+ }
45
+ assert.match(craft.describeCraftMode('engineer', MODES).contract, /read docs\/packs\/engineer\.md/);
46
+ assert.match(craft.describeCraftMode('ui', MODES).contract, /— \/design/, 'a skill is a command to invoke, not a file to read');
47
+ });
48
+
49
+ await test('fail-open: no craft, an unknown craft, or an unreadable registry yields nothing', () => {
50
+ assert.equal(craft.describeCraftMode(null, MODES), null);
51
+ assert.equal(craft.describeCraftMode('governor', MODES), null);
52
+ assert.equal(craft.describeCraftMode('engineer', null), null);
53
+ assert.deepEqual(craft.readModes(path.join(os.tmpdir(), 'no-such-registry.json')), {});
54
+ const bad = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'craft-')), 'modes.json');
55
+ fs.writeFileSync(bad, '{ not json');
56
+ assert.deepEqual(craft.readModes(bad), {});
57
+ });
58
+
59
+ await test('the default registry is read once per process; an explicit file is read fresh', () => {
60
+ assert.equal(craft.readModes(), craft.readModes(), 'GET /me does not re-read and re-parse per request');
61
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'craft-'));
62
+ const f = path.join(dir, 'modes.json');
63
+ fs.writeFileSync(f, JSON.stringify({ modes: { a: { directive: 'x' } } }));
64
+ assert.deepEqual(Object.keys(craft.readModes(f)), ['a']);
65
+ fs.writeFileSync(f, JSON.stringify({ modes: { b: { directive: 'y' } } }));
66
+ assert.deepEqual(Object.keys(craft.readModes(f)), ['b'], 'claim.js always sees the file as it is now');
67
+ });
68
+
69
+ // ---- /me carries it on each claim ---------------------------------------------
70
+ await test('GET /me puts each claim\'s craft_mode on the claim, from the helper', () => {
71
+ const me = fs.readFileSync(path.join(ROOT, 'src/bongos/routes/me.js'), 'utf8');
72
+ assert.match(me, /require\('\.\.\/craft-mode'\)/);
73
+ assert.match(me, /craft_mode: describeCraftMode\(c\.discipline, craftModes\)/);
74
+ assert.doesNotMatch(me, /"modes"\s*:/, 'no second copy of the registry in the route');
75
+ });
76
+
77
+ // ---- the Conductor relays THIS worktree's craft only ---------------------------
78
+ const claim = (id, discipline) => ({ task_id: String(id), title: 't', touches: [], worktree_name: `task-${id}`, discipline, craft_mode: craft.describeCraftMode(discipline, MODES) });
79
+ const craftIds = (out) => out.filter((a) => String(a.id).startsWith('craft:')).map((a) => a.id);
80
+
81
+ await test('the claim bound to this worktree gets its craft; the other tree\'s does not', () => {
82
+ const claims = [claim(1, 'engineer'), claim(2, 'ideator')];
83
+ assert.deepEqual(craftIds(conductor.evaluateTriggers({ activeClaims: claims, worktree: 'task-1' })), ['craft:engineer']);
84
+ assert.deepEqual(craftIds(conductor.evaluateTriggers({ activeClaims: claims, worktree: 'task-2' })), ['craft:ideator']);
85
+ });
86
+
87
+ await test('no bound claim, no session, or no registry entry: nothing is injected', () => {
88
+ const claims = [claim(1, 'engineer')];
89
+ assert.deepEqual(craftIds(conductor.evaluateTriggers({ activeClaims: claims, worktree: 'task-9' })), [], 'an unrelated tree');
90
+ assert.deepEqual(craftIds(conductor.evaluateTriggers({ activeClaims: claims, worktree: null })), [], 'not in a worktree');
91
+ assert.deepEqual(craftIds(conductor.evaluateTriggers({ activeClaims: null, worktree: 'task-1' })), [], 'no session / no network');
92
+ assert.deepEqual(craftIds(conductor.evaluateTriggers({ activeClaims: [{ ...claim(1, 'governor') }], worktree: 'task-1' })), [], 'a craft with no entry');
93
+ assert.deepEqual(craftIds(conductor.evaluateTriggers({ activeClaims: [{ ...claim(1, 'engineer'), craft_mode: { inject: true, contract: '' } }], worktree: 'task-1' })), [], 'an empty contract');
94
+ });
95
+
96
+ // ---- claim.js: one role line, and the ui skills still land ---------------------
97
+ const claimCli = require('../scripts/gds/claim.js');
98
+ const materialize = require('../scripts/gds/claude-materialize.js');
99
+
100
+ function capture(fn) {
101
+ const lines = [];
102
+ const orig = console.log;
103
+ console.log = (...a) => { lines.push(a.join(' ')); };
104
+ try { fn(); } finally { console.log = orig; }
105
+ return lines.filter((l) => l !== '');
106
+ }
107
+
108
+ await test('claim.js prints exactly one role line and no directive block', () => {
109
+ const lines = capture(() => claimCli.printRoleLine({ discipline: 'engineer' }));
110
+ assert.deepEqual(lines, ['▶ Engineering session — read docs/packs/engineer.md']);
111
+ const src = fs.readFileSync(path.join(ROOT, 'scripts/gds/claim.js'), 'utf8');
112
+ assert.doesNotMatch(src, /mode\.directive\}/, 'the directive is no longer printed');
113
+ assert.doesNotMatch(src, /Your pack:/);
114
+ });
115
+
116
+ await test('claiming a ui task STILL materialises its module-owned skills', () => {
117
+ const saved = { into: materialize.materializeModuleSkillsIntoCore, root: materialize.isCoreCheckoutRoot };
118
+ const calls = [];
119
+ materialize.isCoreCheckoutRoot = () => true;
120
+ materialize.materializeModuleSkillsIntoCore = (opts) => { calls.push(opts); return { materialized: ['impeccable', 'minimalist-ui'] }; };
121
+ try {
122
+ const lines = capture(() => claimCli.printRoleLine({ discipline: 'ui' }));
123
+ assert.equal(calls.length, 1, 'the ui claim landed its skills');
124
+ assert.equal(lines[0], '▶ UI design session — /design');
125
+ assert.match(lines[1], /2 module-owned skill\(s\)/);
126
+ calls.length = 0;
127
+ capture(() => claimCli.printRoleLine({ discipline: 'engineer' }));
128
+ assert.equal(calls.length, 0, 'a craft that did not opt in lands nothing');
129
+ } finally {
130
+ materialize.materializeModuleSkillsIntoCore = saved.into;
131
+ materialize.isCoreCheckoutRoot = saved.root;
132
+ }
133
+ });
134
+
135
+ await test('claim.js fails open: no craft or an unknown craft prints nothing and never throws', () => {
136
+ assert.deepEqual(capture(() => claimCli.printRoleLine({})), []);
137
+ assert.deepEqual(capture(() => claimCli.printRoleLine({ discipline: 'governor' })), []);
138
+ assert.deepEqual(capture(() => claimCli.printRoleLine(null)), []);
139
+ });
140
+
141
+ summary();
@@ -0,0 +1,137 @@
1
+ // tests/find_untested.mjs
2
+ //
3
+ // Unit test for scripts/gds/find-untested.js (task 1002458) — the finder the newcomer
4
+ // "add a unit test" chore runs instead of a hardcoded target list. The cases pin the two
5
+ // failures that made the naive scans unusable, measured before it was built:
6
+ // - BASENAME matching falsely marks a common-word module (settings.js) as tested because
7
+ // some unrelated test mentions a different settings.js;
8
+ // - LITERAL-require-only scanning misses tests that reach a module through
9
+ // path.join(ROOT, …), a `const GOV = path.join(…)` prefix, or a spawned script — which is
10
+ // how it once flagged the well-tested memory-map.js as untested.
11
+ // Plus the purity heuristic, the ranking, and one pass over the live tree. Run:
12
+ // node tests/find_untested.mjs
13
+
14
+ import { strict as assert } from 'node:assert';
15
+ import { createRequire } from 'node:module';
16
+
17
+ const require = createRequire(import.meta.url);
18
+ const fu = require('../scripts/gds/find-untested.js');
19
+
20
+ let passed = 0;
21
+ let failed = 0;
22
+ function test(name, fn) {
23
+ try { fn(); passed++; console.log(` ok ${name}`); } catch (e) { failed++; console.log(` FAIL ${name}\n ${e.message}`); }
24
+ }
25
+
26
+ const MODS = new Set([
27
+ 'modules/builder-settings/settings.js',
28
+ 'modules/hall-ui/settings.js',
29
+ 'modules/government/db.js',
30
+ 'modules/government/board.js',
31
+ 'scripts/gds/memory-map.js',
32
+ 'scripts/gds/grade-smoke.js',
33
+ 'modules/lifecycle/index.js',
34
+ ]);
35
+ const hits = (src, file) => [...fu.extractPathRefs(src, file)].map((r) => fu.resolveRef(r, MODS)).filter(Boolean).sort();
36
+
37
+ test('a basename alone never resolves to a module (the common-word false drop)', () => {
38
+ assert.deepEqual(hits("const s = require('./settings.js'); // settings.js", 'tests/x.mjs'), []);
39
+ assert.deepEqual(hits("import x from 'settings.js'", 'tests/x.mjs'), []);
40
+ });
41
+
42
+ test('a full or relative path resolves to exactly that module, not its namesake', () => {
43
+ assert.deepEqual(hits("require('../modules/hall-ui/settings.js')", 'tests/x.mjs'), ['modules/hall-ui/settings.js']);
44
+ assert.deepEqual(hits("require('modules/builder-settings/settings')", 'tests/x.mjs'), ['modules/builder-settings/settings.js']);
45
+ });
46
+
47
+ test('path.join(ROOT, …) segments, and a const prefix, are evaluated (the memory-map miss)', () => {
48
+ const src = [
49
+ "const ROOT = path.resolve(__dirname, '..');",
50
+ "const GOV = path.join(ROOT, 'modules', 'government');",
51
+ "const db = require(path.join(GOV, 'db.js'));",
52
+ "const mm = require(path.join(ROOT, 'scripts', 'gds', 'memory-map.js'));",
53
+ ].join('\n');
54
+ assert.deepEqual(hits(src, 'tests/x.mjs'), ['modules/government/db.js', 'scripts/gds/memory-map.js']);
55
+ });
56
+
57
+ test('an unknown leading identifier stands for the repo root; one in the middle does not', () => {
58
+ assert.deepEqual(hits("path.join(REPO, 'modules/government/board.js')", 'tests/x.mjs'), ['modules/government/board.js']);
59
+ assert.deepEqual(hits("path.join('modules', someVar, 'board.js')", 'tests/x.mjs'), []);
60
+ });
61
+
62
+ test('a spawned script, new URL(…, import.meta.url) and a directory index all count', () => {
63
+ assert.deepEqual(hits("spawnSync(process.execPath, ['scripts/gds/grade-smoke.js', '--dry'])", 'tests/x.mjs'), ['scripts/gds/grade-smoke.js']);
64
+ assert.deepEqual(hits("await import(new URL('../scripts/gds/memory-map.js', import.meta.url))", 'tests/x.mjs'), ['scripts/gds/memory-map.js']);
65
+ assert.deepEqual(hits("require('../modules/lifecycle')", 'tests/x.mjs'), ['modules/lifecycle/index.js']);
66
+ });
67
+
68
+ test('template literals with interpolation and URLs are ignored, not mis-resolved', () => {
69
+ assert.deepEqual(hits('require(`../modules/${k}/settings.js`)', 'tests/x.mjs'), []);
70
+ assert.deepEqual(hits("fetch('https://example.com/scripts/gds/memory-map.js')", 'tests/x.mjs'), []);
71
+ });
72
+
73
+ test('extractRequires separates packages from local edges and ignores comment mentions', () => {
74
+ const r = fu.extractRequires("const pg = require('pg');\nconst fs = require('node:fs');\nconst b = require('./board');\n// see ./db.js", 'modules/government/x.js');
75
+ assert.deepEqual([...r.packages].sort(), ['fs', 'pg']);
76
+ assert.deepEqual([...r.local], ['modules/government/board']);
77
+ });
78
+
79
+ test('ownImpurity: pg, a db module, child_process and fetch are hard; fs is soft', () => {
80
+ const imp = (src, file = 'modules/a/x.js') => fu.ownImpurity(src, fu.extractRequires(src, file));
81
+ assert.deepEqual([...imp("require('pg')").hard], ['postgres']);
82
+ assert.deepEqual([...imp("require('./db-claims.js')").hard], ['postgres']);
83
+ assert.deepEqual([...imp("require('node:child_process')").hard], ['subprocess']);
84
+ assert.deepEqual([...imp('const r = await fetch(url)').hard], ['network']);
85
+ const f = imp("require('fs')");
86
+ assert.equal(f.hard.size, 0);
87
+ assert.equal(f.fs, true);
88
+ // A name that merely contains "db" is not a database module.
89
+ assert.equal(imp("require('./feedback.js')").hard.size, 0);
90
+ });
91
+
92
+ test('isTestPath covers every test root and *.test.js siblings, and nothing else', () => {
93
+ assert.equal(fu.isTestPath('tests/a.mjs'), true);
94
+ assert.equal(fu.isTestPath('modules/lifecycle/tests/a.mjs'), true);
95
+ assert.equal(fu.isTestPath('scripts/gds/newcomer-floor.test.js'), true);
96
+ assert.equal(fu.isTestPath('scripts/gds/latest.js'), false);
97
+ });
98
+
99
+ test('hasExports / countExports read CommonJS and ESM', () => {
100
+ assert.equal(fu.hasExports('function a(){}'), false);
101
+ assert.equal(fu.countExports('module.exports = {\n a,\n b, // note\n c: 1,\n};'), 3);
102
+ assert.equal(fu.countExports('exports.x = 1; module.exports.y = 2;'), 2);
103
+ assert.equal(fu.countExports('export function f(){}\nexport const g = 1;'), 2);
104
+ });
105
+
106
+ test('normRel clamps a climb above the root instead of escaping it', () => {
107
+ assert.equal(fu.normRel('tests/../../../scripts/gds/x.js'), 'scripts/gds/x.js');
108
+ assert.equal(fu.normRel('./a//b/./c'), 'a/b/c');
109
+ assert.equal(fu.normRel('..'), '');
110
+ });
111
+
112
+ test('rankCandidates: unreachable first, then no-fs, then smallest', () => {
113
+ const rows = [
114
+ { path: 'c', reach: 'indirect', fs: false, lines: 10 },
115
+ { path: 'b', reach: 'unreachable', fs: true, lines: 500 },
116
+ { path: 'a', reach: 'indirect', fs: true, lines: 5 },
117
+ { path: 'd', reach: 'indirect', fs: false, lines: 90 },
118
+ ];
119
+ assert.deepEqual(fu.rankCandidates(rows).map((r) => r.path), ['b', 'c', 'd', 'a']);
120
+ });
121
+
122
+ test('the live tree: tested modules the naive scans got wrong are NOT candidates', () => {
123
+ const rows = fu.scanTree();
124
+ const by = new Map(rows.map((r) => [r.path, r]));
125
+ // A scan that finds nothing looks exactly like a clean tree, so assert it found a lot.
126
+ assert.ok(rows.length > 300, `expected hundreds of exported modules, scanned ${rows.length}`);
127
+ for (const p of ['scripts/gds/memory-map.js', 'modules/grading/grader-workers/worker-verdict.js', 'scripts/gds/find-untested.js']) {
128
+ assert.ok(by.has(p), `${p} should be scanned`);
129
+ assert.equal(by.get(p).tested, true, `${p} has a test naming it`);
130
+ }
131
+ const untested = rows.filter((r) => !r.tested);
132
+ assert.ok(untested.length > 0 && untested.length < rows.length / 2,
133
+ `untested should be a minority, got ${untested.length} of ${rows.length}`);
134
+ });
135
+
136
+ console.log(`\nfind-untested (task 1002458) tests: ${passed} passed, ${failed} failed`);
137
+ process.exit(failed > 0 ? 1 : 0);
@@ -90,10 +90,12 @@ await test('a backend task is not nudged — no noise on work that renders nothi
90
90
  assert.equal(visualReviewNudge(task()), null);
91
91
  });
92
92
 
93
- await test('claim.js prints the nudge after the craft directive, and cannot fail a claim with it', () => {
93
+ await test('claim.js prints the nudge after the craft role line, and cannot fail a claim with it', () => {
94
94
  const src = read('scripts/gds/claim.js');
95
95
  const at = src.indexOf('visualReviewNudge(task)');
96
- assert.ok(at > src.indexOf('printDisciplineMode(task);'), 'right after the craft directive');
96
+ const role = src.indexOf('printRoleLine(task);');
97
+ assert.ok(role > -1, 'the role line call exists (task 1002992 renamed it from printDisciplineMode)');
98
+ assert.ok(at > role, 'right after the craft role line');
97
99
  assert.match(src.slice(at - 200, at + 300), /catch \(_\)/, 'wrapped, so a hint can never break a successful claim');
98
100
  });
99
101