@opensip-cli/yagni 0.1.11 → 0.1.13
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/README.md +3 -3
- package/dist/__tests__/apply-advisory-exit.test.js +2 -1
- package/dist/__tests__/apply-advisory-exit.test.js.map +1 -1
- package/dist/__tests__/architecture-invariants.test.d.ts +6 -0
- package/dist/__tests__/architecture-invariants.test.d.ts.map +1 -0
- package/dist/__tests__/architecture-invariants.test.js +37 -0
- package/dist/__tests__/architecture-invariants.test.js.map +1 -0
- package/dist/__tests__/detector-progress.test.d.ts +2 -0
- package/dist/__tests__/detector-progress.test.d.ts.map +1 -0
- package/dist/__tests__/detector-progress.test.js +77 -0
- package/dist/__tests__/detector-progress.test.js.map +1 -0
- package/dist/__tests__/duplicate-parity.test.d.ts +15 -0
- package/dist/__tests__/duplicate-parity.test.d.ts.map +1 -0
- package/dist/__tests__/duplicate-parity.test.js +304 -0
- package/dist/__tests__/duplicate-parity.test.js.map +1 -0
- package/dist/__tests__/hardening.test.d.ts +2 -0
- package/dist/__tests__/hardening.test.d.ts.map +1 -0
- package/dist/__tests__/hardening.test.js +102 -0
- package/dist/__tests__/hardening.test.js.map +1 -0
- package/dist/__tests__/session-payload.test.d.ts +2 -0
- package/dist/__tests__/session-payload.test.d.ts.map +1 -0
- package/dist/__tests__/session-payload.test.js +61 -0
- package/dist/__tests__/session-payload.test.js.map +1 -0
- package/dist/__tests__/walk-typescript-files.test.js +15 -1
- package/dist/__tests__/walk-typescript-files.test.js.map +1 -1
- package/dist/__tests__/yagni-coverage.test.js +49 -202
- package/dist/__tests__/yagni-coverage.test.js.map +1 -1
- package/dist/__tests__/yagni-golden.test.js +8 -24
- package/dist/__tests__/yagni-golden.test.js.map +1 -1
- package/dist/__tests__/yagni-presentation.test.js +5 -5
- package/dist/__tests__/yagni-presentation.test.js.map +1 -1
- package/dist/baseline-strategy.d.ts +1 -2
- package/dist/baseline-strategy.d.ts.map +1 -1
- package/dist/baseline-strategy.js +21 -16
- package/dist/baseline-strategy.js.map +1 -1
- package/dist/cli/__tests__/yagni-runner.test.js +10 -7
- package/dist/cli/__tests__/yagni-runner.test.js.map +1 -1
- package/dist/cli/execute-yagni.d.ts +15 -4
- package/dist/cli/execute-yagni.d.ts.map +1 -1
- package/dist/cli/execute-yagni.js +68 -30
- package/dist/cli/execute-yagni.js.map +1 -1
- package/dist/cli/report-data.d.ts +0 -2
- package/dist/cli/report-data.d.ts.map +1 -1
- package/dist/cli/report-data.js +0 -2
- package/dist/cli/report-data.js.map +1 -1
- package/dist/cli/yagni-command-spec.d.ts.map +1 -1
- package/dist/cli/yagni-command-spec.js +4 -19
- package/dist/cli/yagni-command-spec.js.map +1 -1
- package/dist/cli/yagni-config-schema.d.ts +1 -7
- package/dist/cli/yagni-config-schema.d.ts.map +1 -1
- package/dist/cli/yagni-config-schema.js +4 -6
- package/dist/cli/yagni-config-schema.js.map +1 -1
- package/dist/cli/yagni-config.d.ts.map +1 -1
- package/dist/cli/yagni-config.js +3 -7
- package/dist/cli/yagni-config.js.map +1 -1
- package/dist/cli/yagni-presentation.d.ts +1 -2
- package/dist/cli/yagni-presentation.d.ts.map +1 -1
- package/dist/cli/yagni-presentation.js +4 -4
- package/dist/cli/yagni-presentation.js.map +1 -1
- package/dist/cli/yagni-runner.d.ts +0 -2
- package/dist/cli/yagni-runner.d.ts.map +1 -1
- package/dist/cli/yagni-runner.js +40 -8
- package/dist/cli/yagni-runner.js.map +1 -1
- package/dist/detectors/duplicate-body-candidate.d.ts +9 -2
- package/dist/detectors/duplicate-body-candidate.d.ts.map +1 -1
- package/dist/detectors/duplicate-body-candidate.js +123 -120
- package/dist/detectors/duplicate-body-candidate.js.map +1 -1
- package/dist/detectors/registry.d.ts +9 -1
- package/dist/detectors/registry.d.ts.map +1 -1
- package/dist/detectors/registry.js +9 -1
- package/dist/detectors/registry.js.map +1 -1
- package/dist/detectors/types.d.ts +1 -2
- package/dist/detectors/types.d.ts.map +1 -1
- package/dist/detectors/unused-config-surface.d.ts.map +1 -1
- package/dist/detectors/unused-config-surface.js +0 -2
- package/dist/detectors/unused-config-surface.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/build-ts-inventory.d.ts +33 -0
- package/dist/lib/build-ts-inventory.d.ts.map +1 -0
- package/dist/lib/build-ts-inventory.js +252 -0
- package/dist/lib/build-ts-inventory.js.map +1 -0
- package/dist/persistence/session-payload.d.ts +7 -10
- package/dist/persistence/session-payload.d.ts.map +1 -1
- package/dist/persistence/session-payload.js +64 -6
- package/dist/persistence/session-payload.js.map +1 -1
- package/dist/scoring/__tests__/confidence.test.js +3 -4
- package/dist/scoring/__tests__/confidence.test.js.map +1 -1
- package/dist/scoring/confidence.d.ts +1 -1
- package/dist/scoring/confidence.d.ts.map +1 -1
- package/dist/scoring/confidence.js +1 -2
- package/dist/scoring/confidence.js.map +1 -1
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +3 -1
- package/dist/tool.js.map +1 -1
- package/dist/types/yagni-config.d.ts +1 -4
- package/dist/types/yagni-config.d.ts.map +1 -1
- package/dist/types/yagni-config.js +0 -1
- package/dist/types/yagni-config.js.map +1 -1
- package/dist/types/yagni-metadata.d.ts +0 -1
- package/dist/types/yagni-metadata.d.ts.map +1 -1
- package/package.json +10 -10
- package/dist/__tests__/graph-evidence.test.d.ts +0 -2
- package/dist/__tests__/graph-evidence.test.d.ts.map +0 -1
- package/dist/__tests__/graph-evidence.test.js +0 -159
- package/dist/__tests__/graph-evidence.test.js.map +0 -1
- package/dist/evidence/graph-evidence.d.ts +0 -15
- package/dist/evidence/graph-evidence.d.ts.map +0 -1
- package/dist/evidence/graph-evidence.js +0 -89
- package/dist/evidence/graph-evidence.js.map +0 -1
- package/dist/evidence/load-graph-adapters.d.ts +0 -13
- package/dist/evidence/load-graph-adapters.d.ts.map +0 -1
- package/dist/evidence/load-graph-adapters.js +0 -67
- package/dist/evidence/load-graph-adapters.js.map +0 -1
- package/dist/lib/isolate-exit-code.d.ts +0 -8
- package/dist/lib/isolate-exit-code.d.ts.map +0 -1
- package/dist/lib/isolate-exit-code.js +0 -27
- package/dist/lib/isolate-exit-code.js.map +0 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
This package is the **engine** behind a first-party `opensip-cli` tool. The CLI bundles it — you do not normally install it directly.
|
|
10
10
|
|
|
11
|
-
Part of [**opensip-cli**](https://github.com/opensip-ai/opensip-cli) — an open-source codebase intelligence CLI: fitness checks (`fit`), static call-graph analysis (`graph`),
|
|
11
|
+
Part of [**opensip-cli**](https://github.com/opensip-ai/opensip-cli) — an open-source codebase intelligence CLI: fitness checks (`fit`), static call-graph analysis (`graph`), simulation (`sim`), and advisory reduction audits (`yagni`).
|
|
12
12
|
|
|
13
13
|
**Distribution model:** the published `opensip-cli` npm package is a full-stack bundle of first-party tools, check packs, and language adapters. Third-party tools install separately via the `opensipTools` manifest and host allowlist; a slim install without bundled tools is not a current goal.
|
|
14
14
|
|
|
@@ -25,8 +25,8 @@ This package is published for the CLI and advanced plugin authors; most users sh
|
|
|
25
25
|
## Documentation
|
|
26
26
|
|
|
27
27
|
- 📚 Project docs: https://opensip.ai/docs/opensip-cli/
|
|
28
|
-
- 🧭 Package catalog (what every package does): https://github.com/opensip-ai/opensip-cli/blob/v0.1.
|
|
29
|
-
- 📦 Source: https://github.com/opensip-ai/opensip-cli/tree/v0.1.
|
|
28
|
+
- 🧭 Package catalog (what every package does): https://github.com/opensip-ai/opensip-cli/blob/v0.1.13/docs/public/70-reference/02-package-catalog.md
|
|
29
|
+
- 📦 Source: https://github.com/opensip-ai/opensip-cli/tree/v0.1.13/packages/yagni/engine
|
|
30
30
|
|
|
31
31
|
## License
|
|
32
32
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EXIT_CODES } from '@opensip-cli/contracts';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
3
|
import { applyAdvisoryExitCode } from '../lib/apply-advisory-exit.js';
|
|
4
4
|
function cliWithExit(code) {
|
|
5
5
|
const state = { code };
|
|
@@ -9,6 +9,7 @@ function cliWithExit(code) {
|
|
|
9
9
|
state.code = next;
|
|
10
10
|
},
|
|
11
11
|
_state: state,
|
|
12
|
+
reportFailure: vi.fn(() => Promise.resolve()),
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
describe('applyAdvisoryExitCode', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-advisory-exit.test.js","sourceRoot":"","sources":["../../src/__tests__/apply-advisory-exit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"apply-advisory-exit.test.js","sourceRoot":"","sources":["../../src/__tests__/apply-advisory-exit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAElD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAItE,SAAS,WAAW,CAAC,IAAa;IAChC,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC;IACvB,OAAO;QACL,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI;QAC7B,WAAW,EAAE,CAAC,IAAY,EAAE,EAAE;YAC5B,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,MAAM,EAAE,KAAK;QACb,aAAa,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;KACe,CAAC;AACjE,CAAC;AAED,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,GAAG,GAAG,WAAW,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;QACxD,qBAAqB,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC;QACnE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,GAAG,GAAG,WAAW,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;QAClD,qBAAqB,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC;QACnE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,GAAG,GAAG,WAAW,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;QAClD,qBAAqB,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC;QACnE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"architecture-invariants.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/architecture-invariants.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Architectural invariant probes (ADR-0064): yagni must never import graph;
|
|
3
|
+
* @opensip-cli/clone-detection must stay a leaf.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { describe, expect, it } from 'vitest';
|
|
8
|
+
const REPO_ROOT = fileURLToPath(new URL('../../../../..', import.meta.url));
|
|
9
|
+
describe('ADR-0064 architectural invariants', () => {
|
|
10
|
+
it('yagni package.json declares no @opensip-cli/graph dependency', () => {
|
|
11
|
+
const pkg = JSON.parse(readFileSync(fileURLToPath(new URL('../../package.json', import.meta.url)), 'utf8'));
|
|
12
|
+
expect(Object.keys(pkg.dependencies ?? {})).not.toContain('@opensip-cli/graph');
|
|
13
|
+
});
|
|
14
|
+
it('clone-detection package.json declares no workspace dependencies', () => {
|
|
15
|
+
const pkg = JSON.parse(readFileSync(fileURLToPath(new URL('../../../../clone-detection/package.json', import.meta.url)), 'utf8'));
|
|
16
|
+
const all = [...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})];
|
|
17
|
+
expect(all.filter((n) => n.startsWith('@opensip-cli/'))).toEqual([]);
|
|
18
|
+
expect(all).not.toContain('opensipTools');
|
|
19
|
+
});
|
|
20
|
+
it('dependency-cruiser wires clone-detection-imports-nothing and yagni-no-graph rules', () => {
|
|
21
|
+
const config = readFileSync(`${REPO_ROOT}/.config/dependency-cruiser.cjs`, 'utf8');
|
|
22
|
+
expect(config).toContain('clone-detection-imports-nothing');
|
|
23
|
+
expect(config).toContain('yagni-no-graph-engine');
|
|
24
|
+
expect(config).toContain('yagni-no-graph-adapter-packs');
|
|
25
|
+
});
|
|
26
|
+
it('YagniDetector contract has no requiresGraph field (S5)', () => {
|
|
27
|
+
const types = readFileSync(fileURLToPath(new URL('../detectors/types.ts', import.meta.url)), 'utf8');
|
|
28
|
+
expect(types).not.toContain('requiresGraph');
|
|
29
|
+
expect(types).not.toContain('graph-unavailable');
|
|
30
|
+
});
|
|
31
|
+
it('planDetectors has no graph-gated branch', () => {
|
|
32
|
+
const execute = readFileSync(fileURLToPath(new URL('../cli/execute-yagni.ts', import.meta.url)), 'utf8');
|
|
33
|
+
expect(execute).not.toContain('requiresGraph');
|
|
34
|
+
expect(execute).not.toContain('graph-unavailable');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=architecture-invariants.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"architecture-invariants.test.js","sourceRoot":"","sources":["../../src/__tests__/architecture-invariants.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector-progress.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/detector-progress.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { executeYagni } from '../cli/execute-yagni.js';
|
|
3
|
+
import { unusedConfigSurfaceDetector } from '../detectors/unused-config-surface.js';
|
|
4
|
+
const FIXTURE_ROOT = new URL('fixtures/unused-config-surface/pkg', import.meta.url).pathname;
|
|
5
|
+
function stubCli() {
|
|
6
|
+
return {
|
|
7
|
+
scope: { datastore: () => undefined },
|
|
8
|
+
deliverSignals: vi.fn(() => Promise.resolve({ delivered: false })),
|
|
9
|
+
reportFailure: vi.fn(() => Promise.resolve()),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const disabledStub = {
|
|
13
|
+
id: 'disabled-stub',
|
|
14
|
+
slug: 'yagni:disabled-stub',
|
|
15
|
+
description: 'stub detector disabled via config (test only)',
|
|
16
|
+
run: () => Promise.resolve({ signals: [], durationMs: 0 }),
|
|
17
|
+
};
|
|
18
|
+
const throwingStub = {
|
|
19
|
+
id: 'throwing-stub',
|
|
20
|
+
slug: 'yagni:throwing-stub',
|
|
21
|
+
description: 'stub detector that throws (test only)',
|
|
22
|
+
run: () => Promise.reject(new Error('detector exploded')),
|
|
23
|
+
};
|
|
24
|
+
describe('executeYagni detector progress callbacks (phases live view)', () => {
|
|
25
|
+
it('reports start/done per detector that runs, and skips disabled detectors', async () => {
|
|
26
|
+
const started = [];
|
|
27
|
+
const done = [];
|
|
28
|
+
const skippedBatches = [];
|
|
29
|
+
await executeYagni({
|
|
30
|
+
cwd: FIXTURE_ROOT,
|
|
31
|
+
config: {
|
|
32
|
+
defaultMinConfidence: 'low',
|
|
33
|
+
disabledDetectors: ['yagni:disabled-stub'],
|
|
34
|
+
},
|
|
35
|
+
includeTests: true,
|
|
36
|
+
onDetectorStart: (slug) => started.push(slug),
|
|
37
|
+
onDetectorDone: (slug, durationMs) => done.push({ slug, durationMs }),
|
|
38
|
+
onDetectorsSkipped: (slugs) => skippedBatches.push([...slugs]),
|
|
39
|
+
}, stubCli(), [unusedConfigSurfaceDetector, disabledStub]);
|
|
40
|
+
expect(started).toEqual(['yagni:unused-config-surface']);
|
|
41
|
+
expect(done.map((d) => d.slug)).toEqual(['yagni:unused-config-surface']);
|
|
42
|
+
expect(typeof done[0]?.durationMs).toBe('number');
|
|
43
|
+
expect(skippedBatches).toEqual([['yagni:disabled-stub']]);
|
|
44
|
+
});
|
|
45
|
+
it('start precedes done for the same detector', async () => {
|
|
46
|
+
const events = [];
|
|
47
|
+
await executeYagni({
|
|
48
|
+
cwd: FIXTURE_ROOT,
|
|
49
|
+
config: { defaultMinConfidence: 'low' },
|
|
50
|
+
includeTests: true,
|
|
51
|
+
onDetectorStart: (slug) => events.push(`start:${slug}`),
|
|
52
|
+
onDetectorDone: (slug) => events.push(`done:${slug}`),
|
|
53
|
+
}, stubCli(), [unusedConfigSurfaceDetector]);
|
|
54
|
+
expect(events).toEqual([
|
|
55
|
+
'start:yagni:unused-config-surface',
|
|
56
|
+
'done:yagni:unused-config-surface',
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
it('records a failed unit when a detector throws', async () => {
|
|
60
|
+
const events = [];
|
|
61
|
+
const outcome = await executeYagni({
|
|
62
|
+
cwd: FIXTURE_ROOT,
|
|
63
|
+
config: { defaultMinConfidence: 'low' },
|
|
64
|
+
includeTests: true,
|
|
65
|
+
onDetectorStart: (slug) => events.push(`start:${slug}`),
|
|
66
|
+
onDetectorDone: (slug) => events.push(`done:${slug}`),
|
|
67
|
+
}, stubCli(), [throwingStub]);
|
|
68
|
+
expect(events).toEqual(['start:yagni:throwing-stub', 'done:yagni:throwing-stub']);
|
|
69
|
+
expect(outcome.envelope.units).toHaveLength(1);
|
|
70
|
+
expect(outcome.envelope.units[0]).toMatchObject({
|
|
71
|
+
slug: 'yagni:throwing-stub',
|
|
72
|
+
passed: false,
|
|
73
|
+
error: 'detector exploded',
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=detector-progress.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector-progress.test.js","sourceRoot":"","sources":["../../src/__tests__/detector-progress.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplicate-detection parity + policy guard (ADR-0064).
|
|
3
|
+
*
|
|
4
|
+
* yagni and graph share ONE implementation: the detection algorithm + curation policy
|
|
5
|
+
* live in `@opensip-cli/clone-detection` (`findDuplicateBodies`), and the body hash is
|
|
6
|
+
* computed by the same `digestCanonicalBody(normalizeWhitespace(stripComments(slice)))`
|
|
7
|
+
* pipeline both tools use (yagni: `build-ts-inventory.ts`; graph:
|
|
8
|
+
* `graph-typescript/inventory-helpers/hash-body.ts`). This test pins yagni's extraction +
|
|
9
|
+
* the shared policy on a fixture corpus that exercises every branch — the standing guard
|
|
10
|
+
* against the 430-vs-0 (filter-divergence) regression class. The full both-binaries
|
|
11
|
+
* cross-tool run on a real repo is Phase 5 validation (Task 5.2), where graph has the heap
|
|
12
|
+
* to build a complete catalog.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=duplicate-parity.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate-parity.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/duplicate-parity.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplicate-detection parity + policy guard (ADR-0064).
|
|
3
|
+
*
|
|
4
|
+
* yagni and graph share ONE implementation: the detection algorithm + curation policy
|
|
5
|
+
* live in `@opensip-cli/clone-detection` (`findDuplicateBodies`), and the body hash is
|
|
6
|
+
* computed by the same `digestCanonicalBody(normalizeWhitespace(stripComments(slice)))`
|
|
7
|
+
* pipeline both tools use (yagni: `build-ts-inventory.ts`; graph:
|
|
8
|
+
* `graph-typescript/inventory-helpers/hash-body.ts`). This test pins yagni's extraction +
|
|
9
|
+
* the shared policy on a fixture corpus that exercises every branch — the standing guard
|
|
10
|
+
* against the 430-vs-0 (filter-divergence) regression class. The full both-binaries
|
|
11
|
+
* cross-tool run on a real repo is Phase 5 validation (Task 5.2), where graph has the heap
|
|
12
|
+
* to build a complete catalog.
|
|
13
|
+
*/
|
|
14
|
+
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
15
|
+
import { tmpdir } from 'node:os';
|
|
16
|
+
import { dirname, join } from 'node:path';
|
|
17
|
+
import { digestCanonicalBody, findDuplicateBodies, isEligibleKind, normalizeWhitespace, } from '@opensip-cli/clone-detection';
|
|
18
|
+
import { RunScope, runWithScope } from '@opensip-cli/core';
|
|
19
|
+
import { typescriptGraphAdapter } from '@opensip-cli/graph-typescript';
|
|
20
|
+
import { stripComments } from '@opensip-cli/lang-typescript';
|
|
21
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
22
|
+
import { duplicateBodyCandidateDetector } from '../detectors/duplicate-body-candidate.js';
|
|
23
|
+
import { buildTsInventory } from '../lib/build-ts-inventory.js';
|
|
24
|
+
import { readYagniMetadata } from '../scoring/confidence.js';
|
|
25
|
+
// A function whose normalized body is >= 200 chars (passes the per-instance floor).
|
|
26
|
+
const BIG = `export function summarize(items: readonly number[]): { total: number; max: number; count: number } {
|
|
27
|
+
let total = 0;
|
|
28
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
29
|
+
for (const item of items) {
|
|
30
|
+
total += item;
|
|
31
|
+
if (item > max) { max = item; }
|
|
32
|
+
}
|
|
33
|
+
return { total, max, count: items.length };
|
|
34
|
+
}`;
|
|
35
|
+
// A function whose normalized body is between 80 and 200 chars (passes the cross-package
|
|
36
|
+
// floor of 80, fails the per-instance floor of 200).
|
|
37
|
+
const MEDIUM = `export function normalizeName(raw: string): string {
|
|
38
|
+
const trimmed = raw.trim().toLowerCase();
|
|
39
|
+
return trimmed.replace(/[^a-z0-9]+/g, '-');
|
|
40
|
+
}`;
|
|
41
|
+
// A function whose normalized body is < 80 chars (fails the cross-package floor).
|
|
42
|
+
const THIN = `export function noop(): void {
|
|
43
|
+
return;
|
|
44
|
+
}`;
|
|
45
|
+
// An arrow function — excluded by kind on both tools (yagni emits only eligible kinds).
|
|
46
|
+
const ARROW = `export const handler = (x: number): number => {
|
|
47
|
+
const doubled = x * 2;
|
|
48
|
+
return doubled + 1;
|
|
49
|
+
};`;
|
|
50
|
+
const STATIC_BLOCK = ` static {
|
|
51
|
+
const values = [11, 13, 17, 19, 23, 29, 31, 37];
|
|
52
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
53
|
+
const label = values.map((value) => String(value * total)).join(':');
|
|
54
|
+
globalThis.__opensipStaticParity = { label, total, count: values.length };
|
|
55
|
+
}`;
|
|
56
|
+
const LOOSE_UNIQUE = `export function looseOnly(value: number): number {
|
|
57
|
+
const scaled = value * 3;
|
|
58
|
+
return scaled + 7;
|
|
59
|
+
}`;
|
|
60
|
+
const NAMELESS_CHILD_UNIQUE = `export function namelessChildOnly(value: number): number {
|
|
61
|
+
const shifted = value + 101;
|
|
62
|
+
return shifted * 2;
|
|
63
|
+
}`;
|
|
64
|
+
const EMPTY_NAME_CHILD_UNIQUE = `export function emptyNameChildOnly(value: number): number {
|
|
65
|
+
const shifted = value + 211;
|
|
66
|
+
return shifted * 3;
|
|
67
|
+
}`;
|
|
68
|
+
let root;
|
|
69
|
+
beforeAll(() => {
|
|
70
|
+
root = mkdtempSync(join(tmpdir(), 'yagni-parity-'));
|
|
71
|
+
writeFileSync(join(root, 'tsconfig.json'), JSON.stringify({
|
|
72
|
+
compilerOptions: {
|
|
73
|
+
target: 'ES2022',
|
|
74
|
+
module: 'Node16',
|
|
75
|
+
moduleResolution: 'Node16',
|
|
76
|
+
skipLibCheck: true,
|
|
77
|
+
},
|
|
78
|
+
include: ['**/*.ts', '**/*.tsx'],
|
|
79
|
+
}));
|
|
80
|
+
const pkg = (name) => {
|
|
81
|
+
const dir = join(root, name);
|
|
82
|
+
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
83
|
+
writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: `@p/${name}` }));
|
|
84
|
+
return dir;
|
|
85
|
+
};
|
|
86
|
+
const a = pkg('a');
|
|
87
|
+
const b = pkg('b');
|
|
88
|
+
const c = pkg('c');
|
|
89
|
+
mkdirSync(join(root, 'loose'), { recursive: true });
|
|
90
|
+
writeFileSync(join(root, 'loose', 'unique.ts'), LOOSE_UNIQUE);
|
|
91
|
+
mkdirSync(join(root, 'parent', 'nameless'), { recursive: true });
|
|
92
|
+
writeFileSync(join(root, 'parent', 'package.json'), JSON.stringify({ name: '@p/parent' }));
|
|
93
|
+
writeFileSync(join(root, 'parent', 'nameless', 'package.json'), JSON.stringify({}));
|
|
94
|
+
writeFileSync(join(root, 'parent', 'nameless', 'unique.ts'), NAMELESS_CHILD_UNIQUE);
|
|
95
|
+
mkdirSync(join(root, 'parent', 'empty-name'), { recursive: true });
|
|
96
|
+
writeFileSync(join(root, 'parent', 'empty-name', 'package.json'), JSON.stringify({ name: '' }));
|
|
97
|
+
writeFileSync(join(root, 'parent', 'empty-name', 'unique.ts'), EMPTY_NAME_CHILD_UNIQUE);
|
|
98
|
+
// MEDIUM duplicated across 3 packages → cross-package aggregate (3 occurrences).
|
|
99
|
+
writeFileSync(join(a, 'src', 'u.ts'), MEDIUM);
|
|
100
|
+
writeFileSync(join(b, 'src', 'u.ts'), MEDIUM);
|
|
101
|
+
writeFileSync(join(c, 'src', 'u.ts'), MEDIUM);
|
|
102
|
+
// …plus a copy in a TEST file → excluded by inTestFile (must NOT raise the count to 4).
|
|
103
|
+
writeFileSync(join(a, 'src', 'u.test.ts'), MEDIUM);
|
|
104
|
+
// BIG duplicated twice within ONE package → per-instance group (2 members).
|
|
105
|
+
writeFileSync(join(a, 'src', 'big1.ts'), BIG);
|
|
106
|
+
writeFileSync(join(a, 'src', 'big2.ts'), BIG);
|
|
107
|
+
// Class static blocks are graph-emitted as '<static-init>' function-declarations and
|
|
108
|
+
// must also be emitted by yagni. Duplicated twice within one package → per-instance group.
|
|
109
|
+
writeFileSync(join(a, 'src', 'static1.ts'), `export class StaticOne {\n${STATIC_BLOCK}\n}\n`);
|
|
110
|
+
writeFileSync(join(a, 'src', 'static2.ts'), `export class StaticTwo {\n${STATIC_BLOCK}\n}\n`);
|
|
111
|
+
// THIN duplicated across 3 packages → excluded (below the 80-char cross-package floor).
|
|
112
|
+
writeFileSync(join(a, 'src', 'thin.ts'), THIN);
|
|
113
|
+
writeFileSync(join(b, 'src', 'thin.ts'), THIN);
|
|
114
|
+
writeFileSync(join(c, 'src', 'thin.ts'), THIN);
|
|
115
|
+
// ARROW duplicated across packages → excluded by kind (never emitted by yagni).
|
|
116
|
+
writeFileSync(join(a, 'src', 'arr.ts'), ARROW);
|
|
117
|
+
writeFileSync(join(b, 'src', 'arr.ts'), ARROW);
|
|
118
|
+
// Graph's TS discovery excludes declarations and module-format TS files today; yagni's
|
|
119
|
+
// walker must keep the same duplicate-detection source scope.
|
|
120
|
+
writeFileSync(join(a, 'src', 'ambient.d.ts'), `declare class Ambient { constructor(); }\n`);
|
|
121
|
+
writeFileSync(join(a, 'src', 'module.mts'), MEDIUM);
|
|
122
|
+
writeFileSync(join(a, 'src', 'common.cts'), MEDIUM);
|
|
123
|
+
// A class + default-export + an overload signature — exercises the method / constructor /
|
|
124
|
+
// getter / setter / default-export classify branches and the body-less skip. Single
|
|
125
|
+
// package, not duplicated, so it adds no group.
|
|
126
|
+
writeFileSync(join(a, 'src', 'cls.ts'), `export class Widget {
|
|
127
|
+
constructor(private readonly base: number) {}
|
|
128
|
+
get value(): number { return this.base; }
|
|
129
|
+
set value(v: number) { /* readonly demo */ void v; }
|
|
130
|
+
compute(): number { return this.base * 2 + 1; }
|
|
131
|
+
}
|
|
132
|
+
export default function (): number { return 42; }
|
|
133
|
+
export function overload(x: number): number;
|
|
134
|
+
export function overload(x: string): string;
|
|
135
|
+
export function overload(x: unknown): unknown { return x; }
|
|
136
|
+
`);
|
|
137
|
+
});
|
|
138
|
+
afterAll(() => {
|
|
139
|
+
rmSync(root, { recursive: true, force: true });
|
|
140
|
+
});
|
|
141
|
+
describe('yagni duplicate detection — extraction + shared policy parity', () => {
|
|
142
|
+
it('matches graph-typescript eligible CloneCandidates and exact-duplicate findings', () => {
|
|
143
|
+
const yagniCandidates = buildTsInventory(root).filter(isEligibleKind);
|
|
144
|
+
const graphCandidates = graphEligibleCandidates(root);
|
|
145
|
+
expect(candidateSignatures(yagniCandidates)).toEqual(candidateSignatures(graphCandidates));
|
|
146
|
+
expect(duplicateFindingsSignature(yagniCandidates)).toEqual(duplicateFindingsSignature(graphCandidates));
|
|
147
|
+
});
|
|
148
|
+
it('computes byte-identical bodyHash via the canonical pipeline graph uses', () => {
|
|
149
|
+
const candidates = buildTsInventory(root);
|
|
150
|
+
const medium = candidates.find((c) => c.simpleName === 'normalizeName');
|
|
151
|
+
expect(medium).toBeDefined();
|
|
152
|
+
// The exact pipeline graph-typescript's digestFunctionBody uses — same shared fns.
|
|
153
|
+
const expected = digestCanonicalBody(normalizeWhitespace(stripComments(MEDIUM)));
|
|
154
|
+
expect(medium?.bodyHash).toBe(expected.hash);
|
|
155
|
+
expect(medium?.bodySize).toBe(expected.size);
|
|
156
|
+
// All copies share one hash (so they group).
|
|
157
|
+
const mediumHashes = new Set(candidates.filter((c) => c.simpleName === 'normalizeName').map((c) => c.bodyHash));
|
|
158
|
+
expect(mediumHashes.size).toBe(1);
|
|
159
|
+
});
|
|
160
|
+
it('applies the shared curation policy exactly (cross-package, test-exclusion, floors, kind)', () => {
|
|
161
|
+
const candidates = buildTsInventory(root);
|
|
162
|
+
const { aggregates, groups } = findDuplicateBodies(candidates);
|
|
163
|
+
// Exactly ONE cross-package aggregate: MEDIUM across @p/a, @p/b, @p/c.
|
|
164
|
+
expect(aggregates).toHaveLength(1);
|
|
165
|
+
const agg = aggregates[0];
|
|
166
|
+
expect(agg?.packages).toEqual(['@p/a', '@p/b', '@p/c']);
|
|
167
|
+
// 3 occurrences — the test-file copy in @p/a is EXCLUDED (not 4).
|
|
168
|
+
expect(agg?.occurrenceCount).toBe(3);
|
|
169
|
+
// Exactly TWO per-instance groups: BIG twice and STATIC_BLOCK twice in @p/a.
|
|
170
|
+
expect(groups).toHaveLength(2);
|
|
171
|
+
const groupNames = groups.map((g) => g.members.map((m) => m.simpleName).sort()).sort();
|
|
172
|
+
expect(groupNames).toEqual([
|
|
173
|
+
['<static-init>', '<static-init>'],
|
|
174
|
+
['summarize', 'summarize'],
|
|
175
|
+
]);
|
|
176
|
+
// THIN (below floor) and ARROW (excluded kind) produce no finding.
|
|
177
|
+
const allHashes = [...aggregates.map((a) => a.bodyHash), ...groups.map((g) => g.bodyHash)];
|
|
178
|
+
const thinHash = digestCanonicalBody(normalizeWhitespace(stripComments(THIN))).hash;
|
|
179
|
+
expect(allHashes).not.toContain(thinHash);
|
|
180
|
+
expect(candidates.some((c) => c.kind === 'arrow')).toBe(false);
|
|
181
|
+
});
|
|
182
|
+
it('classifies class members + default export, and skips body-less overloads', () => {
|
|
183
|
+
const candidates = buildTsInventory(root);
|
|
184
|
+
const kinds = new Set(candidates.filter((c) => c.filePath.endsWith('cls.ts')).map((c) => c.kind));
|
|
185
|
+
expect(kinds).toEqual(new Set(['method', 'constructor', 'getter', 'setter', 'function-declaration']));
|
|
186
|
+
// The default-export function records the '<default>' name; the overload SIGNATURE
|
|
187
|
+
// (body-less) is skipped — only the implementation is a candidate.
|
|
188
|
+
const names = candidates.filter((c) => c.filePath.endsWith('cls.ts')).map((c) => c.simpleName);
|
|
189
|
+
expect(names).toContain('<default>');
|
|
190
|
+
expect(names).toContain('Widget');
|
|
191
|
+
expect(names.filter((n) => n === 'overload')).toHaveLength(1);
|
|
192
|
+
});
|
|
193
|
+
it('the detector emits reduction findings with reduction metadata (aggregate + per-instance)', async () => {
|
|
194
|
+
const result = await runWithScope(new RunScope(), () => duplicateBodyCandidateDetector.run({
|
|
195
|
+
cwd: root,
|
|
196
|
+
config: {},
|
|
197
|
+
graphCatalog: null,
|
|
198
|
+
includeTests: false,
|
|
199
|
+
}));
|
|
200
|
+
// 1 cross-package aggregate + 2 per-instance groups = 3 findings.
|
|
201
|
+
expect(result.signals).toHaveLength(3);
|
|
202
|
+
const metas = result.signals.map((s) => readYagniMetadata(s));
|
|
203
|
+
expect(metas.every((m) => m?.reductionCategory === 'dedupe')).toBe(true);
|
|
204
|
+
expect(metas.every((m) => m?.confidence === 'medium')).toBe(true);
|
|
205
|
+
expect(metas.every((m) => (m?.locDelta?.netEstimate ?? -1) >= 0)).toBe(true);
|
|
206
|
+
// The cross-package finding carries the 'cross-package' risk tag; the per-instance one does not.
|
|
207
|
+
expect(metas.some((m) => m?.riskTags.includes('cross-package'))).toBe(true);
|
|
208
|
+
expect(metas.some((m) => m?.riskTags.length === 0)).toBe(true);
|
|
209
|
+
expect(result.durationMs).toBeGreaterThanOrEqual(0);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
function graphEligibleCandidates(projectRoot) {
|
|
213
|
+
const discovery = typescriptGraphAdapter.discoverFiles({ cwd: projectRoot });
|
|
214
|
+
const parsed = typescriptGraphAdapter.parseProject({
|
|
215
|
+
projectDirAbs: discovery.projectDirAbs,
|
|
216
|
+
files: discovery.files,
|
|
217
|
+
configPathAbs: discovery.configPathAbs,
|
|
218
|
+
compilerOptions: discovery.compilerOptions,
|
|
219
|
+
resolutionMode: 'exact',
|
|
220
|
+
});
|
|
221
|
+
expect(parsed.parseErrors).toEqual([]);
|
|
222
|
+
const walked = typescriptGraphAdapter.walkProject({
|
|
223
|
+
project: parsed.project,
|
|
224
|
+
projectDirAbs: discovery.projectDirAbs,
|
|
225
|
+
files: discovery.files,
|
|
226
|
+
});
|
|
227
|
+
expect(walked.parseErrors).toEqual([]);
|
|
228
|
+
return Object.values(walked.occurrences)
|
|
229
|
+
.flatMap((occs) => occs.map((occ) => graphOccurrenceToCandidate(projectRoot, occ)))
|
|
230
|
+
.filter(isEligibleKind);
|
|
231
|
+
}
|
|
232
|
+
function graphOccurrenceToCandidate(projectRoot, occ) {
|
|
233
|
+
return {
|
|
234
|
+
bodyHash: occ.bodyHash,
|
|
235
|
+
kind: occ.kind,
|
|
236
|
+
inTestFile: occ.inTestFile,
|
|
237
|
+
filePath: occ.filePath,
|
|
238
|
+
line: occ.line,
|
|
239
|
+
column: occ.column,
|
|
240
|
+
endLine: occ.endLine,
|
|
241
|
+
simpleName: occ.simpleName,
|
|
242
|
+
qualifiedName: occ.qualifiedName,
|
|
243
|
+
...(occ.bodySize === undefined ? {} : { bodySize: occ.bodySize }),
|
|
244
|
+
package: packageFor(projectRoot, occ.filePath),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function packageFor(projectRoot, projectRel) {
|
|
248
|
+
let dir = dirname(join(projectRoot, projectRel));
|
|
249
|
+
while (dir.startsWith(projectRoot)) {
|
|
250
|
+
try {
|
|
251
|
+
const raw = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
|
|
252
|
+
if (typeof raw.name === 'string' && raw.name.length > 0)
|
|
253
|
+
return raw.name;
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// Keep walking toward the project root.
|
|
257
|
+
}
|
|
258
|
+
const parent = dirname(dir);
|
|
259
|
+
if (parent === dir)
|
|
260
|
+
break;
|
|
261
|
+
dir = parent;
|
|
262
|
+
}
|
|
263
|
+
const segment = projectRel.split('/')[0];
|
|
264
|
+
return segment && segment !== projectRel ? segment : '<unknown>';
|
|
265
|
+
}
|
|
266
|
+
function candidateSignatures(candidates) {
|
|
267
|
+
return candidates
|
|
268
|
+
.map((c) => [
|
|
269
|
+
c.bodyHash,
|
|
270
|
+
String(c.bodySize ?? ''),
|
|
271
|
+
c.kind,
|
|
272
|
+
String(c.inTestFile),
|
|
273
|
+
c.filePath,
|
|
274
|
+
String(c.line),
|
|
275
|
+
String(c.column),
|
|
276
|
+
String(c.endLine),
|
|
277
|
+
c.simpleName,
|
|
278
|
+
c.qualifiedName,
|
|
279
|
+
c.package ?? '',
|
|
280
|
+
].join('|'))
|
|
281
|
+
.sort();
|
|
282
|
+
}
|
|
283
|
+
function duplicateFindingsSignature(candidates) {
|
|
284
|
+
const findings = findDuplicateBodies(candidates);
|
|
285
|
+
return {
|
|
286
|
+
aggregates: findings.aggregates
|
|
287
|
+
.map((a) => `${a.bodyHash}|${a.packages.join(',')}|${String(a.occurrenceCount)}|${memberId(a.anchor)}`)
|
|
288
|
+
.sort(),
|
|
289
|
+
groups: findings.groups
|
|
290
|
+
.map((g) => `${g.bodyHash}|${g.members.map(memberId).sort().join(',')}`)
|
|
291
|
+
.sort(),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function memberId(candidate) {
|
|
295
|
+
return [
|
|
296
|
+
candidate.qualifiedName,
|
|
297
|
+
candidate.filePath,
|
|
298
|
+
String(candidate.line),
|
|
299
|
+
String(candidate.column),
|
|
300
|
+
candidate.simpleName,
|
|
301
|
+
candidate.package ?? '',
|
|
302
|
+
].join('@');
|
|
303
|
+
}
|
|
304
|
+
//# sourceMappingURL=duplicate-parity.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate-parity.test.js","sourceRoot":"","sources":["../../src/__tests__/duplicate-parity.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hardening.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/hardening.test.ts"],"names":[],"mappings":""}
|