@dzhechkov/p-replicator 1.6.0 → 1.10.0

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.
Files changed (48) hide show
  1. package/.dz-manifest.json +92 -32
  2. package/CHANGELOG.md +176 -0
  3. package/README.md +106 -4
  4. package/package.json +4 -4
  5. package/sbom.json +181 -31
  6. package/src/commands/doctor.js +43 -31
  7. package/src/commands/verify.js +27 -2
  8. package/src/utils.js +27 -0
  9. package/templates/.claude/commands/myinsights.md +22 -5
  10. package/templates/.claude/commands/replicate.md +57 -1
  11. package/templates/.claude/hooks/check-docs-complete.cjs +202 -0
  12. package/templates/.claude/hooks/check-growth-trace.cjs +191 -0
  13. package/templates/.claude/hooks/check-ports.cjs +36 -4
  14. package/templates/.claude/hooks/statusline.cjs +16 -5
  15. package/templates/.claude/rules/replicate-pipeline.md +14 -4
  16. package/templates/.claude/rules/skill-interface-protocol.md +9 -0
  17. package/templates/.claude/skills/brutal-honesty-review/scripts/assess-code.sh +40 -7
  18. package/templates/.claude/skills/brutal-honesty-review/scripts/assess-tests.sh +38 -11
  19. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/03-generate-p0.md +6 -4
  20. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/08-skill-composition.md +2 -2
  21. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-hooks-commands.md +40 -4
  22. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/feature-lifecycle.md +2 -2
  23. package/templates/.claude/skills/requirements-validator/SKILL.md +52 -0
  24. package/templates/.claude/skills/reverse-engineering-unicorn/modules/01-intelligence.md +4 -4
  25. package/templates/.claude/skills/reverse-engineering-unicorn/modules/02-product-customers.md +2 -2
  26. package/templates/.claude/skills/reverse-engineering-unicorn/modules/025-cjm-prototype.md +9 -1
  27. package/templates/.claude/skills/reverse-engineering-unicorn/modules/03-market-competition.md +3 -3
  28. package/templates/.claude/skills/reverse-engineering-unicorn/modules/04-business-finance.md +3 -3
  29. package/templates/.claude/skills/reverse-engineering-unicorn/modules/05-growth-engine.md +132 -12
  30. package/templates/.claude/skills/reverse-engineering-unicorn/modules/06-playbook-synthesis.md +1 -1
  31. package/templates/.claude/skills/sparc-prd-mini/SKILL.md +9 -9
  32. package/tests/snapshot/baseline.json +25 -23
  33. package/tests/unit/absence-is-not-emptiness.test.js +255 -0
  34. package/tests/unit/assess-scripts.test.js +150 -0
  35. package/tests/unit/check-docs-complete.test.js +292 -0
  36. package/tests/unit/check-growth-trace.test.js +188 -0
  37. package/tests/unit/check-ports.test.js +99 -0
  38. package/tests/unit/generated-guard-templates.test.js +134 -0
  39. package/tests/unit/growth-axes-and-compliance.test.js +169 -0
  40. package/tests/unit/growth-gate-conditional.test.js +122 -0
  41. package/tests/unit/growth-module-b2b-gate.test.js +20 -2
  42. package/tests/unit/growth-requirements-bridge.test.js +127 -0
  43. package/tests/unit/guard-forms.test.js +302 -0
  44. package/tests/unit/insights-docs-tell-the-truth.test.js +84 -0
  45. package/tests/unit/module-copy-identity.test.js +106 -0
  46. package/tests/unit/shipped-suite-context.test.js +142 -0
  47. package/tests/unit/skill-paths-prebaked.test.js +174 -0
  48. package/tests/unit/sync-templates-guard.test.js +31 -1
@@ -0,0 +1,255 @@
1
+ 'use strict';
2
+
3
+ // A thing that is there but says nothing was reported as fine, in two places a caller reads for the
4
+ // same purpose: "is my toolkit intact?".
5
+ //
6
+ // REPRODUCED 2026-08-27 before the fix: 31 files truncated to zero bytes — every SKILL.md, command,
7
+ // rule and agent — and `verify` printed "[OK] All artifacts verified." with exit 0, `doctor` likewise.
8
+ // DELETING a file was caught (exit 1). Cause: fileExists() is fs.accessSync — it asks whether the
9
+ // path resolves, never what is in it.
10
+ //
11
+ // Why that is worse than it looks, and the evidence is from the same day: a twin test against the
12
+ // harness's own registration event measured that a SKILL.md without YAML frontmatter is NOT
13
+ // registered (35 -> 36 -> 37 as frontmatter was added). A zero-byte SKILL.md certainly has none. So
14
+ // "all artifacts verified" over 31 empty files asserts the integrity of a toolkit that cannot load.
15
+
16
+ const { test, describe } = require('node:test');
17
+ const assert = require('node:assert/strict');
18
+ const { execFileSync, spawnSync } = require('node:child_process');
19
+ const fs = require('node:fs');
20
+ const os = require('node:os');
21
+ const path = require('node:path');
22
+
23
+ const PKG = path.resolve(__dirname, '..', '..');
24
+ const CLI = path.join(PKG, 'bin', 'cli.js');
25
+
26
+ /** A fresh install in a throwaway directory. */
27
+ function project() {
28
+ const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'p-rep-empty-')));
29
+ execFileSync(process.execPath, [CLI, 'init'], { cwd: dir, stdio: 'ignore' });
30
+ return dir;
31
+ }
32
+
33
+ const run = (dir, cmd) => {
34
+ const r = spawnSync(process.execPath, [CLI, cmd], { cwd: dir, encoding: 'utf8' });
35
+ return { code: r.status, out: (r.stdout || '') + (r.stderr || '') };
36
+ };
37
+
38
+ const cleanup = (d) => fs.rmSync(d, { recursive: true, force: true });
39
+
40
+ describe('an artifact that is there but says nothing is not verified', () => {
41
+ test('P1 - truncated artifacts fail both health checks and are named', () => {
42
+ const dir = project();
43
+ try {
44
+ const victims = [
45
+ path.join(dir, '.claude', 'rules', 'docker-ports.md'),
46
+ path.join(dir, '.claude', 'commands', 'replicate.md'),
47
+ path.join(dir, '.claude', 'skills', 'explore', 'SKILL.md'),
48
+ ];
49
+ for (const f of victims) fs.writeFileSync(f, '');
50
+
51
+ const v = run(dir, 'verify');
52
+ assert.notEqual(v.code, 0,
53
+ 'a toolkit whose skills cannot load is not verified: ' + v.out);
54
+ const d = run(dir, 'doctor');
55
+ assert.notEqual(d.code, 0, 'doctor must agree with verify: ' + d.out);
56
+
57
+ // NAMED, not counted. A count sends a reader to inspect files by hand.
58
+ for (const f of victims) {
59
+ const base = path.basename(f) === 'SKILL.md' ? 'explore' : path.basename(f, '.md');
60
+ assert.ok(v.out.includes(base),
61
+ 'verify must name the offending artifact ' + base + ': ' + v.out);
62
+ }
63
+ } finally { cleanup(dir); }
64
+ });
65
+
66
+ test('P2 - whitespace-only counts as empty', () => {
67
+ // A file holding a newline is exactly as dead as one holding nothing, and a naive size check
68
+ // would pass it.
69
+ const dir = project();
70
+ try {
71
+ fs.writeFileSync(path.join(dir, '.claude', 'rules', 'git-workflow.md'), '\n \n\t\n');
72
+ const v = run(dir, 'verify');
73
+ assert.notEqual(v.code, 0, 'whitespace is not content: ' + v.out);
74
+ } finally { cleanup(dir); }
75
+ });
76
+
77
+ test('P3 - missing and empty stay SEPARABLE words', () => {
78
+ // Their cures differ: missing -> run update; empty -> something truncated your file, and update
79
+ // would silently repair it without you ever learning that. Naming both "missing" moves the
80
+ // silence up one level instead of removing it.
81
+ const dirA = project();
82
+ const dirB = project();
83
+ try {
84
+ fs.rmSync(path.join(dirA, '.claude', 'rules', 'docker-ports.md'));
85
+ fs.writeFileSync(path.join(dirB, '.claude', 'rules', 'docker-ports.md'), '');
86
+ const a = run(dirA, 'verify');
87
+ const b = run(dirB, 'verify');
88
+ assert.notEqual(a.code, 0);
89
+ assert.notEqual(b.code, 0);
90
+ assert.match(a.out, /missing/i, 'a deleted artifact is missing: ' + a.out);
91
+ assert.match(b.out, /empty|пуст/i, 'a truncated artifact is EMPTY, not missing: ' + b.out);
92
+ assert.ok(!/\bempty\b/i.test(a.out.split('\n').filter((l) => l.includes('docker-ports')).join('\n')),
93
+ 'the deleted one must not be called empty: ' + a.out);
94
+ } finally { cleanup(dirA); cleanup(dirB); }
95
+ });
96
+
97
+ test('P4 - fileExists keeps its old meaning', () => {
98
+ // 31 call sites. Several ask a genuine presence question about files that may legitimately hold
99
+ // nothing. This asserts the old predicate was NOT repurposed — a future reader must not "fix" it.
100
+ const { fileExists } = require(path.join(PKG, 'src', 'utils.js'));
101
+ const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'p-rep-fe-')));
102
+ try {
103
+ const f = path.join(dir, 'empty.txt');
104
+ fs.writeFileSync(f, '');
105
+ assert.equal(fileExists(f), true,
106
+ 'fileExists answers about PRESENCE and must keep doing so');
107
+ assert.equal(fileExists(path.join(dir, 'nope.txt')), false);
108
+ } finally { cleanup(dir); }
109
+ });
110
+
111
+ test('P5 - artifactState reports three states', () => {
112
+ const { artifactState } = require(path.join(PKG, 'src', 'utils.js'));
113
+ const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'p-rep-as-')));
114
+ try {
115
+ fs.writeFileSync(path.join(dir, 'empty.txt'), '');
116
+ fs.writeFileSync(path.join(dir, 'ws.txt'), ' \n\t ');
117
+ fs.writeFileSync(path.join(dir, 'real.txt'), 'content');
118
+ assert.equal(artifactState(path.join(dir, 'nope.txt')), 'missing');
119
+ assert.equal(artifactState(path.join(dir, 'empty.txt')), 'empty');
120
+ assert.equal(artifactState(path.join(dir, 'ws.txt')), 'empty');
121
+ assert.equal(artifactState(path.join(dir, 'real.txt')), 'present');
122
+ } finally { cleanup(dir); }
123
+ });
124
+
125
+ test('P6 - none of the three insights states changes the exit code', () => {
126
+ // A project that has recorded no insight is a NEW project. A check that refuses a new project is
127
+ // a check people disable, and then the real signal goes with it. The defect was never the
128
+ // emptiness — it was that emptiness and ABSENCE were indistinguishable, so a carrier that never
129
+ // existed looked exactly like one being used and found empty. That is how 27 recorded insights
130
+ // became 0 without anyone noticing.
131
+ const dir = project();
132
+ try {
133
+ const idx = path.join(dir, '.claude', 'insights', 'index.md');
134
+
135
+ const absent = run(dir, 'doctor');
136
+ assert.equal(absent.code, 0, 'a fresh project must not fail: ' + absent.out);
137
+
138
+ fs.mkdirSync(path.dirname(idx), { recursive: true });
139
+ fs.writeFileSync(idx, '# Insights\n');
140
+ const zero = run(dir, 'doctor');
141
+ assert.equal(zero.code, 0, 'zero entries must not fail either: ' + zero.out);
142
+
143
+ fs.writeFileSync(idx, '# Insights\n\n## 2026-08-27 — a rake\n\nBody.\n');
144
+ const some = run(dir, 'doctor');
145
+ assert.equal(some.code, 0);
146
+
147
+ // Three states, three DIFFERENT strings. Two that read alike are the defect.
148
+ // Specific: the component list also contains the word 'insights' (/myinsights, session-insights.cjs).
149
+ const lineOf = (out) => (out.split('\n').find((l) => /insights carrier/i.test(l)) || '').trim();
150
+ const [a, z, s] = [lineOf(absent.out), lineOf(zero.out), lineOf(some.out)];
151
+ assert.ok(a && z && s, 'doctor must say something about insights in all three states: '
152
+ + JSON.stringify([a, z, s]));
153
+ assert.notEqual(a, z, 'ABSENT and ZERO-ENTRY must not read alike: ' + JSON.stringify([a, z]));
154
+ assert.notEqual(z, s, 'ZERO-ENTRY and POPULATED must not read alike: ' + JSON.stringify([z, s]));
155
+ } finally { cleanup(dir); }
156
+ });
157
+
158
+ test('P8 - doctor says EMPTY for every component kind, settings included', () => {
159
+ // Cross-family review found my first pass swapped the CONDITION to artifactState and left the
160
+ // two-way branch, so a whitespace-only artifact was reported as "missing" — the exact collapse
161
+ // AR-4 forbids. Worse: settings.json and the hooks were still on fileExists, so a ZERO-BYTE
162
+ // settings.json — no hooks wired at all — received a checkmark.
163
+ //
164
+ // Matched on the EXACT label the line carries, not a substring: `replicate` also occurs inside
165
+ // `replicate-coordinator`, and a loose matcher read a passing line as the failing one. Third
166
+ // time this class has bitten today.
167
+ const dir = project();
168
+ try {
169
+ const victims = {
170
+ '/replicate': path.join(dir, '.claude', 'commands', 'replicate.md'),
171
+ 'doc-validator': path.join(dir, '.claude', 'agents', 'doc-validator.md'),
172
+ 'docker-ports': path.join(dir, '.claude', 'rules', 'docker-ports.md'),
173
+ 'explore': path.join(dir, '.claude', 'skills', 'explore', 'SKILL.md'),
174
+ 'settings.json': path.join(dir, '.claude', 'settings.json'),
175
+ };
176
+ for (const f of Object.values(victims)) fs.writeFileSync(f, ' \n\t ');
177
+
178
+ const d = run(dir, 'doctor');
179
+ assert.notEqual(d.code, 0, d.out);
180
+ for (const label of Object.keys(victims)) {
181
+ const line = d.out.split('\n')
182
+ .map((l) => l.replace(/\x1b\[[0-9;]*m/g, '').trim())
183
+ .find((l) => l.startsWith('\u2717 ' + label + ' ') || l === '\u2717 ' + label);
184
+ assert.ok(line, label + ' must appear as a FAILING line: ' + d.out);
185
+ assert.match(line, /EMPTY, cannot load/,
186
+ label + ' must be EMPTY, not missing and never a checkmark: ' + JSON.stringify(line));
187
+ }
188
+ } finally { cleanup(dir); }
189
+ });
190
+
191
+ test('P10 - a zero-byte HOOK is reported too, when settings.json still parses', () => {
192
+ // The hook checks live inside the branch that runs only when settings.json is readable, so P8
193
+ // cannot reach them: killing settings.json hides its own children. Asserted separately, which
194
+ // is the honest shape — and it proves the hook path is on artifactState, not fileExists.
195
+ const dir = project();
196
+ try {
197
+ fs.writeFileSync(path.join(dir, '.claude', 'hooks', 'check-ports.cjs'), '');
198
+ const d = run(dir, 'doctor');
199
+ assert.notEqual(d.code, 0, d.out);
200
+ const line = d.out.split('\n')
201
+ .map((l) => l.replace(/\x1b\[[0-9;]*m/g, '').trim())
202
+ .find((l) => l.startsWith('\u2717 check-ports.cjs'));
203
+ assert.ok(line, 'a zero-byte hook must not receive a checkmark: ' + d.out);
204
+ assert.match(line, /EMPTY, cannot load/, line);
205
+ } finally { cleanup(dir); }
206
+ });
207
+
208
+ test('P9 - verify reports the carrier in all three states, failing none', () => {
209
+ // FR-5 asks for three SURFACES. The first pass shipped two and left verify producing identical
210
+ // output for absent, empty and populated — the same indistinguishability, one surface over.
211
+ const dir = project();
212
+ try {
213
+ const idx = path.join(dir, '.claude', 'insights', 'index.md');
214
+ const seen = [];
215
+ const carrierLine = (out) => (out.split('\n').find((l) => /insights carrier/i.test(l)) || '').trim();
216
+
217
+ let r = run(dir, 'verify'); seen.push(carrierLine(r.out));
218
+ assert.equal(r.code, 0, 'a fresh project must not fail verify: ' + r.out);
219
+
220
+ fs.mkdirSync(path.dirname(idx), { recursive: true });
221
+ fs.writeFileSync(idx, '# Insights\n');
222
+ r = run(dir, 'verify'); seen.push(carrierLine(r.out));
223
+ assert.equal(r.code, 0, 'zero entries must not fail verify: ' + r.out);
224
+
225
+ fs.writeFileSync(idx, '# Insights\n\n## 2026-08-27 — a rake\n\nBody.\n');
226
+ r = run(dir, 'verify'); seen.push(carrierLine(r.out));
227
+ assert.equal(r.code, 0);
228
+
229
+ assert.ok(seen.every(Boolean), 'verify must say something in all three states: ' + JSON.stringify(seen));
230
+ assert.equal(new Set(seen).size, 3, 'three states, three DIFFERENT lines: ' + JSON.stringify(seen));
231
+ } finally { cleanup(dir); }
232
+ });
233
+
234
+ test('P7 - the statusline distinguishes no-carrier from zero-entries', () => {
235
+ const dir = project();
236
+ try {
237
+ const hook = path.join(dir, '.claude', 'hooks', 'statusline.cjs');
238
+ const strip = new RegExp('\\x1b\\[[0-9;]*m', 'g');
239
+ const render = () => spawnSync(process.execPath, [hook],
240
+ { cwd: dir, env: { ...process.env, CLAUDE_PROJECT_DIR: dir }, encoding: 'utf8' })
241
+ .stdout.replace(strip, '');
242
+
243
+ const absent = render();
244
+ const idx = path.join(dir, '.claude', 'insights', 'index.md');
245
+ fs.mkdirSync(path.dirname(idx), { recursive: true });
246
+ fs.writeFileSync(idx, '# Insights\n');
247
+ const zero = render();
248
+
249
+ const seg = (out) => (out.split('\n').find((l) => /Insight/i.test(l)) || '');
250
+ assert.notEqual(seg(absent), seg(zero),
251
+ 'a carrier that never existed rendered identically to one found empty — that is the defect: '
252
+ + JSON.stringify([seg(absent), seg(zero)]));
253
+ } finally { cleanup(dir); }
254
+ });
255
+ });
@@ -0,0 +1,150 @@
1
+ 'use strict';
2
+
3
+ // brutal-honesty-review is Phase 4 of /feature — where a course participant meets it. Its two
4
+ // assessment scripts print verdicts and CANNOT FAIL. MEASURED 2026-08-27:
5
+ //
6
+ // assess-code.sh on TODO/FIXME/BUG/HACK + nested for(;;) + empty catch + eval → 2 red, exit 0
7
+ // assess-tests.sh on two tests, one timing-based, no edge cases → 6 red, exit 0
8
+ // assess-tests.sh /nope/nope → exit 1
9
+ //
10
+ // The semantics is INVERTED: "I could not check" is louder than "I found six violations". A
11
+ // participant sees red on screen and any automation reads success.
12
+ //
13
+ // Both scripts DETECT correctly — seven and nine verdict sites, all firing on the right inputs.
14
+ // Nothing about the detection is wrong; nobody counts. These tests RUN the real scripts.
15
+
16
+ const { test, describe } = require('node:test');
17
+ const assert = require('node:assert/strict');
18
+ const { spawnSync } = require('node:child_process');
19
+ const fs = require('node:fs');
20
+ const os = require('node:os');
21
+ const path = require('node:path');
22
+
23
+ const PKG = path.resolve(__dirname, '..', '..');
24
+ const SCRIPTS = path.join(PKG, 'templates', '.claude', 'skills', 'brutal-honesty-review', 'scripts');
25
+ const CODE = path.join(SCRIPTS, 'assess-code.sh');
26
+ const TESTS = path.join(SCRIPTS, 'assess-tests.sh');
27
+
28
+ /** Build a throwaway target and run one script over it. `arg: null` passes no argument at all. */
29
+ function run(script, files, arg) {
30
+ const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'p-rep-assess-')));
31
+ try {
32
+ for (const [name, body] of Object.entries(files || {})) {
33
+ const abs = path.join(dir, name);
34
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
35
+ fs.writeFileSync(abs, body);
36
+ }
37
+ const argv = arg === null ? [script] : [script, path.join(dir, arg === undefined ? 'target' : arg)];
38
+ const r = spawnSync('bash', argv, { cwd: dir, encoding: 'utf8' });
39
+ return { code: r.status, out: (r.stdout || '') + (r.stderr || '') };
40
+ } finally { fs.rmSync(dir, { recursive: true, force: true }); }
41
+ }
42
+
43
+ const AWFUL = 'function f(){ /* TODO FIXME BUG HACK */ for(;;){for(;;){}} try{}catch(e){} eval(x); }\n';
44
+ // "Clean" BY THIS SCRIPT'S OWN STANDARDS, which are stricter than they first look: it wants
45
+ // try/catch present and a tests directory to exist. My first fixture had neither and produced two
46
+ // legitimate findings — the fixture was wrong, not the detection, which AR-1 forbids touching.
47
+ const CLEAN = 'export function add(a, b) {\n'
48
+ + ' try {\n if (typeof a !== "number") throw new Error("bad");\n return a + b;\n'
49
+ + ' } catch (e) {\n console.error(e);\n throw e;\n }\n}\n';
50
+ // `tests/` sits at the fixture ROOT, not under target/: the script's testability check reads
51
+ // `[ -d "tests" ]` against the CURRENT WORKING DIRECTORY rather than against $TARGET. That is a
52
+ // pre-existing quirk — arguably its own defect — and AR-1 forbids changing detection in this
53
+ // change, so the fixture matches the script rather than the other way round. Filed separately.
54
+ const CLEAN_TREE = { 'target/add.js': CLEAN, 'tests/add.test.js': "test('adds', () => {});\n" };
55
+ const BAD_TESTS = "test('t1', () => {});\ntest('t2', () => { setTimeout(() => {}, 1) });\n";
56
+
57
+ describe('an assessment script that prints verdicts can refuse', () => {
58
+ test('P1 - awful code exits 1, and the count is printed', () => {
59
+ const r = run(CODE, { 'target/awful.js': AWFUL });
60
+ assert.equal(r.code, 1, 'red verdicts on screen must reach the exit code: ' + r.out);
61
+ assert.match(r.out, /🔴/, 'and the verdicts themselves must still appear: ' + r.out);
62
+ assert.match(r.out, /\b[1-9]\d*\b.*(?:finding|issue|проблем|нарушен)/i,
63
+ 'the count must be stated, so a human sees the same answer as the exit code: ' + r.out);
64
+ });
65
+
66
+ test('P2 - bad tests exit 1', () => {
67
+ const r = run(TESTS, { 'target/a.test.js': BAD_TESTS });
68
+ assert.equal(r.code, 1, 'six red verdicts must not read as success: ' + r.out);
69
+ assert.match(r.out, /🔴/, r.out);
70
+ });
71
+
72
+ test('P3 - could-not-check is 2, and no argument is 2', () => {
73
+ // THE inversion. Today a nonexistent path exits 1 while six violations exit 0, so a gate
74
+ // reading 1 cannot tell blindness from findings. A caller who passed nothing and one pointing
75
+ // at a missing directory made the same class of mistake: the script could not look.
76
+ for (const script of [CODE, TESTS]) {
77
+ const missing = run(script, {}, 'nope');
78
+ assert.equal(missing.code, 2,
79
+ path.basename(script) + ': a target that does not exist is could-not-check: ' + missing.out);
80
+ const noArg = run(script, {}, null);
81
+ assert.equal(noArg.code, 2,
82
+ path.basename(script) + ': no argument is could-not-check too: ' + noArg.out);
83
+ }
84
+ });
85
+
86
+ test('P4 - clean input exits 0', () => {
87
+ // A script that only fails is as useless as one that only passes, and without this the suite
88
+ // could not tell them apart.
89
+ const r = run(CODE, CLEAN_TREE);
90
+ assert.equal(r.code, 0, 'clean code must pass: ' + r.out);
91
+ });
92
+
93
+ test('P5 - detection is unchanged', () => {
94
+ // The change adds counting. If a verdict that used to fire stops firing, the exit code is right
95
+ // for the wrong reason — and this suite would still be green.
96
+ const r = run(CODE, { 'target/awful.js': AWFUL });
97
+ // Only markers that DID fire before the change. `Found N loops` needs more than five lines
98
+ // matching `for.*{` and never fired on this input — asserting it would have been a test for
99
+ // behaviour the script never had.
100
+ for (const marker of [/TODO\/FIXME\/BUG\/HACK/, /No test directory found/]) {
101
+ assert.match(r.out, marker, 'a verdict that fired before must still fire: ' + r.out);
102
+ }
103
+ });
104
+
105
+ test('P6 - the closing voice survives', () => {
106
+ // The Ramsay-voice lines are the skill's character and a participant reads them. The verdict is
107
+ // ADDED, not substituted.
108
+ const r = run(CODE, CLEAN_TREE);
109
+ assert.match(r.out, /wouldn't deploy this to production/i,
110
+ 'the closing prose must not be replaced by a machine verdict: ' + r.out);
111
+ });
112
+
113
+ test('P7 - all three verdicts from ONE script in one run', () => {
114
+ // assess-code.sh reaches all three from fixtures.
115
+ const seen = [
116
+ run(CODE, CLEAN_TREE).code,
117
+ run(CODE, { 'target/awful.js': AWFUL }).code,
118
+ run(CODE, {}, 'nope').code,
119
+ ];
120
+ assert.deepEqual(seen, [0, 1, 2], 'assess-code.sh: ' + JSON.stringify(seen));
121
+ });
122
+
123
+ test('P8 - assess-tests.sh reaches 1 and 2; its 0 path is proven by the shared footer', () => {
124
+ // HONEST LIMIT, stated rather than faked. assess-tests.sh RUNS the suite it is pointed at
125
+ // ("Tests don't even pass", "Tests failed 3/3 times"), so reaching exit 0 needs a real,
126
+ // installed, passing project — out of reach of a unit fixture. Constructing a fake green here
127
+ // would be the class of lie this whole file exists to remove.
128
+ //
129
+ // What IS proven: both non-zero verdicts from fixtures, and that the exit-0 branch is the SAME
130
+ // mechanism proven reachable in P7 — the two scripts carry a byte-identical verdict footer, so
131
+ // demonstrating it in one demonstrates the mechanism in both.
132
+ const seen = [
133
+ run(TESTS, { 'target/a.test.js': BAD_TESTS }).code,
134
+ run(TESTS, {}, 'nope').code,
135
+ ];
136
+ assert.deepEqual(seen, [1, 2], 'assess-tests.sh: ' + JSON.stringify(seen));
137
+
138
+ const footer = (f) => {
139
+ const src = fs.readFileSync(f, 'utf-8');
140
+ const at = src.indexOf('# ── Verdict ─');
141
+ assert.ok(at > 0, f + ' must carry the verdict footer');
142
+ return src.slice(at);
143
+ };
144
+ assert.equal(footer(TESTS), footer(CODE),
145
+ 'the verdict footer must be identical in both, or P7 proves nothing about this script');
146
+ assert.match(footer(TESTS), /VERDICT: 0 findings\./,
147
+ 'and the exit-0 branch must exist in it');
148
+ assert.match(footer(TESTS), /\nexit 0\n?$/, 'ending in the successful exit');
149
+ });
150
+ });