pi-fabric 0.91.0 → 0.92.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 (98) hide show
  1. package/README.md +1 -1
  2. package/dist/audit/trace.d.ts +4 -0
  3. package/dist/audit/trace.d.ts.map +1 -1
  4. package/dist/chunks/chunk-3YZUQESG.js +16 -0
  5. package/dist/chunks/{chunk-7363CD5A.js.map → chunk-3YZUQESG.js.map} +1 -1
  6. package/dist/chunks/{chunk-H6FEQL2D.js → chunk-5MDFFHOO.js} +223 -8
  7. package/dist/chunks/chunk-5MDFFHOO.js.map +7 -0
  8. package/dist/chunks/{chunk-IRPPGNUF.js → chunk-6GT4YJT2.js} +4 -4
  9. package/dist/chunks/{chunk-NGAUBL2M.js → chunk-DDVCVE4D.js} +3 -3
  10. package/dist/chunks/{chunk-F64KOMNS.js → chunk-FRONVGLX.js} +2 -2
  11. package/dist/chunks/{chunk-P22OSVIC.js → chunk-JS2THBBG.js} +2 -2
  12. package/dist/chunks/{chunk-TDCMU5L4.js → chunk-KNBPIZF7.js} +2 -2
  13. package/dist/chunks/{chunk-UVUWHPAO.js → chunk-LLJXPPTA.js} +150 -610
  14. package/dist/chunks/chunk-LLJXPPTA.js.map +7 -0
  15. package/dist/chunks/{chunk-TAJUG2Q3.js → chunk-MF46OHNU.js} +2 -2
  16. package/dist/chunks/{chunk-LJ6FJRFH.js → chunk-OJX2V36R.js} +2 -2
  17. package/dist/chunks/{chunk-5JEOABJ7.js → chunk-QJUDWDPL.js} +59 -50
  18. package/dist/chunks/chunk-QJUDWDPL.js.map +7 -0
  19. package/dist/chunks/{chunk-6LLUZUH4.js → chunk-QOHTGSXY.js} +4 -4
  20. package/dist/chunks/{chunk-DBNZVS2S.js → chunk-RRGTKHXZ.js} +2 -2
  21. package/dist/chunks/{chunk-KAWR7MS5.js → chunk-T3UYWHLT.js} +296 -7
  22. package/dist/chunks/chunk-T3UYWHLT.js.map +7 -0
  23. package/dist/chunks/{chunk-77FQHHZD.js → chunk-UKMQK5VP.js} +2 -2
  24. package/dist/chunks/{chunk-MYCDJTSM.js → chunk-VJJFD42F.js} +2 -2
  25. package/dist/chunks/{chunk-UQHXFLST.js → chunk-X45GMLON.js} +2 -2
  26. package/dist/commands/fabric.d.ts.map +1 -1
  27. package/dist/compaction/hook.js +4 -3
  28. package/dist/core/action-arguments.d.ts +2 -0
  29. package/dist/core/action-arguments.d.ts.map +1 -1
  30. package/dist/core/action-registry.d.ts.map +1 -1
  31. package/dist/core/action-registry.js +4 -5
  32. package/dist/core/action-repair.d.ts.map +1 -1
  33. package/dist/entropy/active.d.ts +6 -4
  34. package/dist/entropy/active.d.ts.map +1 -1
  35. package/dist/entropy/compiled-store.d.ts.map +1 -1
  36. package/dist/entropy/compiled-surface.d.ts +11 -6
  37. package/dist/entropy/compiled-surface.d.ts.map +1 -1
  38. package/dist/entropy/compiler.d.ts +0 -1
  39. package/dist/entropy/compiler.d.ts.map +1 -1
  40. package/dist/entropy/corpus.d.ts.map +1 -1
  41. package/dist/entropy/index.d.ts +1 -0
  42. package/dist/entropy/index.d.ts.map +1 -1
  43. package/dist/entropy/index.js +45 -26
  44. package/dist/entropy/meter.d.ts.map +1 -1
  45. package/dist/entropy/normal-form.d.ts +40 -0
  46. package/dist/entropy/normal-form.d.ts.map +1 -0
  47. package/dist/entropy/passes.d.ts +1 -1
  48. package/dist/entropy/passes.d.ts.map +1 -1
  49. package/dist/entropy/presentation.d.ts +1 -0
  50. package/dist/entropy/presentation.d.ts.map +1 -1
  51. package/dist/entropy/surface.d.ts.map +1 -1
  52. package/dist/entropy/trial.d.ts +6 -3
  53. package/dist/entropy/trial.d.ts.map +1 -1
  54. package/dist/entropy/types.d.ts +26 -3
  55. package/dist/entropy/types.d.ts.map +1 -1
  56. package/dist/fabric-runtime-state.d.ts.map +1 -1
  57. package/dist/fabric-runtime-state.js +42 -41
  58. package/dist/fabric-runtime-state.js.map +3 -3
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +70 -67
  61. package/dist/index.js.map +2 -2
  62. package/dist/mcp.js +2 -2
  63. package/dist/memory/discovery.js +5 -4
  64. package/dist/memory/normalize.js +4 -3
  65. package/dist/memory/search.js +5 -4
  66. package/dist/memory.js +7 -6
  67. package/dist/memory.js.map +1 -1
  68. package/dist/protocol.js +2 -2
  69. package/dist/providers/memory-provider.js +7 -6
  70. package/dist/repairs/apply.d.ts.map +1 -1
  71. package/dist/residency/host.js +8 -8
  72. package/dist/residency/pi-entry.js +8 -8
  73. package/dist/speculation/python-scanner.js +1 -1
  74. package/dist/speculation/scanner.js +1 -1
  75. package/docs/certification.md +37 -8
  76. package/docs/configuration.md +1 -1
  77. package/docs/entropy.md +206 -420
  78. package/docs/repairs.md +22 -1
  79. package/package.json +1 -1
  80. package/dist/chunks/chunk-5JEOABJ7.js.map +0 -7
  81. package/dist/chunks/chunk-7363CD5A.js +0 -38
  82. package/dist/chunks/chunk-FMLK3ZA7.js +0 -283
  83. package/dist/chunks/chunk-FMLK3ZA7.js.map +0 -7
  84. package/dist/chunks/chunk-H6FEQL2D.js.map +0 -7
  85. package/dist/chunks/chunk-KAWR7MS5.js.map +0 -7
  86. package/dist/chunks/chunk-UVUWHPAO.js.map +0 -7
  87. /package/dist/chunks/{chunk-IRPPGNUF.js.map → chunk-6GT4YJT2.js.map} +0 -0
  88. /package/dist/chunks/{chunk-NGAUBL2M.js.map → chunk-DDVCVE4D.js.map} +0 -0
  89. /package/dist/chunks/{chunk-F64KOMNS.js.map → chunk-FRONVGLX.js.map} +0 -0
  90. /package/dist/chunks/{chunk-P22OSVIC.js.map → chunk-JS2THBBG.js.map} +0 -0
  91. /package/dist/chunks/{chunk-TDCMU5L4.js.map → chunk-KNBPIZF7.js.map} +0 -0
  92. /package/dist/chunks/{chunk-TAJUG2Q3.js.map → chunk-MF46OHNU.js.map} +0 -0
  93. /package/dist/chunks/{chunk-LJ6FJRFH.js.map → chunk-OJX2V36R.js.map} +0 -0
  94. /package/dist/chunks/{chunk-6LLUZUH4.js.map → chunk-QOHTGSXY.js.map} +0 -0
  95. /package/dist/chunks/{chunk-DBNZVS2S.js.map → chunk-RRGTKHXZ.js.map} +0 -0
  96. /package/dist/chunks/{chunk-77FQHHZD.js.map → chunk-UKMQK5VP.js.map} +0 -0
  97. /package/dist/chunks/{chunk-MYCDJTSM.js.map → chunk-VJJFD42F.js.map} +0 -0
  98. /package/dist/chunks/{chunk-UQHXFLST.js.map → chunk-X45GMLON.js.map} +0 -0
@@ -1,183 +1,39 @@
1
- import {
2
- withExclusiveFileLock,
3
- withExclusiveFileLockAsync
4
- } from "./chunk-H6FEQL2D.js";
5
1
  import {
6
2
  COMPILED_SURFACE_VERSION,
3
+ ENTROPY_INVOCATION_STAGES,
4
+ ENTROPY_METRIC_VERSION,
5
+ ENTROPY_WEIGHTS,
7
6
  MAX_COMPILED_SURFACE_PROPOSALS,
8
- applyCompiledSurface,
9
- replaySuccessfulCalls,
10
- replaySuccessfulCallsAsync,
11
- schemaDigest
12
- } from "./chunk-FMLK3ZA7.js";
7
+ emptyCompiledSurface,
8
+ withExclusiveFileLock,
9
+ withExclusiveFileLockAsync
10
+ } from "./chunk-5MDFFHOO.js";
13
11
  import {
14
12
  sessionDirForCwd,
15
13
  sessionsDirRoot
16
- } from "./chunk-TDCMU5L4.js";
14
+ } from "./chunk-KNBPIZF7.js";
17
15
  import {
18
16
  writeJsonAtomic,
19
17
  writeJsonAtomicAsync
20
18
  } from "./chunk-KAD7UWT2.js";
21
19
  import {
22
20
  FABRIC_EXECUTION_TRACE_KIND,
23
- readFabricExecutionTraceV1
24
- } from "./chunk-KAWR7MS5.js";
21
+ MAX_NORMAL_FORM_PLANS,
22
+ applyNormalFormPlan,
23
+ compareCodeUnits,
24
+ deriveNormalFormPlan,
25
+ provesNormalFormPlan,
26
+ readFabricExecutionTraceV1,
27
+ roundMetric,
28
+ shannonEntropyBits,
29
+ shapeSignature,
30
+ signatureDistance,
31
+ staticFreedomFromSchema,
32
+ trendFromScores
33
+ } from "./chunk-T3UYWHLT.js";
25
34
  import {
26
- stableJsonHash,
27
- stableJsonHashArrayAsync
28
- } from "./chunk-7363CD5A.js";
29
-
30
- // src/entropy/types.ts
31
- var ENTROPY_METRIC_VERSION = 2;
32
- var ENTROPY_WEIGHTS = {
33
- shape: 1,
34
- failureStage: 1,
35
- churn: 4,
36
- navigation: 4,
37
- flow: 1,
38
- lexicon: 2,
39
- staticFreedom: 0.25
40
- };
41
- var ENTROPY_INVOCATION_STAGES = ["resolve", "prepare", "validate"];
42
-
43
- // src/entropy/fingerprint.ts
44
- var SIGNATURE_MAX_KEYS = 32;
45
- var SIGNATURE_MAX_DEPTH = 3;
46
- var ENUM_SATURATION_BITS = 6;
47
- var isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
48
- var roundMetric = (value) => {
49
- const rounded = Math.round(value * 1e6) / 1e6;
50
- return rounded === 0 ? 0 : rounded;
51
- };
52
- var compareCodeUnits = (left, right) => left < right ? -1 : left > right ? 1 : 0;
53
- var typeTag = (value, depth) => {
54
- if (value === null) return "null";
55
- if (Array.isArray(value)) return "arr";
56
- switch (typeof value) {
57
- case "string":
58
- return "str";
59
- case "number":
60
- return "num";
61
- case "boolean":
62
- return "bool";
63
- case "object": {
64
- if (depth >= SIGNATURE_MAX_DEPTH) return "obj";
65
- try {
66
- const record = value;
67
- const keys = Object.keys(record).sort();
68
- const head = keys.slice(0, SIGNATURE_MAX_KEYS).map((key) => `${key}:${typeTag(record[key], depth + 1)}`);
69
- if (keys.length > SIGNATURE_MAX_KEYS) {
70
- head.push(`\u2026+${keys.length - SIGNATURE_MAX_KEYS}`);
71
- }
72
- return `{${head.join(",")}}`;
73
- } catch {
74
- return "obj";
75
- }
76
- }
77
- default:
78
- return "other";
79
- }
80
- };
81
- var shapeSignature = (args) => {
82
- const keys = Object.keys(args).sort();
83
- const head = keys.slice(0, SIGNATURE_MAX_KEYS).map((key) => `${key}:${typeTag(args[key], 1)}`);
84
- if (keys.length > SIGNATURE_MAX_KEYS) {
85
- head.push(`\u2026+${keys.length - SIGNATURE_MAX_KEYS}`);
86
- }
87
- return `(${head.join(",")})`;
88
- };
89
- var signatureDistance = (left, right) => {
90
- if (left === right) return 0;
91
- const bound = Math.max(left.length, right.length);
92
- if (bound === 0) return 0;
93
- let previous = [];
94
- for (let index = 0; index <= right.length; index++) {
95
- previous.push(index);
96
- }
97
- for (let index = 1; index <= left.length; index++) {
98
- const current = [index];
99
- for (let inner = 1; inner <= right.length; inner++) {
100
- const substitution = previous[inner - 1] + (left[index - 1] === right[inner - 1] ? 0 : 1);
101
- const insertion = current[inner - 1] + 1;
102
- const deletion = previous[inner] + 1;
103
- current.push(Math.min(substitution, insertion, deletion));
104
- }
105
- previous = current;
106
- }
107
- return roundMetric(previous[right.length] / bound);
108
- };
109
- var shannonEntropyBits = (counts) => {
110
- const total = counts.reduce((sum, value) => sum + value, 0);
111
- if (total <= 0) return 0;
112
- let bits = 0;
113
- for (const count of counts) {
114
- if (count <= 0) continue;
115
- const probability = count / total;
116
- bits += probability * Math.log2(probability);
117
- }
118
- return roundMetric(-bits);
119
- };
120
- var schemaFreedom = (schema, depth) => {
121
- if (depth > SIGNATURE_MAX_DEPTH) return 0.5;
122
- if (!isPlainRecord(schema)) return 0.5;
123
- const union = Array.isArray(schema.anyOf) ? schema.anyOf : Array.isArray(schema.oneOf) ? schema.oneOf : void 0;
124
- if (union) {
125
- const scores = union.map((branch) => schemaFreedom(branch, depth + 1));
126
- const widest = scores.length > 0 ? Math.max(...scores) : 0;
127
- return roundMetric(widest + 0.1 * Math.max(0, union.length - 1));
128
- }
129
- if (Array.isArray(schema.enum)) {
130
- return roundMetric(
131
- Math.min(1, Math.log2(Math.max(1, schema.enum.length)) / ENUM_SATURATION_BITS)
132
- );
133
- }
134
- if ("const" in schema) return 0;
135
- const type = schema.type;
136
- if (typeof type !== "string") return 0.75;
137
- if (type === "boolean") return 0.1;
138
- if (type === "number" || type === "integer") return 0.5;
139
- if (type === "string") return 1;
140
- if (type === "array") return roundMetric(0.5 + 0.5 * schemaFreedom(schema.items, depth + 1));
141
- if (type === "object") {
142
- const properties = schema.properties;
143
- if (!isPlainRecord(properties)) return 1;
144
- const keys = Object.keys(properties).sort();
145
- if (keys.length === 0) return 1;
146
- const required = Array.isArray(schema.required) ? schema.required.filter((entry) => typeof entry === "string") : [];
147
- let score = 0;
148
- for (const key of keys) {
149
- score += schemaFreedom(properties[key], depth + 1);
150
- if (!required.includes(key)) score += 0.25;
151
- }
152
- if (schema.additionalProperties !== false) score += 0.5;
153
- return roundMetric(score);
154
- }
155
- return 0.75;
156
- };
157
- var staticFreedomFromSchema = (schema) => schemaFreedom(schema, 0);
158
- var trendFromScores = (scores) => {
159
- const count = scores.length;
160
- const first = count > 0 ? scores[0] : void 0;
161
- const last = count > 0 ? scores[count - 1] : void 0;
162
- let slope = 0;
163
- if (count >= 2) {
164
- const meanX = (count - 1) / 2;
165
- const meanY = scores.reduce((sum, value) => sum + value, 0) / count;
166
- let numerator = 0;
167
- let denominator = 0;
168
- for (let index = 0; index < count; index++) {
169
- numerator += (index - meanX) * (scores[index] - meanY);
170
- denominator += (index - meanX) ** 2;
171
- }
172
- slope = denominator > 0 ? numerator / denominator : 0;
173
- }
174
- return {
175
- count,
176
- ...first !== void 0 ? { first: roundMetric(first) } : {},
177
- ...last !== void 0 ? { last: roundMetric(last) } : {},
178
- slopePerStep: roundMetric(slope)
179
- };
180
- };
35
+ stableJsonHash
36
+ } from "./chunk-3YZUQESG.js";
181
37
 
182
38
  // src/entropy/meter.ts
183
39
  var DISCOVERY_PREFIX = "fabric.discovery.";
@@ -296,7 +152,6 @@ var finalizeMeasure = (input, state) => {
296
152
  let staticWeighted = 0;
297
153
  let shapeWeighted = 0;
298
154
  let failureStageWeighted = 0;
299
- let scoreSum = 0;
300
155
  for (const [ref, acc] of refEntries) {
301
156
  const shapeEntropyBits = shannonEntropyBits([...acc.signatures.values()]);
302
157
  const failureStageEntropyBits = shannonEntropyBits([...acc.stages.values()]);
@@ -304,13 +159,14 @@ var finalizeMeasure = (input, state) => {
304
159
  const lexicon = state.lexiconByRef.get(ref) ?? 0;
305
160
  const staticFreedom = state.surfaceByRef.has(ref) ? staticFreedomFromSchema(state.surfaceByRef.get(ref)) : 0;
306
161
  staticFreedomTotal += staticFreedom;
307
- staticWeighted += ENTROPY_WEIGHTS.staticFreedom * staticFreedom;
162
+ staticWeighted += ENTROPY_WEIGHTS.staticFreedom * staticFreedom * acc.calls;
308
163
  shapeWeighted += shapeEntropyBits * acc.calls;
309
164
  failureStageWeighted += failureStageEntropyBits * acc.failed;
310
- const score2 = roundMetric(
311
- ENTROPY_WEIGHTS.shape * shapeEntropyBits + ENTROPY_WEIGHTS.failureStage * failureStageEntropyBits + ENTROPY_WEIGHTS.churn * refChurnRate + ENTROPY_WEIGHTS.lexicon * lexicon + ENTROPY_WEIGHTS.staticFreedom * staticFreedom
165
+ const invocationRejections = ENTROPY_INVOCATION_STAGES.reduce(
166
+ (sum, stage) => sum + (acc.stages.get(stage) ?? 0),
167
+ 0
312
168
  );
313
- scoreSum += score2;
169
+ const score2 = roundMetric(invocationRejections / acc.calls);
314
170
  const shapeSignatures = [...acc.signatures.entries()].map(([signature, count]) => ({ signature, count })).sort(
315
171
  (left, right) => right.count - left.count || compareCodeUnits(left.signature, right.signature)
316
172
  ).slice(0, TOP_SIGNATURES);
@@ -339,11 +195,9 @@ var finalizeMeasure = (input, state) => {
339
195
  }
340
196
  const flowEntropyBits = flowOccurrences > 0 ? roundMetric(flowWeighted / flowOccurrences) : 0;
341
197
  state.totals.invocationRejectionsPer1k = state.totals.actionOperations > 0 ? roundMetric(state.totals.invocationRejections / state.totals.actionOperations * 1e3) : 0;
342
- const score = roundMetric(
343
- (scoreSum + ENTROPY_WEIGHTS.navigation * navigationRatio + ENTROPY_WEIGHTS.flow * flowEntropyBits) / Math.max(1, state.totals.succeeded)
344
- );
345
- const staticScore = roundMetric(staticWeighted / Math.max(1, state.totals.succeeded));
346
- const behavioralScore = roundMetric(score - staticScore);
198
+ const score = state.totals.actionOperations > 0 ? roundMetric(state.totals.invocationRejections / state.totals.actionOperations) : 0;
199
+ const staticScore = state.totals.actionOperations > 0 ? roundMetric(staticWeighted / state.totals.actionOperations) : 0;
200
+ const behavioralScore = score;
347
201
  const sortedRefs = [...refReports].sort(
348
202
  (left, right) => right.score - left.score || compareCodeUnits(left.ref, right.ref)
349
203
  );
@@ -440,26 +294,14 @@ var FUSE_MIN_SEQUENCE_LENGTH = 3;
440
294
  var FUSE_MAX_SEQUENCE_LENGTH = 6;
441
295
  var FUSE_MIN_OCCURRENCES = 3;
442
296
  var FUSE_MIN_EXECUTIONS = 3;
443
- var QUARANTINE_MIN_CALLS = 3;
444
- var QUARANTINE_MIN_STAGE_ENTROPY_BITS = 1;
445
297
  var MAX_PROPOSALS_PER_KIND = 8;
446
298
  var ENTROPY_ENUM_CANDIDATE_ANNOTATION = "x-fabric-enum-candidate";
447
- var isPlainRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
299
+ var isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
448
300
  var valueKey = (value) => `${typeof value}:${String(value)}`;
449
301
  var schemaProperties = (schema) => {
450
- if (!isPlainRecord2(schema)) return void 0;
302
+ if (!isPlainRecord(schema)) return void 0;
451
303
  const properties = schema.properties;
452
- return isPlainRecord2(properties) ? properties : void 0;
453
- };
454
- var enumKeys = (schema) => {
455
- if (!isPlainRecord2(schema) || !Array.isArray(schema.enum)) return void 0;
456
- const keys = /* @__PURE__ */ new Set();
457
- for (const entry of schema.enum) {
458
- if (typeof entry === "string" || typeof entry === "number" || typeof entry === "boolean") {
459
- keys.add(valueKey(entry));
460
- }
461
- }
462
- return keys;
304
+ return isPlainRecord(properties) ? properties : void 0;
463
305
  };
464
306
  var contains = (outer, inner) => {
465
307
  if (outer.length < inner.length) return false;
@@ -475,23 +317,20 @@ var contains = (outer, inner) => {
475
317
  }
476
318
  return false;
477
319
  };
478
- var tightenBeneathDeclaredEnum = (candidate, declaredDomain) => {
479
- const ranked = candidate.ranked.filter((item) => declaredDomain.has(valueKey(item.value)));
480
- if (ranked.length === 0 || ranked.length === declaredDomain.size) return void 0;
481
- return { ...candidate, ranked };
482
- };
483
320
  var proposeEntropyReductions = (input) => {
484
321
  const calledRefs = /* @__PURE__ */ new Map();
485
322
  for (const ref of input.report.refs) calledRefs.set(ref.ref, ref);
486
323
  const surfaceByRef = /* @__PURE__ */ new Map();
487
- const surfaceRefs = /* @__PURE__ */ new Set();
488
324
  if (input.surface) {
489
325
  for (const action of input.surface.actions) {
490
326
  surfaceByRef.set(action.ref, action.inputSchema);
491
- surfaceRefs.add(action.ref);
492
327
  }
493
328
  }
494
329
  const proposals = [];
330
+ for (const action of [...input.surface?.actions ?? []].sort((a, b) => compareCodeUnits(a.ref, b.ref)).slice(0, MAX_NORMAL_FORM_PLANS)) {
331
+ const plan = deriveNormalFormPlan(action.ref, action.inputSchema);
332
+ if (plan) proposals.push({ kind: "normal-form", ref: plan.ref, baseSchemaDigest: plan.baseSchemaDigest, rules: plan.rules });
333
+ }
495
334
  const observations = /* @__PURE__ */ new Map();
496
335
  const observeValue = (ref, key, value, weight = 1) => {
497
336
  const id = `${ref}\0${key}`;
@@ -530,35 +369,11 @@ var proposeEntropyReductions = (input) => {
530
369
  );
531
370
  return { entry, ranked, topShare: roundMetric(ranked[0].count / entry.total) };
532
371
  }).filter((candidate) => candidate.topShare >= ENUM_MIN_TOP_SHARE);
533
- const closedCandidates = [];
534
- const openCandidates = [];
535
- for (const candidate of eligibleCandidates) {
536
- const properties = input.surface ? schemaProperties(surfaceByRef.get(candidate.entry.ref)) : void 0;
537
- const target = properties ? properties[candidate.entry.key] : void 0;
538
- if (isPlainRecord2(target) && target.type === "boolean") continue;
539
- const declaredDomain = target ? enumKeys(target) : void 0;
540
- if (declaredDomain) {
541
- const tightened = tightenBeneathDeclaredEnum(candidate, declaredDomain);
542
- if (tightened) closedCandidates.push(tightened);
543
- } else if (isPlainRecord2(target) && target[ENTROPY_ENUM_CANDIDATE_ANNOTATION] === true) {
544
- openCandidates.push(candidate);
545
- }
546
- }
547
- const byRefKey = (left, right) => compareCodeUnits(
548
- `${left.entry.ref}\0${left.entry.key}`,
549
- `${right.entry.ref}\0${right.entry.key}`
550
- );
551
- for (const candidate of closedCandidates.sort(byRefKey).slice(0, MAX_PROPOSALS_PER_KIND)) {
552
- proposals.push({
553
- kind: "enum-tighten",
554
- ref: candidate.entry.ref,
555
- key: candidate.entry.key,
556
- values: candidate.ranked.map((item) => item.value),
557
- calls: candidate.entry.total,
558
- distinct: candidate.ranked.length,
559
- topShare: candidate.topShare
560
- });
561
- }
372
+ const openCandidates = eligibleCandidates.filter((candidate) => {
373
+ const target = schemaProperties(surfaceByRef.get(candidate.entry.ref))?.[candidate.entry.key];
374
+ return isPlainRecord(target) && target.type !== "boolean" && !Array.isArray(target.enum) && target[ENTROPY_ENUM_CANDIDATE_ANNOTATION] === true;
375
+ });
376
+ const byRefKey = (left, right) => compareCodeUnits(`${left.entry.ref}\0${left.entry.key}`, `${right.entry.ref}\0${right.entry.key}`);
562
377
  for (const candidate of openCandidates.sort(byRefKey).slice(0, MAX_PROPOSALS_PER_KIND)) {
563
378
  proposals.push({
564
379
  kind: "declare-enum",
@@ -671,47 +486,9 @@ var proposeEntropyReductions = (input) => {
671
486
  occurrences: candidate.occurrences
672
487
  });
673
488
  }
674
- const quarantineCandidates = input.report.refs.filter(
675
- (ref) => ref.calls >= QUARANTINE_MIN_CALLS && ref.failed > ref.succeeded && ref.failureStageEntropyBits >= QUARANTINE_MIN_STAGE_ENTROPY_BITS
676
- ).filter((ref) => !input.surface || surfaceRefs.has(ref.ref)).slice(0, MAX_PROPOSALS_PER_KIND);
677
- for (const ref of quarantineCandidates) {
678
- proposals.push({
679
- kind: "noise-quarantine",
680
- ref: ref.ref,
681
- calls: ref.calls,
682
- succeeded: ref.succeeded,
683
- failed: ref.failed,
684
- failureStageEntropyBits: ref.failureStageEntropyBits
685
- });
686
- }
687
489
  return proposals;
688
490
  };
689
- var deepCloneJson = (value) => value === void 0 ? {} : JSON.parse(JSON.stringify(value));
690
- var applyProposalsToSurface = (surface, proposals) => {
691
- const actions = surface.actions.map((action) => ({
692
- ref: action.ref,
693
- inputSchema: deepCloneJson(action.inputSchema)
694
- }));
695
- const quarantined = /* @__PURE__ */ new Set();
696
- for (const proposal of proposals) {
697
- if (proposal.kind === "noise-quarantine") {
698
- quarantined.add(proposal.ref);
699
- continue;
700
- }
701
- if (proposal.kind === "enum-tighten") {
702
- const action = actions.find((candidate) => candidate.ref === proposal.ref);
703
- if (!action || !isPlainRecord2(action.inputSchema)) continue;
704
- const properties = schemaProperties(action.inputSchema);
705
- if (!properties || !isPlainRecord2(properties[proposal.key])) continue;
706
- properties[proposal.key] = {
707
- ...properties[proposal.key],
708
- enum: [...proposal.values]
709
- };
710
- }
711
- }
712
- const survivors = actions.filter((action) => !quarantined.has(action.ref)).sort((left, right) => compareCodeUnits(left.ref, right.ref));
713
- return { version: 1, actions: survivors };
714
- };
491
+ var applyProposalsToSurface = (surface, _proposals) => surface;
715
492
  var evaluateGate = (before, after) => {
716
493
  const reasons = [];
717
494
  if (after.score > before.score) {
@@ -1195,258 +972,60 @@ var surfaceFreedomReport = (surface) => {
1195
972
  };
1196
973
 
1197
974
  // src/entropy/compiler.ts
1198
- var AUTO_APPLY_PROPOSAL_KINDS = ["enum-tighten", "noise-quarantine"];
1199
- var entropyReviewSignals = (input) => proposeEntropyReductions(input).filter(
1200
- (proposal) => !AUTO_APPLY_PROPOSAL_KINDS.includes(proposal.kind)
1201
- );
975
+ var AUTO_APPLY_PROPOSAL_KINDS = ["normal-form"];
976
+ var entropyReviewSignals = (input) => proposeEntropyReductions(input).filter((proposal) => ["declare-enum", "overload-split", "sequence-fuse"].includes(proposal.kind));
1202
977
  var formatEntropyReviewSignal = (proposal) => {
1203
978
  if (proposal.kind === "declare-enum") {
1204
- const values = proposal.values.slice(0, 4).map((value) => String(value)).join(", ");
979
+ const values = proposal.values.slice(0, 4).map(String).join(", ");
1205
980
  return `declare-enum ${proposal.ref}.${proposal.key} (${values}${proposal.values.length > 4 ? ", ..." : ""})`;
1206
981
  }
1207
- if (proposal.kind === "overload-split") {
1208
- return `overload-split ${proposal.ref} (${proposal.clusters.length} key-set clusters)`;
1209
- }
1210
- if (proposal.kind === "sequence-fuse") {
1211
- return `sequence-fuse ${proposal.sequence.join(" -> ")}`;
1212
- }
982
+ if (proposal.kind === "overload-split") return `overload-split ${proposal.ref} (${proposal.clusters.length} key-set clusters)`;
983
+ if (proposal.kind === "sequence-fuse") return `sequence-fuse ${proposal.sequence.join(" -> ")}`;
1213
984
  return `${proposal.kind} ${proposal.ref}`;
1214
985
  };
1215
- var proposalDetail = (proposal) => {
1216
- if (proposal.kind === "enum-tighten") {
1217
- return { ref: proposal.ref, detail: `${proposal.key}: ${proposal.values.length} observed values` };
1218
- }
1219
- if (proposal.kind === "noise-quarantine") {
1220
- return { ref: proposal.ref, detail: `${proposal.failed} failed vs ${proposal.succeeded} succeeded` };
1221
- }
1222
- return { ref: "n/a", detail: "review-only" };
1223
- };
1224
- var touchedRefs = (before, after) => {
1225
- const beforeByRef = new Map(before.actions.map((action) => [action.ref, action.inputSchema]));
1226
- const touched = /* @__PURE__ */ new Set();
1227
- for (const action of after.actions) {
1228
- if (stableJsonHash(beforeByRef.get(action.ref)) !== stableJsonHash(action.inputSchema)) {
1229
- touched.add(action.ref);
1230
- }
1231
- }
1232
- for (const ref of beforeByRef.keys()) {
1233
- if (!after.actions.some((action) => action.ref === ref)) touched.add(ref);
1234
- }
1235
- return touched;
1236
- };
1237
- var artifactFromCandidate = (live, candidate, proposals, gate, evidenceDigest, previous) => {
1238
- const liveByRef = new Map(live.actions.map((action) => [action.ref, action.inputSchema]));
1239
- const actions = candidate.actions.filter(
1240
- (action) => stableJsonHash(action.inputSchema) !== stableJsonHash(liveByRef.get(action.ref))
1241
- ).map((action) => ({
1242
- ref: action.ref,
1243
- inputSchema: action.inputSchema,
1244
- baseSchemaDigest: schemaDigest(liveByRef.get(action.ref))
986
+ var sortedActions = (surface) => [...surface.actions].sort((a, b) => a.ref < b.ref ? -1 : a.ref > b.ref ? 1 : 0);
987
+ var finishCompile = (input, report, plans) => {
988
+ const review = entropyReviewSignals({ ...input, report });
989
+ const previous = input.artifact;
990
+ const current = previous?.version === COMPILED_SURFACE_VERSION && previous.actions.length === 0 && previous.quarantined.length === 0 && stableJsonHash(previous.normalizations ?? []) === stableJsonHash(plans);
991
+ if (current || !previous && plans.length === 0) {
992
+ return { status: "converged", ...previous ? { artifact: previous } : {}, report, proposals: review };
993
+ }
994
+ const proposals = plans.map((plan) => ({
995
+ kind: "normal-form",
996
+ ref: plan.ref,
997
+ baseSchemaDigest: plan.baseSchemaDigest,
998
+ rules: plan.rules
1245
999
  }));
1246
- const candidateRefs = new Set(candidate.actions.map((action) => action.ref));
1247
- const quarantined = live.actions.filter((action) => !candidateRefs.has(action.ref)).map((action) => ({ ref: action.ref, baseSchemaDigest: schemaDigest(action.inputSchema) }));
1248
- const applied = [...previous?.applied ?? []];
1249
- const seen = new Set(applied.map((entry) => `${entry.kind}:${entry.ref}:${entry.detail}`));
1250
- for (const proposal of proposals) {
1251
- const { ref, detail } = proposalDetail(proposal);
1252
- const identity = `${proposal.kind}:${ref}:${detail}`;
1253
- if (seen.has(identity)) continue;
1254
- seen.add(identity);
1255
- applied.push({ kind: proposal.kind, ref, detail });
1256
- }
1257
- return {
1258
- version: COMPILED_SURFACE_VERSION,
1259
- metricVersion: ENTROPY_METRIC_VERSION,
1260
- actions,
1261
- quarantined,
1262
- applied: applied.slice(0, MAX_COMPILED_SURFACE_PROPOSALS),
1263
- gate,
1264
- evidenceDigest
1000
+ const gate = { passed: true, beforeScore: report.score, afterScore: report.score, delta: 0, reasons: [] };
1001
+ const artifact = {
1002
+ ...emptyCompiledSurface(),
1003
+ normalizations: plans,
1004
+ applied: plans.slice(0, MAX_COMPILED_SURFACE_PROPOSALS).map((plan) => ({ kind: "normal-form", ref: plan.ref, detail: `${plan.rules.length} proven rules` })),
1005
+ gate: { passed: true, beforeScore: report.score, afterScore: report.score, reasons: [] },
1006
+ evidenceDigest: stableJsonHash({ surface: entropySurfaceHash(input.surface), plans })
1265
1007
  };
1008
+ return { status: "compiled", artifact, report, after: report, proposals: [...proposals, ...review], gate };
1266
1009
  };
1267
1010
  var compileEntropySurface = (input) => {
1268
- const effective = applyCompiledSurface(input.surface, input.artifact);
1269
- const report = measureEntropy({
1270
- traces: input.traces,
1271
- surface: effective,
1272
- ...input.repairs ? { repairs: input.repairs } : {},
1273
- catalogDigest: input.catalogDigest ?? entropySurfaceHash(effective)
1274
- });
1275
- const proposals = proposeEntropyReductions({
1276
- report,
1277
- traces: input.traces,
1278
- surface: effective,
1279
- ...input.repairs ? { repairs: input.repairs } : {},
1280
- ...input.valueObservations ? { valueObservations: input.valueObservations } : {}
1281
- });
1282
- const auto = proposals.filter(
1283
- (proposal) => AUTO_APPLY_PROPOSAL_KINDS.includes(proposal.kind)
1284
- );
1285
- if (auto.length === 0) {
1286
- return {
1287
- status: "converged",
1288
- ...input.artifact ? { artifact: input.artifact } : {},
1289
- report,
1290
- proposals
1291
- };
1011
+ const report = measureEntropy({ ...input, catalogDigest: input.catalogDigest ?? entropySurfaceHash(input.surface) });
1012
+ const plans = [];
1013
+ for (const action of sortedActions(input.surface)) {
1014
+ const plan = deriveNormalFormPlan(action.ref, action.inputSchema);
1015
+ if (plan && plans.length < MAX_NORMAL_FORM_PLANS) plans.push(plan);
1292
1016
  }
1293
- const candidate = applyProposalsToSurface(effective, auto);
1294
- const after = measureEntropy({
1295
- traces: input.traces,
1296
- surface: candidate,
1297
- ...input.repairs ? { repairs: input.repairs } : {},
1298
- catalogDigest: entropySurfaceHash(candidate)
1299
- });
1300
- const scoreGate = evaluateGate(report, after);
1301
- const violations = replaySuccessfulCalls(
1302
- candidate,
1303
- effective,
1304
- input.traces,
1305
- touchedRefs(effective, candidate),
1306
- input.auditCalls
1307
- );
1308
- const reasons = [
1309
- ...scoreGate.reasons,
1310
- ...violations.map((violation) => `${violation.ref}: ${violation.reason}`)
1311
- ];
1312
- const gate = {
1313
- passed: scoreGate.passed && violations.length === 0,
1314
- beforeScore: report.score,
1315
- afterScore: after.score,
1316
- delta: scoreGate.delta,
1317
- reasons: scoreGate.passed && violations.length === 0 ? [] : reasons
1318
- };
1319
- if (gate.passed) {
1320
- const evidenceDigest = stableJsonHash({
1321
- traces: input.traces.length,
1322
- operations: report.totals.operations,
1323
- succeeded: report.totals.succeeded,
1324
- failed: report.totals.failed,
1325
- surface: entropySurfaceHash(effective),
1326
- repairs: input.repairs?.length ?? 0,
1327
- valueObservations: stableJsonHash(input.valueObservations ?? []),
1328
- auditCalls: stableJsonHash(input.auditCalls ?? [])
1329
- });
1330
- return {
1331
- status: "compiled",
1332
- artifact: artifactFromCandidate(
1333
- input.surface,
1334
- candidate,
1335
- auto,
1336
- { passed: true, beforeScore: report.score, afterScore: after.score, reasons: [] },
1337
- evidenceDigest,
1338
- input.artifact
1339
- ),
1340
- report,
1341
- after,
1342
- proposals,
1343
- gate
1344
- };
1345
- }
1346
- return {
1347
- status: "rejected",
1348
- ...input.artifact ? { artifact: input.artifact } : {},
1349
- report,
1350
- after,
1351
- proposals,
1352
- gate
1353
- };
1017
+ return finishCompile(input, report, plans);
1354
1018
  };
1355
- var yieldToLoop3 = () => new Promise((resolve) => setImmediate(resolve));
1356
1019
  var compileEntropySurfaceAsync = async (input) => {
1357
- const effective = applyCompiledSurface(input.surface, input.artifact);
1358
- const report = await measureEntropyAsync({
1359
- traces: input.traces,
1360
- surface: effective,
1361
- ...input.repairs ? { repairs: input.repairs } : {},
1362
- catalogDigest: input.catalogDigest ?? entropySurfaceHash(effective)
1363
- });
1364
- await yieldToLoop3();
1365
- const proposals = proposeEntropyReductions({
1366
- report,
1367
- traces: input.traces,
1368
- surface: effective,
1369
- ...input.repairs ? { repairs: input.repairs } : {},
1370
- ...input.valueObservations ? { valueObservations: input.valueObservations } : {}
1371
- });
1372
- const auto = proposals.filter(
1373
- (proposal) => AUTO_APPLY_PROPOSAL_KINDS.includes(proposal.kind)
1374
- );
1375
- if (auto.length === 0) {
1376
- return {
1377
- status: "converged",
1378
- ...input.artifact ? { artifact: input.artifact } : {},
1379
- report,
1380
- proposals
1381
- };
1382
- }
1383
- await yieldToLoop3();
1384
- const candidate = applyProposalsToSurface(effective, auto);
1385
- const after = await measureEntropyAsync({
1386
- traces: input.traces,
1387
- surface: candidate,
1388
- ...input.repairs ? { repairs: input.repairs } : {},
1389
- catalogDigest: entropySurfaceHash(candidate)
1390
- });
1391
- const scoreGate = evaluateGate(report, after);
1392
- await yieldToLoop3();
1393
- const violations = await replaySuccessfulCallsAsync(
1394
- candidate,
1395
- effective,
1396
- input.traces,
1397
- touchedRefs(effective, candidate),
1398
- input.auditCalls
1399
- );
1400
- const reasons = [
1401
- ...scoreGate.reasons,
1402
- ...violations.map((violation) => `${violation.ref}: ${violation.reason}`)
1403
- ];
1404
- const gate = {
1405
- passed: scoreGate.passed && violations.length === 0,
1406
- beforeScore: report.score,
1407
- afterScore: after.score,
1408
- delta: scoreGate.delta,
1409
- reasons: scoreGate.passed && violations.length === 0 ? [] : reasons
1410
- };
1411
- if (gate.passed) {
1412
- const [valueObservationsDigest, auditCallsDigest] = await Promise.all([
1413
- stableJsonHashArrayAsync(input.valueObservations ?? []),
1414
- stableJsonHashArrayAsync(input.auditCalls ?? [])
1415
- ]);
1416
- const evidenceDigest = stableJsonHash({
1417
- traces: input.traces.length,
1418
- operations: report.totals.operations,
1419
- succeeded: report.totals.succeeded,
1420
- failed: report.totals.failed,
1421
- surface: entropySurfaceHash(effective),
1422
- repairs: input.repairs?.length ?? 0,
1423
- valueObservations: valueObservationsDigest,
1424
- auditCalls: auditCallsDigest
1425
- });
1426
- return {
1427
- status: "compiled",
1428
- artifact: artifactFromCandidate(
1429
- input.surface,
1430
- candidate,
1431
- auto,
1432
- { passed: true, beforeScore: report.score, afterScore: after.score, reasons: [] },
1433
- evidenceDigest,
1434
- input.artifact
1435
- ),
1436
- report,
1437
- after,
1438
- proposals,
1439
- gate
1440
- };
1020
+ const report = await measureEntropyAsync({ ...input, catalogDigest: input.catalogDigest ?? entropySurfaceHash(input.surface) });
1021
+ const plans = [];
1022
+ let processed = 0;
1023
+ for (const action of sortedActions(input.surface)) {
1024
+ const plan = deriveNormalFormPlan(action.ref, action.inputSchema);
1025
+ if (plan && plans.length < MAX_NORMAL_FORM_PLANS) plans.push(plan);
1026
+ if (++processed % 32 === 0) await new Promise((resolve) => setImmediate(resolve));
1441
1027
  }
1442
- return {
1443
- status: "rejected",
1444
- ...input.artifact ? { artifact: input.artifact } : {},
1445
- report,
1446
- after,
1447
- proposals,
1448
- gate
1449
- };
1028
+ return finishCompile(input, report, plans);
1450
1029
  };
1451
1030
 
1452
1031
  // src/entropy/presentation.ts
@@ -1525,7 +1104,7 @@ var formatEntropyReviewNotice = (proposals) => {
1525
1104
  };
1526
1105
  var formatEntropyCompileNotice = (input) => {
1527
1106
  const review = input.reviewCount ? ` \xB7 ${input.reviewCount} suggestion${input.reviewCount === 1 ? "" : "s"} await review (/fabric entropy)` : "";
1528
- return `entropy: background optimization complete \xB7 ${scoreChange(input.beforeScore, input.afterScore)} \xB7 safety checks passed${review}`;
1107
+ return `entropy: background optimization complete \xB7 ${input.normalizations ?? 0} normal-form plans \xB7 canonical capabilities preserved \xB7 ${scoreChange(input.beforeScore, input.afterScore)} \xB7 safety checks passed${review}`;
1529
1108
  };
1530
1109
 
1531
1110
  // src/entropy/corpus.ts
@@ -1536,7 +1115,8 @@ var entropyTraceFromFabricTrace = (trace, model) => ({
1536
1115
  ref: operation.ref,
1537
1116
  args: operation.args,
1538
1117
  outcome: operation.outcome,
1539
- ...operation.failureStage !== void 0 ? { failureStage: operation.failureStage } : {}
1118
+ ...operation.failureStage !== void 0 ? { failureStage: operation.failureStage } : {},
1119
+ ...operation.normalization ? { normalization: operation.normalization } : {}
1540
1120
  })),
1541
1121
  taskKey: trace.phases[0] ?? DEFAULT_TASK_KEY2,
1542
1122
  ...model ? { model } : {}
@@ -1993,136 +1573,96 @@ var sessionWindowEvidence = (files) => {
1993
1573
 
1994
1574
  // src/entropy/trial.ts
1995
1575
  import { Value } from "typebox/value";
1996
- var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1997
1576
  var accepts = (schema, args) => {
1998
1577
  try {
1999
- return isRecord3(schema) && Value.Check(schema, args);
1578
+ return typeof schema === "object" && schema !== null && Value.Check(schema, args);
2000
1579
  } catch {
2001
1580
  return false;
2002
1581
  }
2003
1582
  };
2004
- var trialClassFields = {
2005
- "both-accept": "bothAccept",
2006
- "both-reject": "bothReject",
2007
- "tightening-cost": "tighteningCost",
2008
- "typed-failure-win": "typedFailureWin",
2009
- "quarantine-win": "quarantineWin",
2010
- "quarantine-cost": "quarantineCost"
2011
- };
2012
- var trialClassField = (trialClass) => trialClassFields[trialClass];
2013
- var classify = (operation, declaredAccepts, compiledAccepts, quarantined) => {
2014
- if (quarantined) {
2015
- if (!declaredAccepts) return "both-reject";
2016
- return operation.outcome === "succeeded" ? "quarantine-cost" : "quarantine-win";
2017
- }
2018
- if (!declaredAccepts) return "both-reject";
2019
- if (compiledAccepts) return "both-accept";
2020
- return operation.outcome === "succeeded" ? "tightening-cost" : "typed-failure-win";
2021
- };
2022
1583
  var runEntropyTrial = (input) => {
2023
- const effective = applyCompiledSurface(input.live, input.artifact);
2024
- const declaredByRef = new Map(
2025
- input.live.actions.map((action) => [action.ref, action.inputSchema])
2026
- );
2027
- const effectiveByRef = new Map(
2028
- effective.actions.map((action) => [action.ref, action.inputSchema])
2029
- );
2030
- const declaredReport = measureEntropy({
2031
- traces: input.traces,
2032
- surface: input.live,
2033
- catalogDigest: entropySurfaceHash(input.live)
2034
- });
2035
- const effectiveReport = measureEntropy({
2036
- traces: input.traces,
2037
- surface: effective,
2038
- catalogDigest: entropySurfaceHash(effective)
2039
- });
1584
+ const schemas = new Map(input.live.actions.map((action) => [action.ref, action.inputSchema]));
1585
+ const plans = /* @__PURE__ */ new Map();
1586
+ if (input.artifact?.version === COMPILED_SURFACE_VERSION) {
1587
+ for (const plan of input.artifact.normalizations ?? []) {
1588
+ if (provesNormalFormPlan(plan, schemas.get(plan.ref))) plans.set(plan.ref, plan);
1589
+ }
1590
+ }
1591
+ const report = measureEntropy({ traces: input.traces, surface: input.live, catalogDigest: entropySurfaceHash(input.live) });
2040
1592
  const totals = {
2041
1593
  operations: 0,
2042
1594
  bothAccept: 0,
2043
1595
  bothReject: 0,
1596
+ normalizationWin: 0,
1597
+ canonicalIdentityChecks: 0,
1598
+ canonicalIdentityCost: 0,
1599
+ idempotenceCost: 0,
2044
1600
  tighteningCost: 0,
2045
1601
  typedFailureWin: 0,
2046
1602
  quarantineWin: 0,
2047
1603
  quarantineCost: 0
2048
1604
  };
2049
- const divergenceCounts = /* @__PURE__ */ new Map();
2050
- const auditedArgsByRef = /* @__PURE__ */ new Map();
2051
- if (input.auditCalls) {
2052
- for (const call of input.auditCalls) {
2053
- if (declaredByRef.get(call.ref) === void 0) continue;
2054
- const bucket = auditedArgsByRef.get(call.ref) ?? [];
2055
- bucket.push(call.args);
2056
- auditedArgsByRef.set(call.ref, bucket);
1605
+ const divergences = /* @__PURE__ */ new Map();
1606
+ let plannedOperations = 0;
1607
+ const record = (ref, args) => {
1608
+ if (ref.startsWith("fabric.discovery.") || ref.startsWith("fabric.workflow.")) return;
1609
+ const schema = schemas.get(ref);
1610
+ if (schema === void 0) return;
1611
+ const plan = plans.get(ref);
1612
+ if (plan) plannedOperations++;
1613
+ const before = accepts(schema, args);
1614
+ const normalized = applyNormalFormPlan(ref, schema, args, plan);
1615
+ const after = accepts(schema, normalized.args);
1616
+ totals.operations++;
1617
+ if (before) {
1618
+ totals.canonicalIdentityChecks++;
1619
+ if (normalized.args !== args || normalized.witness) totals.canonicalIdentityCost++;
1620
+ }
1621
+ const twice = applyNormalFormPlan(ref, schema, normalized.args, plan);
1622
+ if (twice.args !== normalized.args || twice.witness) totals.idempotenceCost++;
1623
+ let trialClass;
1624
+ if (before && !after) {
1625
+ totals.tighteningCost++;
1626
+ trialClass = "tightening-cost";
1627
+ } else if (before) {
1628
+ totals.bothAccept++;
1629
+ trialClass = "both-accept";
1630
+ } else if (after && normalized.witness) {
1631
+ totals.normalizationWin++;
1632
+ trialClass = "normalization-win";
1633
+ } else {
1634
+ totals.bothReject++;
1635
+ trialClass = "both-reject";
1636
+ }
1637
+ if (trialClass !== "both-accept" && trialClass !== "both-reject") {
1638
+ const key = JSON.stringify([ref, trialClass]);
1639
+ const entry = divergences.get(key) ?? { ref, trialClass, count: 0 };
1640
+ entry.count++;
1641
+ divergences.set(key, entry);
2057
1642
  }
1643
+ };
1644
+ const auditedRefs = /* @__PURE__ */ new Set();
1645
+ for (const call of input.auditCalls ?? []) {
1646
+ auditedRefs.add(call.ref);
1647
+ record(call.ref, call.args);
2058
1648
  }
2059
1649
  for (const trace of input.traces) {
2060
1650
  for (const operation of trace.operations) {
2061
- if (operation.ref.startsWith("fabric.")) continue;
2062
- const declared = declaredByRef.get(operation.ref);
2063
- if (declared === void 0) continue;
2064
- if (auditedArgsByRef.has(operation.ref)) continue;
2065
- const compiled = effectiveByRef.get(operation.ref);
2066
- const quarantined = compiled === void 0;
2067
- const trialClass = classify(
2068
- operation,
2069
- accepts(declared, operation.args),
2070
- quarantined ? false : accepts(compiled, operation.args),
2071
- quarantined
2072
- );
2073
- totals.operations++;
2074
- totals[trialClassField(trialClass)]++;
2075
- if (trialClass !== "both-accept" && trialClass !== "both-reject") {
2076
- const key = `${operation.ref}\0${trialClass}`;
2077
- divergenceCounts.set(key, (divergenceCounts.get(key) ?? 0) + 1);
2078
- }
2079
- }
2080
- }
2081
- for (const ref of [...auditedArgsByRef.keys()].sort(compareCodeUnits)) {
2082
- const declared = declaredByRef.get(ref);
2083
- const compiled = effectiveByRef.get(ref);
2084
- const quarantined = compiled === void 0;
2085
- for (const args of auditedArgsByRef.get(ref)) {
2086
- const declaredAccepts = accepts(declared, args);
2087
- const trialClass = !declaredAccepts ? "both-reject" : quarantined ? "quarantine-cost" : accepts(compiled, args) ? "both-accept" : "tightening-cost";
2088
- totals.operations++;
2089
- totals[trialClassField(trialClass)]++;
2090
- if (trialClass !== "both-accept" && trialClass !== "both-reject") {
2091
- const key = `${ref}\0${trialClass}`;
2092
- divergenceCounts.set(key, (divergenceCounts.get(key) ?? 0) + 1);
2093
- }
1651
+ if (!auditedRefs.has(operation.ref)) record(operation.ref, operation.args);
2094
1652
  }
2095
1653
  }
2096
- const hasEntries = (input.artifact?.actions.length ?? 0) + (input.artifact?.quarantined.length ?? 0) > 0;
2097
- const costs = totals.tighteningCost + totals.quarantineCost;
2098
- const verdict = !hasEntries || totals.operations === 0 ? "no-evidence" : costs === 0 ? "clean" : "costly";
2099
- const divergences = [...divergenceCounts.entries()].map(([key, count]) => {
2100
- const [ref, trialClass] = key.split("\0");
2101
- return { ref, trialClass, count };
2102
- }).sort(
2103
- (left, right) => compareCodeUnits(left.ref, right.ref) || compareCodeUnits(left.trialClass, right.trialClass)
2104
- );
1654
+ const costs = totals.tighteningCost + totals.canonicalIdentityCost + totals.idempotenceCost;
2105
1655
  return {
2106
- verdict,
2107
- declaredScore: declaredReport.score,
2108
- effectiveScore: effectiveReport.score,
2109
- delta: effectiveReport.score - declaredReport.score,
1656
+ verdict: costs > 0 ? "costly" : plannedOperations === 0 ? "no-evidence" : "clean",
1657
+ declaredScore: report.score,
1658
+ effectiveScore: report.score,
1659
+ delta: 0,
2110
1660
  totals,
2111
- divergences
1661
+ divergences: [...divergences.values()].sort((a, b) => compareCodeUnits(a.ref, b.ref) || compareCodeUnits(a.trialClass, b.trialClass))
2112
1662
  };
2113
1663
  };
2114
1664
 
2115
1665
  export {
2116
- ENTROPY_METRIC_VERSION,
2117
- ENTROPY_WEIGHTS,
2118
- ENTROPY_INVOCATION_STAGES,
2119
- roundMetric,
2120
- compareCodeUnits,
2121
- shapeSignature,
2122
- signatureDistance,
2123
- shannonEntropyBits,
2124
- staticFreedomFromSchema,
2125
- trendFromScores,
2126
1666
  liveSurfaceSnapshot,
2127
1667
  entropySurfaceHash,
2128
1668
  surfaceFreedomReport,
@@ -2175,4 +1715,4 @@ export {
2175
1715
  formatEntropyCompileNotice,
2176
1716
  runEntropyTrial
2177
1717
  };
2178
- //# sourceMappingURL=chunk-UVUWHPAO.js.map
1718
+ //# sourceMappingURL=chunk-LLJXPPTA.js.map