@principles/core 1.240.2 → 1.240.4

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,25 @@
1
+ /**
2
+ * Pruning Mask Tests — Core Package
3
+ *
4
+ * Direct unit tests for the pruning mask module:
5
+ * - buildMaskedPrincipleSet() — pure LWW (Last Write Wins) function
6
+ * - getCachedMaskedPrincipleSet() — TTL-cached wrapper
7
+ * - clearPruningMaskCache() — cache invalidation
8
+ *
9
+ * Tests verify:
10
+ * - Empty/null input → empty set
11
+ * - Single review → correct masking
12
+ * - Multiple reviews for same principle → LWW semantics
13
+ * - Corrupt records → skipped silently
14
+ * - Archive-candidate → masked; keep/defer → not masked
15
+ * - TTL cache: returns cached value within TTL, re-reads after expiry
16
+ * - Cache keyed by workspaceDir
17
+ * - clearPruningMaskCache resets cache state
18
+ *
19
+ * ERR checklist:
20
+ * - ERR-002 / EP-03: corrupt records skipped (fail-safe)
21
+ * - ERR-007 / EP-02: single source for LWW decision
22
+ * - EP-07: cache keyed by workspaceDir prevents cross-workspace leakage
23
+ */
24
+ export {};
25
+ //# sourceMappingURL=pruning-mask.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pruning-mask.test.d.ts","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/pruning-mask.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Pruning Mask Tests — Core Package
3
+ *
4
+ * Direct unit tests for the pruning mask module:
5
+ * - buildMaskedPrincipleSet() — pure LWW (Last Write Wins) function
6
+ * - getCachedMaskedPrincipleSet() — TTL-cached wrapper
7
+ * - clearPruningMaskCache() — cache invalidation
8
+ *
9
+ * Tests verify:
10
+ * - Empty/null input → empty set
11
+ * - Single review → correct masking
12
+ * - Multiple reviews for same principle → LWW semantics
13
+ * - Corrupt records → skipped silently
14
+ * - Archive-candidate → masked; keep/defer → not masked
15
+ * - TTL cache: returns cached value within TTL, re-reads after expiry
16
+ * - Cache keyed by workspaceDir
17
+ * - clearPruningMaskCache resets cache state
18
+ *
19
+ * ERR checklist:
20
+ * - ERR-002 / EP-03: corrupt records skipped (fail-safe)
21
+ * - ERR-007 / EP-02: single source for LWW decision
22
+ * - EP-07: cache keyed by workspaceDir prevents cross-workspace leakage
23
+ */
24
+ /* eslint-disable @typescript-eslint/max-params */
25
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
26
+ import * as fs from 'fs';
27
+ import * as path from 'path';
28
+ import * as os from 'os';
29
+ import { buildMaskedPrincipleSet, getCachedMaskedPrincipleSet, clearPruningMaskCache, } from '../pruning-mask.js';
30
+ function makeReview(principleId, decision, reviewedAt, overrides = {}) {
31
+ return {
32
+ reviewId: `review-${principleId}-${Date.now()}`,
33
+ principleId,
34
+ decision,
35
+ note: 'test note',
36
+ reviewer: 'test-reviewer',
37
+ reviewedAt,
38
+ signalSnapshot: {
39
+ principleId,
40
+ status: 'active',
41
+ createdAt: reviewedAt,
42
+ updatedAt: reviewedAt,
43
+ derivedCandidateIds: [],
44
+ derivedPainCount: 0,
45
+ matchedCandidateCount: 0,
46
+ recentCandidateCount: 0,
47
+ orphanCandidateCount: 0,
48
+ ageDays: 1,
49
+ riskLevel: 'watch',
50
+ reasons: [],
51
+ },
52
+ ...overrides,
53
+ };
54
+ }
55
+ describe('buildMaskedPrincipleSet', () => {
56
+ it('returns empty set for null input', () => {
57
+ const result = buildMaskedPrincipleSet(null);
58
+ expect(result.size).toBe(0);
59
+ });
60
+ it('returns empty set for empty array', () => {
61
+ const result = buildMaskedPrincipleSet([]);
62
+ expect(result.size).toBe(0);
63
+ });
64
+ it('masks principles with archive-candidate decision', () => {
65
+ const reviews = [
66
+ makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
67
+ makeReview('p2', 'keep', '2026-07-01T00:00:00.000Z'),
68
+ makeReview('p3', 'defer', '2026-07-01T00:00:00.000Z'),
69
+ ];
70
+ const result = buildMaskedPrincipleSet(reviews);
71
+ expect(result.size).toBe(1);
72
+ expect(result.has('p1')).toBe(true);
73
+ expect(result.has('p2')).toBe(false);
74
+ expect(result.has('p3')).toBe(false);
75
+ });
76
+ it('applies LWW semantics: later review overrides earlier one', () => {
77
+ const reviews = [
78
+ makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
79
+ makeReview('p1', 'keep', '2026-07-02T00:00:00.000Z'),
80
+ ];
81
+ const result = buildMaskedPrincipleSet(reviews);
82
+ expect(result.size).toBe(0);
83
+ expect(result.has('p1')).toBe(false);
84
+ });
85
+ it('applies LWW semantics: later archive-candidate overrides earlier keep', () => {
86
+ const reviews = [
87
+ makeReview('p1', 'keep', '2026-07-01T00:00:00.000Z'),
88
+ makeReview('p1', 'archive-candidate', '2026-07-02T00:00:00.000Z'),
89
+ ];
90
+ const result = buildMaskedPrincipleSet(reviews);
91
+ expect(result.size).toBe(1);
92
+ expect(result.has('p1')).toBe(true);
93
+ });
94
+ it('handles same timestamp: later array entry wins (stable ordering)', () => {
95
+ const reviews = [
96
+ makeReview('p1', 'keep', '2026-07-01T00:00:00.000Z'),
97
+ makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
98
+ ];
99
+ const result = buildMaskedPrincipleSet(reviews);
100
+ expect(result.has('p1')).toBe(true);
101
+ });
102
+ it('skips corrupt records with missing principleId', () => {
103
+ const reviews = [
104
+ makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
105
+ { ...makeReview('p2', 'archive-candidate', '2026-07-01T00:00:00.000Z'), principleId: '' },
106
+ { ...makeReview('p3', 'archive-candidate', '2026-07-01T00:00:00.000Z'), principleId: undefined },
107
+ ];
108
+ const result = buildMaskedPrincipleSet(reviews);
109
+ expect(result.size).toBe(1);
110
+ expect(result.has('p1')).toBe(true);
111
+ });
112
+ it('skips corrupt records with missing decision', () => {
113
+ const reviews = [
114
+ makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
115
+ { ...makeReview('p2', 'archive-candidate', '2026-07-01T00:00:00.000Z'), decision: '' },
116
+ { ...makeReview('p3', 'archive-candidate', '2026-07-01T00:00:00.000Z'), decision: undefined },
117
+ ];
118
+ const result = buildMaskedPrincipleSet(reviews);
119
+ expect(result.size).toBe(1);
120
+ expect(result.has('p1')).toBe(true);
121
+ });
122
+ it('skips corrupt records with missing reviewedAt', () => {
123
+ const reviews = [
124
+ makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
125
+ { ...makeReview('p2', 'archive-candidate', '2026-07-01T00:00:00.000Z'), reviewedAt: '' },
126
+ { ...makeReview('p3', 'archive-candidate', '2026-07-01T00:00:00.000Z'), reviewedAt: undefined },
127
+ ];
128
+ const result = buildMaskedPrincipleSet(reviews);
129
+ expect(result.size).toBe(1);
130
+ expect(result.has('p1')).toBe(true);
131
+ });
132
+ it('skips null/undefined entries in the array', () => {
133
+ const reviews = [
134
+ makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
135
+ null,
136
+ undefined,
137
+ ];
138
+ const result = buildMaskedPrincipleSet(reviews);
139
+ expect(result.size).toBe(1);
140
+ expect(result.has('p1')).toBe(true);
141
+ });
142
+ it('handles multiple principles with mixed decisions', () => {
143
+ const reviews = [
144
+ makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
145
+ makeReview('p2', 'keep', '2026-07-01T00:00:00.000Z'),
146
+ makeReview('p3', 'defer', '2026-07-01T00:00:00.000Z'),
147
+ makeReview('p4', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
148
+ makeReview('p2', 'archive-candidate', '2026-07-02T00:00:00.000Z'),
149
+ makeReview('p4', 'keep', '2026-07-03T00:00:00.000Z'),
150
+ ];
151
+ const result = buildMaskedPrincipleSet(reviews);
152
+ expect(result.size).toBe(2);
153
+ expect(result.has('p1')).toBe(true);
154
+ expect(result.has('p2')).toBe(true);
155
+ expect(result.has('p3')).toBe(false);
156
+ expect(result.has('p4')).toBe(false);
157
+ });
158
+ it('returns a new Set instance each call (no shared mutable state)', () => {
159
+ const reviews = [makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z')];
160
+ const result1 = buildMaskedPrincipleSet(reviews);
161
+ const result2 = buildMaskedPrincipleSet(reviews);
162
+ expect(result1).not.toBe(result2);
163
+ result1.add('p999');
164
+ expect(result2.has('p999')).toBe(false);
165
+ });
166
+ });
167
+ describe('getCachedMaskedPrincipleSet', () => {
168
+ let tmpDir;
169
+ let stateDir;
170
+ let logPath;
171
+ beforeEach(() => {
172
+ clearPruningMaskCache();
173
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-pruning-mask-test-'));
174
+ stateDir = path.join(tmpDir, '.state');
175
+ fs.mkdirSync(stateDir, { recursive: true });
176
+ logPath = path.join(stateDir, 'pruning_reviews.jsonl');
177
+ });
178
+ afterEach(() => {
179
+ fs.rmSync(tmpDir, { recursive: true, force: true });
180
+ });
181
+ function writeReviews(reviews) {
182
+ const lines = reviews.map(r => JSON.stringify(r));
183
+ fs.writeFileSync(logPath, lines.join('\n') + '\n', 'utf-8');
184
+ }
185
+ it('reads from file and returns correct mask on first call', () => {
186
+ const reviews = [
187
+ makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
188
+ makeReview('p2', 'keep', '2026-07-01T00:00:00.000Z'),
189
+ ];
190
+ writeReviews(reviews);
191
+ const result = getCachedMaskedPrincipleSet(tmpDir);
192
+ expect(result.size).toBe(1);
193
+ expect(result.has('p1')).toBe(true);
194
+ expect(result.has('p2')).toBe(false);
195
+ });
196
+ it('returns cached value within TTL (no re-read)', () => {
197
+ const reviews = [makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z')];
198
+ writeReviews(reviews);
199
+ const result1 = getCachedMaskedPrincipleSet(tmpDir, 60_000);
200
+ const updatedReviews = [makeReview('p1', 'keep', '2026-07-02T00:00:00.000Z')];
201
+ writeReviews(updatedReviews);
202
+ const result2 = getCachedMaskedPrincipleSet(tmpDir, 60_000);
203
+ expect(result2.has('p1')).toBe(true);
204
+ expect(result2).toBe(result1);
205
+ });
206
+ it('re-reads after TTL expires', () => {
207
+ vi.useFakeTimers();
208
+ const reviews = [makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z')];
209
+ writeReviews(reviews);
210
+ const result1 = getCachedMaskedPrincipleSet(tmpDir, 1000);
211
+ expect(result1.has('p1')).toBe(true);
212
+ const updatedReviews = [makeReview('p1', 'keep', '2026-07-02T00:00:00.000Z')];
213
+ writeReviews(updatedReviews);
214
+ vi.advanceTimersByTime(500);
215
+ const result2 = getCachedMaskedPrincipleSet(tmpDir, 1000);
216
+ expect(result2.has('p1')).toBe(true);
217
+ vi.advanceTimersByTime(600);
218
+ const result3 = getCachedMaskedPrincipleSet(tmpDir, 1000);
219
+ expect(result3.has('p1')).toBe(false);
220
+ vi.useRealTimers();
221
+ });
222
+ it('cache is keyed by workspaceDir', () => {
223
+ const otherDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-pruning-mask-other-'));
224
+ const otherStateDir = path.join(otherDir, '.state');
225
+ fs.mkdirSync(otherStateDir, { recursive: true });
226
+ const otherLogPath = path.join(otherStateDir, 'pruning_reviews.jsonl');
227
+ try {
228
+ const reviews1 = [makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z')];
229
+ writeReviews(reviews1);
230
+ const reviews2 = [makeReview('p1', 'keep', '2026-07-01T00:00:00.000Z')];
231
+ fs.writeFileSync(otherLogPath, reviews2.map(r => JSON.stringify(r)).join('\n') + '\n', 'utf-8');
232
+ const result1 = getCachedMaskedPrincipleSet(tmpDir);
233
+ const result2 = getCachedMaskedPrincipleSet(otherDir);
234
+ expect(result1.has('p1')).toBe(true);
235
+ expect(result2.has('p1')).toBe(false);
236
+ }
237
+ finally {
238
+ fs.rmSync(otherDir, { recursive: true, force: true });
239
+ }
240
+ });
241
+ it('returns empty set when no pruning_reviews.jsonl exists', () => {
242
+ fs.rmSync(logPath, { force: true });
243
+ const result = getCachedMaskedPrincipleSet(tmpDir);
244
+ expect(result.size).toBe(0);
245
+ });
246
+ });
247
+ describe('clearPruningMaskCache', () => {
248
+ let tmpDir;
249
+ let stateDir;
250
+ let logPath;
251
+ beforeEach(() => {
252
+ clearPruningMaskCache();
253
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-pruning-mask-clear-'));
254
+ stateDir = path.join(tmpDir, '.state');
255
+ fs.mkdirSync(stateDir, { recursive: true });
256
+ logPath = path.join(stateDir, 'pruning_reviews.jsonl');
257
+ });
258
+ afterEach(() => {
259
+ fs.rmSync(tmpDir, { recursive: true, force: true });
260
+ });
261
+ function writeReviews(reviews) {
262
+ const lines = reviews.map(r => JSON.stringify(r));
263
+ fs.writeFileSync(logPath, lines.join('\n') + '\n', 'utf-8');
264
+ }
265
+ it('clears cache so next call re-reads from disk', () => {
266
+ const reviews = [makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z')];
267
+ writeReviews(reviews);
268
+ const result1 = getCachedMaskedPrincipleSet(tmpDir);
269
+ expect(result1.has('p1')).toBe(true);
270
+ const updatedReviews = [makeReview('p1', 'keep', '2026-07-02T00:00:00.000Z')];
271
+ writeReviews(updatedReviews);
272
+ clearPruningMaskCache();
273
+ const result2 = getCachedMaskedPrincipleSet(tmpDir);
274
+ expect(result2.has('p1')).toBe(false);
275
+ });
276
+ it('is safe to call when cache is already empty', () => {
277
+ expect(() => clearPruningMaskCache()).not.toThrow();
278
+ expect(() => clearPruningMaskCache()).not.toThrow();
279
+ });
280
+ });
281
+ //# sourceMappingURL=pruning-mask.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pruning-mask.test.js","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/pruning-mask.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * WorkflowFunnelLoader Tests — Core Package
3
+ *
4
+ * Direct unit tests for the WorkflowFunnelLoader class.
5
+ *
6
+ * Tests verify:
7
+ * - Constructor loads workflows.yaml from stateDir
8
+ * - Missing file → empty funnels + warning
9
+ * - Malformed YAML → preserves last known-good config + warning
10
+ * - Invalid schema (missing version/funnels) → preserves last known-good + warning
11
+ * - Invalid funnel entries (missing workflowId/stages) → skipped + warning
12
+ * - getStages returns correct stages for known workflow
13
+ * - getStages returns empty array for unknown workflow
14
+ * - getFunnel returns full funnel with policy for known workflow
15
+ * - getFunnel returns undefined for unknown workflow
16
+ * - getFunnel returns deep clone (mutations don't affect internal state)
17
+ * - getAllFunnels returns deep clone of all stages
18
+ * - getAllFunnelsWithPolicy returns deep clone of all funnels
19
+ * - watch() starts file watching, dispose() cleans up
20
+ * - getWarnings returns warnings from last load
21
+ * - getConfigPath returns correct path
22
+ *
23
+ * ERR checklist:
24
+ * - ERR-002 / EP-03: malformed YAML preserves last known-good (fail-safe)
25
+ * - ERR-007 / EP-02: single source for funnels table
26
+ * - EP-07: deep clones prevent consumer mutation of internal state
27
+ */
28
+ export {};
29
+ //# sourceMappingURL=workflow-funnel-loader.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-funnel-loader.test.d.ts","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/workflow-funnel-loader.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG"}