@principles/core 1.240.20 → 1.241.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 (103) hide show
  1. package/dist/runtime-v2/diagnostician/__tests__/diag-distiller-output.test.js +46 -0
  2. package/dist/runtime-v2/diagnostician/__tests__/diag-distiller-output.test.js.map +1 -1
  3. package/dist/runtime-v2/diagnostician/diag-distiller-output.d.ts +2 -0
  4. package/dist/runtime-v2/diagnostician/diag-distiller-output.d.ts.map +1 -1
  5. package/dist/runtime-v2/diagnostician/diag-distiller-output.js +29 -2
  6. package/dist/runtime-v2/diagnostician/diag-distiller-output.js.map +1 -1
  7. package/dist/runtime-v2/feature-flags/__tests__/artifact-summary-redundancy-flag.test.d.ts +15 -0
  8. package/dist/runtime-v2/feature-flags/__tests__/artifact-summary-redundancy-flag.test.d.ts.map +1 -0
  9. package/dist/runtime-v2/feature-flags/__tests__/artifact-summary-redundancy-flag.test.js +90 -0
  10. package/dist/runtime-v2/feature-flags/__tests__/artifact-summary-redundancy-flag.test.js.map +1 -0
  11. package/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  12. package/dist/runtime-v2/feature-flags/feature-flag-contract.js +6 -0
  13. package/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  14. package/dist/runtime-v2/internalization/__tests__/artifact-content-hash.property.test.d.ts +11 -0
  15. package/dist/runtime-v2/internalization/__tests__/artifact-content-hash.property.test.d.ts.map +1 -0
  16. package/dist/runtime-v2/internalization/__tests__/artifact-content-hash.property.test.js +124 -0
  17. package/dist/runtime-v2/internalization/__tests__/artifact-content-hash.property.test.js.map +1 -0
  18. package/dist/runtime-v2/internalization/__tests__/artifact-summary.property.test.d.ts +12 -0
  19. package/dist/runtime-v2/internalization/__tests__/artifact-summary.property.test.d.ts.map +1 -0
  20. package/dist/runtime-v2/internalization/__tests__/artifact-summary.property.test.js +253 -0
  21. package/dist/runtime-v2/internalization/__tests__/artifact-summary.property.test.js.map +1 -0
  22. package/dist/runtime-v2/internalization/__tests__/philosopher-toggle-characterization.test.d.ts +2 -0
  23. package/dist/runtime-v2/internalization/__tests__/philosopher-toggle-characterization.test.d.ts.map +1 -0
  24. package/dist/runtime-v2/internalization/__tests__/philosopher-toggle-characterization.test.js +279 -0
  25. package/dist/runtime-v2/internalization/__tests__/philosopher-toggle-characterization.test.js.map +1 -0
  26. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-scope-regression.test.d.ts +22 -0
  27. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-scope-regression.test.d.ts.map +1 -0
  28. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-scope-regression.test.js +92 -0
  29. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-scope-regression.test.js.map +1 -0
  30. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike-fixtures.d.ts +165 -0
  31. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike-fixtures.d.ts.map +1 -0
  32. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike-fixtures.js +657 -0
  33. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike-fixtures.js.map +1 -0
  34. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike-fixtures.test.d.ts +22 -0
  35. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike-fixtures.test.d.ts.map +1 -0
  36. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike-fixtures.test.js +229 -0
  37. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike-fixtures.test.js.map +1 -0
  38. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike.test.d.ts +2 -0
  39. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike.test.d.ts.map +1 -0
  40. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike.test.js +1039 -0
  41. package/dist/runtime-v2/internalization/__tests__/progressive-disclosure-spike.test.js.map +1 -0
  42. package/dist/runtime-v2/internalization/__tests__/summary-chain-flow.property.test.d.ts +22 -0
  43. package/dist/runtime-v2/internalization/__tests__/summary-chain-flow.property.test.d.ts.map +1 -0
  44. package/dist/runtime-v2/internalization/__tests__/summary-chain-flow.property.test.js +280 -0
  45. package/dist/runtime-v2/internalization/__tests__/summary-chain-flow.property.test.js.map +1 -0
  46. package/dist/runtime-v2/internalization/__tests__/summary-envelope-writer.property.test.d.ts +25 -0
  47. package/dist/runtime-v2/internalization/__tests__/summary-envelope-writer.property.test.d.ts.map +1 -0
  48. package/dist/runtime-v2/internalization/__tests__/summary-envelope-writer.property.test.js +413 -0
  49. package/dist/runtime-v2/internalization/__tests__/summary-envelope-writer.property.test.js.map +1 -0
  50. package/dist/runtime-v2/internalization/__tests__/summary-freshness.property.test.d.ts +12 -0
  51. package/dist/runtime-v2/internalization/__tests__/summary-freshness.property.test.d.ts.map +1 -0
  52. package/dist/runtime-v2/internalization/__tests__/summary-freshness.property.test.js +176 -0
  53. package/dist/runtime-v2/internalization/__tests__/summary-freshness.property.test.js.map +1 -0
  54. package/dist/runtime-v2/internalization/artifact-content-hash.d.ts +19 -0
  55. package/dist/runtime-v2/internalization/artifact-content-hash.d.ts.map +1 -0
  56. package/dist/runtime-v2/internalization/artifact-content-hash.js +116 -0
  57. package/dist/runtime-v2/internalization/artifact-content-hash.js.map +1 -0
  58. package/dist/runtime-v2/internalization/artifact-summary.d.ts +106 -0
  59. package/dist/runtime-v2/internalization/artifact-summary.d.ts.map +1 -0
  60. package/dist/runtime-v2/internalization/artifact-summary.js +331 -0
  61. package/dist/runtime-v2/internalization/artifact-summary.js.map +1 -0
  62. package/dist/runtime-v2/internalization/artificer-runner.d.ts.map +1 -1
  63. package/dist/runtime-v2/internalization/artificer-runner.js +29 -1
  64. package/dist/runtime-v2/internalization/artificer-runner.js.map +1 -1
  65. package/dist/runtime-v2/internalization/attach-summary-envelope.d.ts +60 -0
  66. package/dist/runtime-v2/internalization/attach-summary-envelope.d.ts.map +1 -0
  67. package/dist/runtime-v2/internalization/attach-summary-envelope.js +126 -0
  68. package/dist/runtime-v2/internalization/attach-summary-envelope.js.map +1 -0
  69. package/dist/runtime-v2/internalization/diag-distiller-runner.d.ts +1 -1
  70. package/dist/runtime-v2/internalization/diag-distiller-runner.d.ts.map +1 -1
  71. package/dist/runtime-v2/internalization/diag-distiller-runner.js +20 -2
  72. package/dist/runtime-v2/internalization/diag-distiller-runner.js.map +1 -1
  73. package/dist/runtime-v2/internalization/diag-rootcause-runner.d.ts.map +1 -1
  74. package/dist/runtime-v2/internalization/diag-rootcause-runner.js +6 -1
  75. package/dist/runtime-v2/internalization/diag-rootcause-runner.js.map +1 -1
  76. package/dist/runtime-v2/internalization/diag-router-runner.d.ts +1 -1
  77. package/dist/runtime-v2/internalization/diag-router-runner.d.ts.map +1 -1
  78. package/dist/runtime-v2/internalization/diag-router-runner.js +25 -1
  79. package/dist/runtime-v2/internalization/diag-router-runner.js.map +1 -1
  80. package/dist/runtime-v2/internalization/dreamer-runner.d.ts +9 -1
  81. package/dist/runtime-v2/internalization/dreamer-runner.d.ts.map +1 -1
  82. package/dist/runtime-v2/internalization/dreamer-runner.js +28 -6
  83. package/dist/runtime-v2/internalization/dreamer-runner.js.map +1 -1
  84. package/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  85. package/dist/runtime-v2/internalization/evaluator-runner.js +35 -2
  86. package/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  87. package/dist/runtime-v2/internalization/philosopher-runner.d.ts.map +1 -1
  88. package/dist/runtime-v2/internalization/philosopher-runner.js +25 -1
  89. package/dist/runtime-v2/internalization/philosopher-runner.js.map +1 -1
  90. package/dist/runtime-v2/internalization/scribe-runner.d.ts.map +1 -1
  91. package/dist/runtime-v2/internalization/scribe-runner.js +23 -1
  92. package/dist/runtime-v2/internalization/scribe-runner.js.map +1 -1
  93. package/dist/runtime-v2/runner/base-peer-runner.d.ts +30 -0
  94. package/dist/runtime-v2/runner/base-peer-runner.d.ts.map +1 -1
  95. package/dist/runtime-v2/runner/base-peer-runner.js +106 -0
  96. package/dist/runtime-v2/runner/base-peer-runner.js.map +1 -1
  97. package/dist/runtime-v2/runner/peer-runner-types.d.ts +19 -0
  98. package/dist/runtime-v2/runner/peer-runner-types.d.ts.map +1 -1
  99. package/dist/runtime-v2/store/commit/diagnostician-committer.d.ts +15 -0
  100. package/dist/runtime-v2/store/commit/diagnostician-committer.d.ts.map +1 -1
  101. package/dist/runtime-v2/store/commit/diagnostician-committer.js +19 -1
  102. package/dist/runtime-v2/store/commit/diagnostician-committer.js.map +1 -1
  103. package/package.json +2 -1
@@ -0,0 +1,413 @@
1
+ /**
2
+ * Layer 0 writer-side property tests (design §6.1, tasks 3.12–3.20).
3
+ *
4
+ * These exercise the *wiring* layer (`BasePeerRunner.buildArtifactContentJson`,
5
+ * which calls `attachSummaryEnvelope`), not the pure derivation functions
6
+ * (those are covered by `artifact-summary.property.test.ts`). The wiring layer
7
+ * is where the feature flag, the predecessor-contentHash injection, the
8
+ * degradation telemetry, and the "summary failure must not affect task success"
9
+ * contract (F11) all live.
10
+ *
11
+ * CP-04: derivation failure does not affect task success / failure
12
+ * CP-05: predecessor summary adds zero extra store reads & is same-source
13
+ * CP-06: envelope depth is always exactly one (no nested predecessorSummary)
14
+ * CP-11: stale summary content never enters the written contentJson
15
+ * CP-32: contentJson key-level compatibility (flag on vs off, byte-identical
16
+ * aside from summary/predecessorSummary)
17
+ * CP-35: direct-predecessor uniqueness (evaluator loads artificer+scribe,
18
+ * diag_router loads rootcause+distiller — only the edge predecessor
19
+ * lands in predecessorSummary)
20
+ *
21
+ * @see .kiro/specs/internalization-progressive-disclosure/design.md §6.1, §16
22
+ * @see .kiro/specs/internalization-progressive-disclosure/requirements.md Requirements 1.8, 2, 3.6, 11.5–11.9
23
+ */
24
+ import { describe, it, expect, vi } from 'vitest';
25
+ import { createHash } from 'node:crypto';
26
+ import fc from 'fast-check';
27
+ import { BasePeerRunner } from '../../runner/base-peer-runner.js';
28
+ import { getDefaultPdConfig } from '../../config/pd-config-defaults.js';
29
+ import { computeEffectivePdConfig } from '../../config/pd-config-effective.js';
30
+ import { computeContentHash } from '../artifact-content-hash.js';
31
+ const sha256 = (input) => createHash('sha256').update(input).digest('hex');
32
+ const TASK_ID = 'task-layer0-writer-001';
33
+ /**
34
+ * Build an EffectivePdConfig with the Layer 0 flag on or off. Uses the real
35
+ * production config-merge path (`computeEffectivePdConfig`) rather than
36
+ * hand-rolling the EffectivePdConfig shape — this is the same path the
37
+ * production loader uses (ERR-024: test the real wiring).
38
+ */
39
+ function makeEffectiveConfig(opts) {
40
+ if (!opts.summaryEnabled) {
41
+ return computeEffectivePdConfig(null);
42
+ }
43
+ const base = getDefaultPdConfig();
44
+ const userConfig = {
45
+ ...base,
46
+ features: {
47
+ ...base.features,
48
+ artifact_summary_redundancy: { category: 'quiet', enabled: true },
49
+ },
50
+ };
51
+ return computeEffectivePdConfig(userConfig);
52
+ }
53
+ /**
54
+ * Test runner exposing the protected `buildArtifactContentJson` directly.
55
+ * Mirrors the DegradationTestRunner pattern from
56
+ * base-peer-runner-rate-limit-degradation.test.ts.
57
+ */
58
+ class Layer0TestRunner extends BasePeerRunner {
59
+ constructor(deps, effectiveConfig) {
60
+ super(deps, { owner: 'test', runtimeKind: 'test-double' }, {
61
+ runnerName: 'test',
62
+ expectedTaskKind: 'dreamer',
63
+ defaultAgentId: 'test',
64
+ resultRefPrefix: 'test',
65
+ effectiveConfig,
66
+ });
67
+ }
68
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
69
+ get permanentErrorCategories() {
70
+ return new Set(['input_invalid', 'output_invalid']);
71
+ }
72
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
73
+ async buildContext() {
74
+ return { contextHash: 'test-hash' };
75
+ }
76
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
77
+ async invokeRuntime() {
78
+ return { runId: 'run-001', runtimeKind: 'test-double', startedAt: new Date().toISOString() };
79
+ }
80
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
81
+ async validateOutput() {
82
+ return { valid: true, errors: [] };
83
+ }
84
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this, @typescript-eslint/max-params
85
+ async succeedTask(taskId, runId, _output, task) {
86
+ return { status: 'succeeded', taskId, runId, attemptCount: task.attemptCount };
87
+ }
88
+ // eslint-disable-next-line @typescript-eslint/max-params -- mirrors the protected method under test 1:1.
89
+ callBuildArtifactContentJson(taskId, runnerKind, output, loadedPredecessor) {
90
+ return this.buildArtifactContentJson(taskId, runnerKind, output, loadedPredecessor);
91
+ }
92
+ /** Expose the flag check for direct assertion. */
93
+ callIsArtifactSummaryEnabled() {
94
+ return this.isArtifactSummaryEnabled();
95
+ }
96
+ }
97
+ function createMockDeps(overrides) {
98
+ return {
99
+ stateManager: {
100
+ getRetryPolicy: vi.fn().mockReturnValue({ shouldRetry: vi.fn().mockReturnValue(false) }),
101
+ markTaskFailed: vi.fn().mockResolvedValue({}),
102
+ markTaskSucceeded: vi.fn().mockResolvedValue({}),
103
+ updateRunOutput: vi.fn().mockResolvedValue({}),
104
+ },
105
+ runtimeAdapter: {},
106
+ eventEmitter: {
107
+ emitTelemetry: vi.fn(),
108
+ },
109
+ artifactStore: {
110
+ // Counting stub: CP-05 proves zero extra reads. Every method the writer
111
+ // path could reach is a spy that throws if called unexpectedly.
112
+ getArtifactById: vi.fn(),
113
+ listBySourceTaskId: vi.fn(),
114
+ },
115
+ contentHashFn: sha256,
116
+ ...overrides,
117
+ };
118
+ }
119
+ function telemetryCalls(mock) {
120
+ return mock.mock.calls.map((call) => call[0]);
121
+ }
122
+ /**
123
+ * `emitEvent` prefixes eventType with the runner name (e.g. `test_...`).
124
+ * This helper matches on the suffix so tests stay runner-name agnostic.
125
+ */
126
+ function telemetryWithSuffix(mock, suffix) {
127
+ return telemetryCalls(mock).filter((c) => c.eventType.endsWith(`_${suffix}`));
128
+ }
129
+ /** A legal dreamer-shaped output for the wiring tests. */
130
+ const DREAMER_OUTPUT = {
131
+ candidates: [{
132
+ badDecision: 'renamed without checking callers',
133
+ betterDecision: 'audit file tree and grep imports before rename',
134
+ rationale: 'cross-package callers break compilation',
135
+ riskLevel: 'high',
136
+ strategicPerspective: 'caller-side lens',
137
+ }],
138
+ };
139
+ function makePredecessor(runnerKind, content) {
140
+ return {
141
+ artifactId: `pred-${runnerKind}`,
142
+ runnerKind,
143
+ contentJson: content,
144
+ };
145
+ }
146
+ // ── CP-32: contentJson key-level compatibility ───────────────────────────────
147
+ describe('Layer 0 — CP-32 contentJson key-level compatibility', () => {
148
+ it('flag OFF → contentJson is byte-identical to JSON.stringify(output)', () => {
149
+ const deps = createMockDeps();
150
+ const runner = new Layer0TestRunner(deps); // no effectiveConfig → flag off
151
+ expect(runner.callIsArtifactSummaryEnabled()).toBe(false);
152
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, null);
153
+ expect(result).toBe(JSON.stringify(DREAMER_OUTPUT));
154
+ // And the same when a predecessor is provided: flag off = no envelope at all.
155
+ const resultWithPred = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, makePredecessor('diag_router', { summary: 'router output' }));
156
+ expect(resultWithPred).toBe(JSON.stringify(DREAMER_OUTPUT));
157
+ });
158
+ it('flag ON → every original output key is preserved (only summary/predecessorSummary added)', () => {
159
+ const deps = createMockDeps();
160
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
161
+ expect(runner.callIsArtifactSummaryEnabled()).toBe(true);
162
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, makePredecessor('diag_router', { summary: 'router output' }));
163
+ const parsed = JSON.parse(result);
164
+ // Every original key from DREAMER_OUTPUT is present and unchanged.
165
+ const original = DREAMER_OUTPUT;
166
+ for (const key of Object.keys(original)) {
167
+ expect(parsed[key]).toEqual(original[key]);
168
+ }
169
+ // The two additive envelope keys are present.
170
+ expect(Object.hasOwn(parsed, 'summary')).toBe(true);
171
+ expect(Object.hasOwn(parsed, 'predecessorSummary')).toBe(true);
172
+ });
173
+ it('flag ON with no predecessor → only `summary` added, no `predecessorSummary` key', () => {
174
+ const deps = createMockDeps();
175
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
176
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, null);
177
+ const parsed = JSON.parse(result);
178
+ expect(Object.hasOwn(parsed, 'summary')).toBe(true);
179
+ expect(Object.hasOwn(parsed, 'predecessorSummary')).toBe(false);
180
+ });
181
+ it('P1 regression: output already declaring a top-level `summary` field (diag_rootcause / diag_router) is NOT overwritten', () => {
182
+ // CodeRabbit PR #1273: DiagRootCauseOutputV1 and DiagnosticianOutputV1 both
183
+ // declare a top-level `summary` string field. The Layer 0 envelope must not
184
+ // clobber it. On collision, the envelope `summary` key is skipped, a
185
+ // structured degradation is emitted, and predecessorSummary is still
186
+ // attached (it does not collide).
187
+ const deps = createMockDeps();
188
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
189
+ const emitMock = deps.eventEmitter.emitTelemetry;
190
+ // diag_router-shaped output carries a legitimate top-level `summary` field.
191
+ const routerOutput = {
192
+ summary: 'route to internalization: principle guard', // legitimate output field
193
+ rootCause: 'rename without enumeration',
194
+ violatedPrinciples: [],
195
+ recommendations: [{ kind: 'principle', description: 'guard', triggerPattern: 'x', action: 'y' }],
196
+ };
197
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'diag_router', routerOutput, makePredecessor('diag_distiller', { abstractedPrinciple: 'p', rationale: 'r', scope: 'general' }));
198
+ const parsed = JSON.parse(result);
199
+ // The legitimate output.summary is preserved verbatim (NOT overwritten by
200
+ // the ArtifactSummary envelope object).
201
+ expect(parsed.summary).toBe('route to internalization: principle guard');
202
+ expect(typeof parsed.summary).toBe('string');
203
+ // predecessorSummary is still attached (no collision).
204
+ expect(Object.hasOwn(parsed, 'predecessorSummary')).toBe(true);
205
+ expect(parsed.predecessorSummary.runnerKind).toBe('diag_distiller');
206
+ // A structured degradation was emitted for the collision (rc-9).
207
+ const skipped = telemetryWithSuffix(emitMock, 'artifact_summary_skipped');
208
+ expect(skipped.length).toBe(1);
209
+ expect(skipped[0]?.payload?.reason).toBe('output_summary_key_collision');
210
+ });
211
+ });
212
+ // ── CP-04: derivation failure does not affect task success ───────────────────
213
+ describe('Layer 0 — CP-04 derivation failure does not affect task success', () => {
214
+ it('returns a valid contentJson (no throw) and emits artifact_summary_skipped when output has no derivable field', () => {
215
+ const deps = createMockDeps();
216
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
217
+ const emitMock = deps.eventEmitter.emitTelemetry;
218
+ // An object with none of the fields any resolver looks for → no_derivable_field.
219
+ const bare = { unrelated: 'value' };
220
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', bare, null);
221
+ // No throw, valid JSON, and falls back to the bare output (no summary key).
222
+ expect(() => JSON.parse(result)).not.toThrow();
223
+ const parsed = JSON.parse(result);
224
+ expect(Object.hasOwn(parsed, 'summary')).toBe(false);
225
+ // Exactly one artifact_summary_skipped degradation event emitted (rc-9).
226
+ const skipped = telemetryWithSuffix(emitMock, 'artifact_summary_skipped');
227
+ expect(skipped.length).toBe(1);
228
+ expect(skipped[0]?.payload?.reason).toBe('no_derivable_field');
229
+ });
230
+ it('output_not_object (e.g. a string) → no summary, no throw', () => {
231
+ const deps = createMockDeps();
232
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
233
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', 'not-an-object', null);
234
+ // Falls back to JSON.stringify of the input; no summary.
235
+ expect(result).toBe(JSON.stringify('not-an-object'));
236
+ });
237
+ });
238
+ // ── CP-06: envelope depth is always exactly one ──────────────────────────────
239
+ describe('Layer 0 — CP-06 envelope depth is always exactly one', () => {
240
+ it('predecessorSummary never carries its own predecessorSummary key', () => {
241
+ const deps = createMockDeps();
242
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
243
+ // Even when the predecessor's own contentJson already contains a nested
244
+ // predecessorSummary (as if it were itself a mid-chain node), the freshly
245
+ // attached predecessorSummary must NOT propagate that nested ref — depth is
246
+ // capped at one (design §4.6 修正六).
247
+ const pred = makePredecessor('diag_router', {
248
+ summary: 'router output',
249
+ predecessorSummary: { artifactId: 'should-not-propagate', runnerKind: 'diag_distiller' },
250
+ });
251
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, pred);
252
+ const parsed = JSON.parse(result);
253
+ const predSummary = parsed.predecessorSummary;
254
+ expect(predSummary).toBeDefined();
255
+ // The only keys on predecessorSummary are artifactId/runnerKind/contentHash/summary.
256
+ const allowed = new Set(['artifactId', 'runnerKind', 'contentHash', 'summary']);
257
+ for (const key of Object.keys(predSummary)) {
258
+ expect(allowed.has(key)).toBe(true);
259
+ }
260
+ // And critically: no nested predecessorSummary.predecessorSummary.
261
+ const nested = predSummary.summary;
262
+ expect(Object.hasOwn(nested, 'predecessorSummary')).toBe(false);
263
+ });
264
+ it('a synthetic 1..6-hop chain never produces nested predecessorSummary at any hop', () => {
265
+ fc.assert(fc.property(fc.integer({ min: 1, max: 6 }), (hops) => {
266
+ const deps = createMockDeps();
267
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
268
+ // Build hops sequentially, feeding each hop's written contentJson as
269
+ // the next hop's predecessor content.
270
+ const kinds = ['diag_rootcause', 'diag_distiller', 'diag_router', 'dreamer', 'philosopher', 'scribe'];
271
+ let prevContent = { rootCause: 'seed root cause' };
272
+ let prevKind = 'diag_rootcause';
273
+ for (let i = 0; i < hops; i++) {
274
+ const kind = kinds[Math.min(i, kinds.length - 1)] ?? 'scribe';
275
+ const predecessor = i === 0
276
+ ? null
277
+ : { artifactId: `art-${i}`, runnerKind: prevKind, contentJson: prevContent };
278
+ const out = { candidates: [{ betterDecision: 'hop action', riskLevel: 'low' }] };
279
+ const written = runner.callBuildArtifactContentJson(`task-${i}`, kind, out, predecessor);
280
+ const parsed = JSON.parse(written);
281
+ // Check this hop's predecessorSummary (if present) has no nesting.
282
+ const pred = parsed.predecessorSummary;
283
+ if (pred) {
284
+ const allowed = new Set(['artifactId', 'runnerKind', 'contentHash', 'summary']);
285
+ for (const key of Object.keys(pred))
286
+ expect(allowed.has(key)).toBe(true);
287
+ const inner = pred.summary;
288
+ expect(Object.hasOwn(inner, 'predecessorSummary')).toBe(false);
289
+ }
290
+ prevContent = parsed;
291
+ prevKind = kind;
292
+ }
293
+ }), { numRuns: 12 });
294
+ });
295
+ });
296
+ // ── CP-05: predecessor summary adds zero extra store reads & is same-source ──
297
+ describe('Layer 0 — CP-05 predecessor summary zero extra reads & same-source', () => {
298
+ it('flag ON with predecessor → no artifactStore.getArtifactById/listBySourceTaskId calls', () => {
299
+ const deps = createMockDeps();
300
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
301
+ const getMock = deps.artifactStore.getArtifactById;
302
+ const listMock = deps.artifactStore.listBySourceTaskId;
303
+ runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, makePredecessor('diag_router', { summary: 'router output' }));
304
+ expect(getMock).not.toHaveBeenCalled();
305
+ expect(listMock).not.toHaveBeenCalled();
306
+ });
307
+ it('predecessorSummary artifactId / runnerKind / contentHash are all same-source (rc-6)', () => {
308
+ const deps = createMockDeps();
309
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
310
+ const predContent = { summary: 'router output', rootCause: 'rc' };
311
+ const pred = {
312
+ artifactId: 'pred-art-999',
313
+ runnerKind: 'diag_router',
314
+ contentJson: predContent,
315
+ };
316
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, pred);
317
+ const parsed = JSON.parse(result);
318
+ const predSummary = parsed.predecessorSummary;
319
+ // All three fields come from the same predecessor object.
320
+ expect(predSummary.artifactId).toBe('pred-art-999');
321
+ expect(predSummary.runnerKind).toBe('diag_router');
322
+ // contentHash is the canonical (key-sorted) serialization of the predecessor content.
323
+ expect(predSummary.contentHash).toBe(computeContentHash(predContent, sha256));
324
+ });
325
+ it('null predecessor → artifact_summary_predecessor_absent emitted, self summary still written', () => {
326
+ const deps = createMockDeps();
327
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
328
+ const emitMock = deps.eventEmitter.emitTelemetry;
329
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, null);
330
+ const parsed = JSON.parse(result);
331
+ expect(Object.hasOwn(parsed, 'summary')).toBe(true);
332
+ expect(Object.hasOwn(parsed, 'predecessorSummary')).toBe(false);
333
+ const absent = telemetryWithSuffix(emitMock, 'artifact_summary_predecessor_absent');
334
+ expect(absent.length).toBe(1);
335
+ expect(absent[0]?.payload?.reason).toBe('no_predecessor_in_context');
336
+ });
337
+ });
338
+ // ── CP-11: stale summary content never enters the written contentJson ────────
339
+ //
340
+ // Note: CP-11's core invariant (stale predecessorSummary content is excluded
341
+ // from the injected context) is enforced by the READ-side freshness check
342
+ // (`checkPredecessorSummaryFreshness`, covered by summary-freshness.property
343
+ // .test.ts CP-09). The WRITE side tested here guarantees the contentHash is
344
+ // computed correctly so the read side can detect staleness. This test verifies
345
+ // the contentHash is stable for a given content, so a content change is always
346
+ // detectable.
347
+ describe('Layer 0 — CP-11 stale summary detectability (contentHash stability on write)', () => {
348
+ it('contentHash changes iff predecessor content changes (so stale refs are always detectable)', () => {
349
+ const deps = createMockDeps();
350
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
351
+ const contentA = { summary: 'router output v1' };
352
+ const contentB = { summary: 'router output v2' };
353
+ const r1 = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, makePredecessor('diag_router', contentA));
354
+ const r2 = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, makePredecessor('diag_router', contentA));
355
+ const r3 = runner.callBuildArtifactContentJson(TASK_ID, 'dreamer', DREAMER_OUTPUT, makePredecessor('diag_router', contentB));
356
+ const hashOf = (s) => {
357
+ const parsed = JSON.parse(s);
358
+ const pred = parsed.predecessorSummary;
359
+ return pred.contentHash;
360
+ };
361
+ // Same content → same hash (deterministic).
362
+ expect(hashOf(r1)).toBe(hashOf(r2));
363
+ // Different content → different hash (staleness is detectable).
364
+ expect(hashOf(r1)).not.toBe(hashOf(r3));
365
+ });
366
+ });
367
+ // ── CP-35: direct-predecessor uniqueness ──────────────────────────────────────
368
+ //
369
+ // F17: some runners load TWO artifacts in the same context (evaluator loads
370
+ // artificer+scribe; diag_router loads rootcause+distiller). Only the EDGE
371
+ // predecessor may land in predecessorSummary. The wiring helper
372
+ // (attachSummaryEnvelope) only ever receives ONE predecessor object — the
373
+ // caller (runner) is responsible for passing the edge one. This test proves the
374
+ // single-predecessor contract holds regardless of what else is in memory.
375
+ describe('Layer 0 — CP-35 direct-predecessor uniqueness (single predecessor per write)', () => {
376
+ it('each call writes at most one predecessorSummary — the object the caller passed', () => {
377
+ const deps = createMockDeps();
378
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
379
+ // Simulate evaluator's multi-artifact context: it loaded BOTH artificer
380
+ // (edge predecessor) AND scribe (non-edge). Only artificer may go into
381
+ // predecessorSummary. The wiring contract: the runner passes ONLY the edge
382
+ // predecessor; scribe flows through its separate path.
383
+ const artificerContent = { affectedTools: ['t1'], implementationSummary: 'impl', sourceTrace: { scribeArtifactId: 'scribe-art-1' } };
384
+ const scribeContent = { principleDraft: { statement: 'principle' } };
385
+ // Evaluator passes ONLY artificer as loadedPredecessor (edge predecessor).
386
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'evaluator', { evaluation: { decision: 'approved', concerns: [] }, codeReview: { intentConsistency: { aligned: true } } }, makePredecessor('artificer', artificerContent));
387
+ const parsed = JSON.parse(result);
388
+ const predSummary = parsed.predecessorSummary;
389
+ // Exactly one predecessorSummary, and it's the artificer (edge).
390
+ expect(predSummary).toBeDefined();
391
+ expect(predSummary.runnerKind).toBe('artificer');
392
+ expect(predSummary.artifactId).toBe('pred-artificer');
393
+ // The scribe content (a sentinel string) must NOT appear anywhere in the
394
+ // written contentJson — it was never passed in.
395
+ expect(result).not.toContain(JSON.stringify(scribeContent.principleDraft));
396
+ });
397
+ it('passing the non-edge predecessor (scribe) is the caller\'s contract violation, but the helper still writes exactly one', () => {
398
+ // This documents the contract boundary: attachSummaryEnvelope trusts the
399
+ // caller to pass the edge predecessor. If a caller passes the wrong one,
400
+ // the helper still writes exactly ONE predecessorSummary — it never
401
+ // merges multiple. This is the invariant CP-35 guards.
402
+ const deps = createMockDeps();
403
+ const runner = new Layer0TestRunner(deps, makeEffectiveConfig({ summaryEnabled: true }));
404
+ const result = runner.callBuildArtifactContentJson(TASK_ID, 'evaluator', { evaluation: { decision: 'approved', concerns: [] } }, makePredecessor('scribe', { principleDraft: { statement: 'p' } }));
405
+ const parsed = JSON.parse(result);
406
+ const predSummary = parsed.predecessorSummary;
407
+ expect(predSummary).toBeDefined();
408
+ // Exactly one — no array, no merge.
409
+ expect(typeof predSummary).toBe('object');
410
+ expect(Array.isArray(predSummary)).toBe(false);
411
+ });
412
+ });
413
+ //# sourceMappingURL=summary-envelope-writer.property.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary-envelope-writer.property.test.js","sourceRoot":"","sources":["../../../../src/runtime-v2/internalization/__tests__/summary-envelope-writer.property.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Property tests for `checkPredecessorSummaryFreshness` (design §6.1, tasks
3
+ * 3.9–3.10).
4
+ *
5
+ * CP-09: freshness ⟺ content hash equality
6
+ * CP-10: updatedAt does not participate in freshness determination
7
+ *
8
+ * @see .kiro/specs/internalization-progressive-disclosure/design.md §6.1, §16
9
+ * @see .kiro/specs/internalization-progressive-disclosure/requirements.md Requirement 3
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=summary-freshness.property.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary-freshness.property.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/internalization/__tests__/summary-freshness.property.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Property tests for `checkPredecessorSummaryFreshness` (design §6.1, tasks
3
+ * 3.9–3.10).
4
+ *
5
+ * CP-09: freshness ⟺ content hash equality
6
+ * CP-10: updatedAt does not participate in freshness determination
7
+ *
8
+ * @see .kiro/specs/internalization-progressive-disclosure/design.md §6.1, §16
9
+ * @see .kiro/specs/internalization-progressive-disclosure/requirements.md Requirement 3
10
+ */
11
+ import { describe, it, expect } from 'vitest';
12
+ import fc from 'fast-check';
13
+ import { createHash } from 'node:crypto';
14
+ import { computeContentHash } from '../artifact-content-hash.js';
15
+ import { checkPredecessorSummaryFreshness, } from '../artifact-summary.js';
16
+ const sha256 = (input) => createHash('sha256').update(input).digest('hex');
17
+ /** Counting store stub — proves zero artifact-store reads happen inside the freshness check. */
18
+ class CountingStoreStub {
19
+ reads = 0;
20
+ getArtifactById() {
21
+ this.reads += 1;
22
+ throw new Error('checkPredecessorSummaryFreshness must not read the artifact store');
23
+ }
24
+ }
25
+ function makeSummary() {
26
+ return {
27
+ schemaVersion: 1,
28
+ runnerKind: 'scribe',
29
+ headline: 'headline',
30
+ fields: { principleText: 'text' },
31
+ derivedFrom: 'structured_output',
32
+ omittedFields: [],
33
+ };
34
+ }
35
+ function makeRef(contentJson) {
36
+ return {
37
+ artifactId: 'artifact-1',
38
+ runnerKind: 'scribe',
39
+ contentHash: computeContentHash(contentJson, sha256),
40
+ summary: makeSummary(),
41
+ };
42
+ }
43
+ const jsonSafeValueGen = fc.jsonValue({ maxDepth: 3 });
44
+ const jsonSafeObjectGen = fc.dictionary(fc.string({ minLength: 1, maxLength: 10 }), jsonSafeValueGen, { minKeys: 1, maxKeys: 6 });
45
+ describe('checkPredecessorSummaryFreshness — CP-09 freshness ⟺ content hash equality', () => {
46
+ it('identity: unchanged content is always fresh', () => {
47
+ fc.assert(fc.property(jsonSafeObjectGen, (content) => {
48
+ const ref = makeRef(content);
49
+ expect(checkPredecessorSummaryFreshness(ref, content, sha256)).toEqual({ fresh: true });
50
+ }), { numRuns: 100 });
51
+ });
52
+ it('value change on an existing key: hash mismatch → stale', () => {
53
+ fc.assert(fc.property(jsonSafeObjectGen, fc.string({ minLength: 1, maxLength: 5 }), jsonSafeValueGen, (content, key, newValue) => {
54
+ const ref = makeRef(content);
55
+ const mutated = { ...content, [key]: newValue };
56
+ // Only assert staleness when the mutation actually changes the
57
+ // canonical serialization (guards against the rare case where
58
+ // newValue happens to canonicalize identically to the old value,
59
+ // or the key didn't previously exist and content is empty).
60
+ const before = computeContentHash(content, sha256);
61
+ const after = computeContentHash(mutated, sha256);
62
+ fc.pre(before !== after);
63
+ expect(checkPredecessorSummaryFreshness(ref, mutated, sha256)).toEqual({
64
+ fresh: false,
65
+ reason: 'content_hash_mismatch',
66
+ });
67
+ }), { numRuns: 200 });
68
+ });
69
+ it('key deletion: hash mismatch → stale (when content actually shrinks)', () => {
70
+ fc.assert(fc.property(jsonSafeObjectGen, (content) => {
71
+ const keys = Object.keys(content);
72
+ const firstKey = keys[0];
73
+ fc.pre(firstKey !== undefined);
74
+ fc.pre(keys.length > 0);
75
+ const ref = makeRef(content);
76
+ const { [firstKey]: _removed, ...rest } = content;
77
+ const before = computeContentHash(content, sha256);
78
+ const after = computeContentHash(rest, sha256);
79
+ fc.pre(before !== after);
80
+ expect(checkPredecessorSummaryFreshness(ref, rest, sha256)).toEqual({
81
+ fresh: false,
82
+ reason: 'content_hash_mismatch',
83
+ });
84
+ }), { numRuns: 150 });
85
+ });
86
+ it('key addition: hash mismatch → stale', () => {
87
+ fc.assert(fc.property(jsonSafeObjectGen, jsonSafeValueGen, (content, newValue) => {
88
+ const ref = makeRef(content);
89
+ const added = { ...content, __new_key__: newValue };
90
+ expect(checkPredecessorSummaryFreshness(ref, added, sha256)).toEqual({
91
+ fresh: false,
92
+ reason: 'content_hash_mismatch',
93
+ });
94
+ }), { numRuns: 100 });
95
+ });
96
+ it('setting an existing key to undefined (JSON-equivalent to deletion): matches JSON semantics', () => {
97
+ fc.assert(fc.property(jsonSafeObjectGen, (content) => {
98
+ const keys = Object.keys(content);
99
+ const firstKey = keys[0];
100
+ fc.pre(firstKey !== undefined);
101
+ fc.pre(keys.length > 0);
102
+ const ref = makeRef(content);
103
+ const mutated = { ...content, [firstKey]: undefined };
104
+ const before = computeContentHash(content, sha256);
105
+ const after = computeContentHash(mutated, sha256);
106
+ const result = checkPredecessorSummaryFreshness(ref, mutated, sha256);
107
+ if (before === after) {
108
+ expect(result).toEqual({ fresh: true });
109
+ }
110
+ else {
111
+ expect(result).toEqual({ fresh: false, reason: 'content_hash_mismatch' });
112
+ }
113
+ }), { numRuns: 100 });
114
+ });
115
+ it('missing predecessor content → predecessor_missing, distinct from a hash mismatch', () => {
116
+ const ref = makeRef({ a: 1 });
117
+ expect(checkPredecessorSummaryFreshness(ref, undefined, sha256)).toEqual({
118
+ fresh: false,
119
+ reason: 'predecessor_missing',
120
+ });
121
+ expect(checkPredecessorSummaryFreshness(undefined, { a: 1 }, sha256)).toEqual({
122
+ fresh: false,
123
+ reason: 'predecessor_missing',
124
+ });
125
+ });
126
+ it('performs zero artifact-store reads (no I/O; predecessor must already be in memory)', () => {
127
+ const store = new CountingStoreStub();
128
+ const content = { a: 1, b: 'text' };
129
+ const ref = makeRef(content);
130
+ checkPredecessorSummaryFreshness(ref, content, sha256);
131
+ checkPredecessorSummaryFreshness(ref, undefined, sha256);
132
+ checkPredecessorSummaryFreshness(undefined, content, sha256);
133
+ expect(store.reads).toBe(0);
134
+ });
135
+ });
136
+ describe('checkPredecessorSummaryFreshness — CP-10 updatedAt does not participate', () => {
137
+ it('is unaffected by a pending→validated-style timestamp-only refresh (memory store shape)', () => {
138
+ fc.assert(fc.property(jsonSafeObjectGen, fc.string({ minLength: 1, maxLength: 24 }), (content, newUpdatedAt) => {
139
+ // Simulate a memory-store record: contentJson is untouched, only a
140
+ // sibling `updatedAt` field on the *record* (not inside contentJson)
141
+ // changes. checkPredecessorSummaryFreshness only ever receives the
142
+ // contentJson itself, so it structurally cannot see updatedAt.
143
+ const ref = makeRef(content);
144
+ const recordAfterRefresh = { contentJson: content, updatedAt: newUpdatedAt };
145
+ expect(checkPredecessorSummaryFreshness(ref, recordAfterRefresh.contentJson, sha256)).toEqual({
146
+ fresh: true,
147
+ });
148
+ }), { numRuns: 100 });
149
+ });
150
+ it('is unaffected by a pending→validated-style timestamp-only refresh (sqlite upsert shape)', () => {
151
+ fc.assert(fc.property(jsonSafeObjectGen, fc.integer({ min: 0, max: 2_000_000_000 }), (content, newUpdatedAtEpoch) => {
152
+ // Simulate a sqlite row: contentJson column unchanged, updated_at
153
+ // column bumped by an upsert. Same structural argument as above —
154
+ // the row's updated_at is never passed into the freshness check.
155
+ const ref = makeRef(content);
156
+ const rowAfterUpsert = { content_json: JSON.stringify(content), updated_at: newUpdatedAtEpoch };
157
+ const parsedContentJson = JSON.parse(rowAfterUpsert.content_json);
158
+ expect(checkPredecessorSummaryFreshness(ref, parsedContentJson, sha256)).toEqual({
159
+ fresh: true,
160
+ });
161
+ }), { numRuns: 100 });
162
+ });
163
+ it('freshness result does not change when only an out-of-band updatedAt-like value varies, content held fixed', () => {
164
+ const content = { statement: 'fixed content', scope: ['a', 'b'] };
165
+ const ref = makeRef(content);
166
+ const timestamps = ['2026-01-01T00:00:00Z', '2026-06-15T12:30:00Z', '2027-12-31T23:59:59Z'];
167
+ for (const _ts of timestamps) {
168
+ // The function signature has no updatedAt parameter at all — this test
169
+ // documents that fact by construction: there is no way to pass a
170
+ // timestamp into checkPredecessorSummaryFreshness that could influence
171
+ // its result.
172
+ expect(checkPredecessorSummaryFreshness(ref, content, sha256)).toEqual({ fresh: true });
173
+ }
174
+ });
175
+ });
176
+ //# sourceMappingURL=summary-freshness.property.test.js.map