@davesheffer/hunch 1.7.0 → 1.8.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 (71) hide show
  1. package/README.md +242 -0
  2. package/bench/constitution-exp03-v1.json +70 -0
  3. package/dist/cli/index.js +1630 -107
  4. package/dist/constitution/adapters.js +487 -0
  5. package/dist/constitution/behaviorAttestationBinding.js +17 -0
  6. package/dist/constitution/behaviorEvaluator.js +220 -0
  7. package/dist/constitution/behaviorProof.js +205 -0
  8. package/dist/constitution/behaviorWorkspace.js +124 -0
  9. package/dist/constitution/bootstrap.js +133 -0
  10. package/dist/constitution/canonical.js +51 -0
  11. package/dist/constitution/card.js +133 -0
  12. package/dist/constitution/compiler.js +176 -0
  13. package/dist/constitution/composition.js +101 -0
  14. package/dist/constitution/corpus.js +58 -0
  15. package/dist/constitution/delta.js +154 -0
  16. package/dist/constitution/disposition.js +141 -0
  17. package/dist/constitution/evaluator.js +435 -0
  18. package/dist/constitution/experiment.js +1007 -0
  19. package/dist/constitution/experimentRunner.js +344 -0
  20. package/dist/constitution/g2.js +291 -0
  21. package/dist/constitution/g2BehaviorAttestation.js +209 -0
  22. package/dist/constitution/g2BehaviorCandidates.js +703 -0
  23. package/dist/constitution/g2BehaviorDependencies.js +379 -0
  24. package/dist/constitution/g2BehaviorMaterialization.js +171 -0
  25. package/dist/constitution/g2BehaviorPolicyMaterializer.js +241 -0
  26. package/dist/constitution/g2CandidateAttestation.js +179 -0
  27. package/dist/constitution/g2Candidates.js +195 -0
  28. package/dist/constitution/g2Drills.js +122 -0
  29. package/dist/constitution/g3.js +511 -0
  30. package/dist/constitution/g3Conformance.js +132 -0
  31. package/dist/constitution/lifecycle.js +224 -0
  32. package/dist/constitution/mutation.js +262 -0
  33. package/dist/constitution/nodeTestEvidence.js +47 -0
  34. package/dist/constitution/plan.js +172 -0
  35. package/dist/constitution/policyRuntime.js +8 -0
  36. package/dist/constitution/proof.js +166 -0
  37. package/dist/constitution/repairPolicies.js +78 -0
  38. package/dist/constitution/replay.js +361 -0
  39. package/dist/constitution/replayCache.js +89 -0
  40. package/dist/constitution/replayWorker.js +34 -0
  41. package/dist/constitution/repository.js +533 -0
  42. package/dist/constitution/schema.js +545 -0
  43. package/dist/constitution/scorecard.js +106 -0
  44. package/dist/constitution/service.js +1211 -0
  45. package/dist/constitution/shadow.js +235 -0
  46. package/dist/constitution/sourceMutation.js +316 -0
  47. package/dist/constitution/structural.js +601 -0
  48. package/dist/core/autoreview.js +27 -3
  49. package/dist/core/dupdetect.js +10 -3
  50. package/dist/core/escalations.js +65 -0
  51. package/dist/core/events.js +61 -0
  52. package/dist/core/externalImports.js +24 -0
  53. package/dist/core/hookpolicy.js +3 -0
  54. package/dist/core/memorylog.js +69 -0
  55. package/dist/core/relativeImports.js +33 -0
  56. package/dist/core/repair.js +71 -0
  57. package/dist/core/reviewqueue.js +11 -0
  58. package/dist/core/stats.js +115 -0
  59. package/dist/extractors/git.js +120 -0
  60. package/dist/extractors/indexer.js +39 -38
  61. package/dist/extractors/nativeTreeSitter.js +108 -0
  62. package/dist/extractors/parse.js +5 -15
  63. package/dist/integrations/claudemd.js +8 -1
  64. package/dist/integrations/gitignore.js +8 -0
  65. package/dist/integrations/providers.js +32 -10
  66. package/dist/integrations/sync.js +16 -1
  67. package/dist/mcp/server.js +317 -1
  68. package/dist/synthesis/synthesize.js +8 -1
  69. package/dist/wiki/graph.js +301 -0
  70. package/dist/wiki/wiki.js +31 -3
  71. package/package.json +5 -1
@@ -0,0 +1,545 @@
1
+ import { z } from "zod";
2
+ import { ProvenanceSchema } from "../core/types.js";
3
+ export const POLICY_IR_VERSION = 1;
4
+ export const POLICY_EVALUATOR = { name: "hunch-graph-policy", version: "1.3.0" };
5
+ export const MUTATION_ENGINE = { name: "hunch-static-graph-controls", version: "5" };
6
+ export const EXECUTABLE_BEHAVIOR_IR_VERSION = 2;
7
+ export const BEHAVIOR_POLICY_EVALUATOR = { name: "hunch-executable-behavior", version: "1.0.0" };
8
+ export const BEHAVIOR_MUTATION_ENGINE = { name: "hunch-behavior-controls", version: "1" };
9
+ export const DataClassSchema = z.enum(["public", "private", "secret"]);
10
+ export const StructuralSymbolRefSchema = z.object({
11
+ file: z.string().min(1),
12
+ name: z.string().min(1),
13
+ kind: z.enum(["function", "method", "class", "interface", "type"]),
14
+ });
15
+ export const StructuralCallRefSchema = z.object({
16
+ file: z.string().min(1),
17
+ caller: z.string().min(1),
18
+ callee: z.string().min(1),
19
+ member: z.boolean().default(false),
20
+ });
21
+ export const StructuralImportRefSchema = z.object({
22
+ file: z.string().min(1),
23
+ specifier: z.string().min(1),
24
+ });
25
+ export const StructuralDeltaSchema = z.object({
26
+ id: z.string().regex(/^delta_[a-f0-9]{10}$/),
27
+ before_commit: z.string(),
28
+ after_commit: z.string().min(1),
29
+ files: z.array(z.string()).default([]),
30
+ symbols: z.object({
31
+ added: z.array(StructuralSymbolRefSchema).default([]),
32
+ removed: z.array(StructuralSymbolRefSchema).default([]),
33
+ moved: z.array(z.object({
34
+ from: z.string().min(1),
35
+ to: z.string().min(1),
36
+ name: z.string().min(1),
37
+ kind: StructuralSymbolRefSchema.shape.kind,
38
+ })).default([]),
39
+ }),
40
+ calls: z.object({
41
+ added: z.array(StructuralCallRefSchema).default([]),
42
+ removed: z.array(StructuralCallRefSchema).default([]),
43
+ }),
44
+ imports: z.object({
45
+ added: z.array(StructuralImportRefSchema).default([]),
46
+ removed: z.array(StructuralImportRefSchema).default([]),
47
+ }),
48
+ content_hash: z.string().min(1),
49
+ }).strict();
50
+ export const CandidateAlternativeSchema = z.object({
51
+ id: z.string().regex(/^cand_[a-f0-9]{10}$/),
52
+ basis: z.string().min(1),
53
+ reason: z.string().min(1),
54
+ assertion_hash: z.string().min(1),
55
+ }).strict();
56
+ export const PolicyScopeSchema = z.object({
57
+ repos: z.array(z.string()).default([]),
58
+ paths: z.array(z.string()).default([]),
59
+ components: z.array(z.string()).default([]),
60
+ }).default({ repos: [], paths: [], components: [] });
61
+ export const CandidateContextSchema = z.object({
62
+ alternatives: z.array(CandidateAlternativeSchema).default([]),
63
+ uncertainty: z.array(z.string().min(1)).default([]),
64
+ conflicts: z.array(z.string().regex(/^pol_[a-f0-9]{10}$/)).default([]),
65
+ incumbent: z.string().regex(/^pol_[a-f0-9]{10}$/).nullable().default(null),
66
+ scope_suggestion: PolicyScopeSchema.nullable().default(null),
67
+ counterexamples: z.array(z.string().min(1)).default([]),
68
+ }).default({ alternatives: [], uncertainty: [], conflicts: [], incumbent: null, scope_suggestion: null, counterexamples: [] });
69
+ export const EvidenceEventSchema = z.object({
70
+ id: z.string().regex(/^ev_[a-f0-9]{10}$/),
71
+ kind: z.enum(["correction", "review", "incident", "decision", "revert", "bug_fix", "test_failure", "instruction", "commit"]),
72
+ occurred_at: z.string().datetime({ offset: true }),
73
+ actor: z.string().optional(),
74
+ repository: z.string().min(1),
75
+ commit: z.string().optional(),
76
+ files: z.array(z.string()).default([]),
77
+ symbols: z.array(z.string()).default([]),
78
+ text_ref: z.string().optional(),
79
+ diff_ref: z.string().optional(),
80
+ related_records: z.array(z.string()).default([]),
81
+ data_class: DataClassSchema,
82
+ content_hash: z.string(),
83
+ structural_delta: StructuralDeltaSchema.optional(),
84
+ compiler: z.object({
85
+ status: z.enum(["eligible", "compiled", "covered", "uncompilable", "conflicted"]),
86
+ policy: z.string().nullable().default(null),
87
+ reason: z.string().default(""),
88
+ alternatives: z.array(CandidateAlternativeSchema).optional(),
89
+ uncertainty: z.array(z.string().min(1)).optional(),
90
+ conflicts: z.array(z.string().regex(/^pol_[a-f0-9]{10}$/)).optional(),
91
+ incumbent: z.string().regex(/^pol_[a-f0-9]{10}$/).nullable().optional(),
92
+ scope_suggestion: PolicyScopeSchema.nullable().optional(),
93
+ counterexamples: z.array(z.string().min(1)).optional(),
94
+ }).optional(),
95
+ provenance: ProvenanceSchema,
96
+ }).passthrough();
97
+ export const EvidenceImportItemSchema = z.object({
98
+ id: z.string().min(1).max(200),
99
+ kind: z.enum(["review", "instruction", "decision", "commit"]),
100
+ occurred_at: z.string().datetime({ offset: true }),
101
+ actor: z.string().min(1).max(200).optional(),
102
+ commit: z.string().regex(/^[0-9a-fA-F]{4,64}$/, "commit must be a hexadecimal object id").optional(),
103
+ files: z.array(z.string().min(1).max(1024)).max(64).default([]),
104
+ symbols: z.array(z.string().min(1).max(500)).max(64).default([]),
105
+ text: z.string().min(1).max(65_536).optional(),
106
+ text_ref: z.string().min(1).max(1024).optional(),
107
+ related_records: z.array(z.string().min(1).max(200)).max(64).default([]),
108
+ data_class: DataClassSchema.default("public"),
109
+ maintainer_confirmed: z.boolean().default(false),
110
+ }).strict().refine((item) => !!item.text || !!item.text_ref, {
111
+ message: "imported evidence needs text or text_ref",
112
+ });
113
+ export const EvidenceImportSchema = z.object({
114
+ version: z.literal(1),
115
+ source: z.enum(["pr_export", "review_export", "conversation_export"]),
116
+ items: z.array(EvidenceImportItemSchema).min(1).max(100),
117
+ }).strict();
118
+ export const PolicyStateSchema = z.enum([
119
+ "observed",
120
+ "drafted",
121
+ "compiled",
122
+ "uncompilable",
123
+ "validating",
124
+ "proposed",
125
+ "active_advisory",
126
+ "active_blocking",
127
+ "stale",
128
+ "repaired",
129
+ "superseded",
130
+ "retired",
131
+ "rejected",
132
+ ]);
133
+ export const PolicySelectorSchema = z.object({
134
+ selector: z.string().min(1),
135
+ });
136
+ export const PolicyRelationSchema = z.object({
137
+ edges: z.array(z.enum(["calls", "imports", "depends_on", "contains"])).min(1).default(["calls", "imports"]),
138
+ transitive: z.boolean().default(true),
139
+ max_depth: z.number().int().min(1).max(64).default(6),
140
+ });
141
+ const ExistsAssertionSchema = z.object({
142
+ kind: z.literal("exists"),
143
+ subject: PolicySelectorSchema,
144
+ });
145
+ const ReachAssertionSchema = z.object({
146
+ kind: z.enum(["reaches", "not-reaches"]),
147
+ subject: PolicySelectorSchema,
148
+ relation: PolicyRelationSchema,
149
+ object: PolicySelectorSchema,
150
+ });
151
+ const MustPassThroughAssertionSchema = z.object({
152
+ kind: z.literal("must-pass-through"),
153
+ subject: PolicySelectorSchema,
154
+ relation: PolicyRelationSchema,
155
+ via: PolicySelectorSchema,
156
+ object: PolicySelectorSchema,
157
+ });
158
+ const ExecutableBehaviorAssertionSchema = z.object({
159
+ kind: z.literal("executable-behavior"),
160
+ test: z.object({
161
+ file: z.string().min(1).max(1024).refine((file) => !file.includes("\\") && !file.includes("\0") && !file.split("/").some((part) => part === "" || part === "." || part === ".."), "behavior test file must be a safe relative POSIX path"),
162
+ name: z.string().min(1).max(500).refine((name) => !/[\0\r\n]/.test(name), "behavior test name cannot contain control line breaks"),
163
+ source_commit: z.string().regex(/^[a-f0-9]{40}$/),
164
+ source_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
165
+ }).strict(),
166
+ runner: z.enum(["node-test", "node-test-tsx"]),
167
+ attestation: z.object({
168
+ id: z.string().regex(/^g2behaviorattest_[a-f0-9]{10}$/),
169
+ content_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
170
+ candidate_id: z.string().regex(/^g2behavior_[a-f0-9]{10}$/),
171
+ candidate_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
172
+ replay_id: z.string().regex(/^g2behaviorreplay_[a-f0-9]{10}$/),
173
+ replay_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
174
+ }).strict(),
175
+ dependency_snapshot_ids: z.array(z.string().regex(/^g2deps_[a-f0-9]{10}$/)).min(1).max(3),
176
+ timeout_ms: z.number().int().min(1).max(120_000),
177
+ }).strict();
178
+ export const PolicyAssertionSchema = z.discriminatedUnion("kind", [
179
+ ExistsAssertionSchema,
180
+ ReachAssertionSchema,
181
+ MustPassThroughAssertionSchema,
182
+ ExecutableBehaviorAssertionSchema,
183
+ ]);
184
+ export const PolicyAuditEventSchema = z.object({
185
+ action: z.enum(["compiled", "enriched", "linked_exception", "proved", "approved_advisory", "approved_blocking", "demoted", "withdrawn", "retired", "rejected", "repaired"]),
186
+ actor_kind: z.enum(["system", "human"]),
187
+ actor: z.string().min(1),
188
+ at: z.string().datetime({ offset: true }),
189
+ reason: z.string().default(""),
190
+ proof: z.string().nullable().default(null),
191
+ });
192
+ export const PolicyAuthoritySchema = z.object({
193
+ kind: z.literal("human"),
194
+ actor: z.string().min(1),
195
+ event: z.string().min(1),
196
+ at: z.string().datetime({ offset: true }),
197
+ });
198
+ export const PolicySpecSchema = z.object({
199
+ id: z.string().regex(/^pol_[a-f0-9]{10}$/),
200
+ topic: z.string().min(1),
201
+ ir_version: z.union([z.literal(POLICY_IR_VERSION), z.literal(EXECUTABLE_BEHAVIOR_IR_VERSION)]),
202
+ revision: z.number().int().min(1),
203
+ state: PolicyStateSchema,
204
+ statement: z.string().min(1),
205
+ rationale: z.string().default(""),
206
+ scope: PolicyScopeSchema,
207
+ assertion: PolicyAssertionSchema,
208
+ severity: z.enum(["advisory", "warning", "blocking"]).default("warning"),
209
+ surfaces: z.array(z.enum(["pre_edit", "pre_commit", "ci", "mcp", "cli"])).default(["cli", "mcp"]),
210
+ authority: PolicyAuthoritySchema.nullable().default(null),
211
+ evidence: z.array(z.string()).default([]),
212
+ proof: z.string().nullable().default(null),
213
+ reversal_conditions: z.array(z.string()).default([]),
214
+ supersedes: z.string().nullable().default(null),
215
+ superseded_by: z.string().nullable().default(null),
216
+ exception_of: z.string().regex(/^pol_[a-f0-9]{10}$/).nullable().default(null),
217
+ valid_from: z.string().nullable().default(null),
218
+ valid_to: z.string().nullable().default(null),
219
+ data_class: DataClassSchema.default("public"),
220
+ limitations: z.array(z.string()).default([]),
221
+ candidate: CandidateContextSchema,
222
+ legacy_refs: z.array(z.string()).default([]),
223
+ audit: z.array(PolicyAuditEventSchema).default([]),
224
+ created_at: z.string().datetime({ offset: true }),
225
+ updated_at: z.string().datetime({ offset: true }),
226
+ provenance: ProvenanceSchema,
227
+ }).passthrough().superRefine((policy, context) => {
228
+ if (policy.assertion.kind === "executable-behavior" && policy.ir_version !== EXECUTABLE_BEHAVIOR_IR_VERSION) {
229
+ context.addIssue({ code: "custom", path: ["ir_version"], message: `executable-behavior requires Policy IR v${EXECUTABLE_BEHAVIOR_IR_VERSION}` });
230
+ }
231
+ if (policy.assertion.kind !== "executable-behavior" && policy.ir_version !== POLICY_IR_VERSION) {
232
+ context.addIssue({ code: "custom", path: ["ir_version"], message: `graph assertions require Policy IR v${POLICY_IR_VERSION}` });
233
+ }
234
+ });
235
+ export const PolicyCompositionMemberSchema = z.object({
236
+ policy_id: z.string().regex(/^pol_[a-f0-9]{10}$/),
237
+ policy_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
238
+ exception_of: z.string().regex(/^pol_[a-f0-9]{10}$/),
239
+ scope: PolicyScopeSchema,
240
+ }).strict();
241
+ export const PolicyCompositionBindingSchema = z.object({
242
+ kind: z.literal("parent_with_exceptions"),
243
+ root_policy_id: z.string().regex(/^pol_[a-f0-9]{10}$/),
244
+ root_policy_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
245
+ members: z.array(PolicyCompositionMemberSchema).min(1),
246
+ composite_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
247
+ }).strict();
248
+ export const PolicyEvaluationResultSchema = z.enum([
249
+ "satisfied",
250
+ "violated",
251
+ "not_applicable",
252
+ "unknown",
253
+ "error",
254
+ ]);
255
+ export const BehaviorExecutionSchema = z.object({
256
+ commit: z.string().regex(/^[a-f0-9]{40}$/),
257
+ workspace: z.object({
258
+ kind: z.enum(["staged", "working"]),
259
+ base_commit: z.string().regex(/^[a-f0-9]{40}$/),
260
+ snapshot_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
261
+ files: z.array(z.string().min(1)).max(10_000),
262
+ }).strict().optional(),
263
+ test: z.object({
264
+ file: z.string().min(1),
265
+ name: z.string().min(1),
266
+ source_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
267
+ }).strict(),
268
+ runner: z.enum(["node-test", "node-test-tsx"]),
269
+ dependency_snapshot_id: z.string().regex(/^g2deps_[a-f0-9]{10}$/).optional(),
270
+ exit_code: z.number().int().nullable(),
271
+ selected_event: z.enum(["passed", "failed"]).nullable(),
272
+ error_code: z.string().min(1).optional(),
273
+ execution_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
274
+ }).strict();
275
+ const HumanFixtureAttestationSchema = z.object({
276
+ actor: z.string().regex(/^(human|github|git):[^\s]+$/i, "fixture attestation requires an explicit human actor (human:, github:, or git:)"),
277
+ reason: z.string().min(1).max(2000),
278
+ }).strict();
279
+ export const ProofFixtureRefSchema = z.object({
280
+ kind: z.enum(["commit", "fixture", "event", "mutation"]),
281
+ ref: z.string().min(1),
282
+ label: z.string().min(1),
283
+ expected: PolicyEvaluationResultSchema,
284
+ attestation: HumanFixtureAttestationSchema.optional(),
285
+ });
286
+ const CorpusInputFixtureSchema = z.object({
287
+ ref: z.string().min(1).max(1024),
288
+ label: z.string().min(1).max(500),
289
+ }).strict();
290
+ const KnownGoodCorpusInputFixtureSchema = z.object({
291
+ ref: z.string().min(1).max(1024),
292
+ label: z.string().min(1).max(500),
293
+ attestation: HumanFixtureAttestationSchema.optional(),
294
+ }).strict();
295
+ export const ProofCorpusInputSchema = z.object({
296
+ known_bad: z.array(CorpusInputFixtureSchema).max(50).default([]),
297
+ known_good: z.array(KnownGoodCorpusInputFixtureSchema).max(50).default([]),
298
+ }).strict().refine((value) => value.known_bad.length + value.known_good.length > 0, {
299
+ message: "corpus import must declare at least one known-good or known-bad fixture",
300
+ });
301
+ const CorpusCommitFixtureSchema = z.object({
302
+ kind: z.literal("commit"),
303
+ ref: z.string().regex(/^[a-f0-9]{40}$/),
304
+ label: z.string().min(1).max(500),
305
+ expected: PolicyEvaluationResultSchema,
306
+ }).strict();
307
+ const KnownGoodCorpusCommitFixtureSchema = z.object({
308
+ kind: z.literal("commit"),
309
+ ref: z.string().regex(/^[a-f0-9]{40}$/),
310
+ label: z.string().min(1).max(500),
311
+ expected: z.literal("satisfied"),
312
+ attestation: HumanFixtureAttestationSchema.optional(),
313
+ }).strict();
314
+ export const ProofCorpusSchema = z.object({
315
+ id: z.string().regex(/^corpus_[a-f0-9]{10}$/),
316
+ content_hash: z.string().min(1),
317
+ policy_id: z.string().regex(/^pol_[a-f0-9]{10}$/),
318
+ policy_hash: z.string().min(1),
319
+ repository: z.string().min(1),
320
+ data_class: DataClassSchema,
321
+ known_bad: z.array(CorpusCommitFixtureSchema.extend({ expected: z.literal("violated") })).max(50).default([]),
322
+ known_good: z.array(KnownGoodCorpusCommitFixtureSchema).max(50).default([]),
323
+ created_at: z.string().datetime({ offset: true }),
324
+ }).strict();
325
+ export const ProofPlanSchema = z.object({
326
+ id: z.string().regex(/^plan_[a-f0-9]{10}$/),
327
+ content_hash: z.string().min(1),
328
+ policy_id: z.string().regex(/^pol_[a-f0-9]{10}$/),
329
+ policy_candidate_hash: z.string().min(1),
330
+ repository: z.string().min(1),
331
+ data_class: DataClassSchema,
332
+ source_commit: z.string().min(1),
333
+ valid_from_commit: z.string().min(1),
334
+ evaluator: z.object({ name: z.string().min(1), version: z.string().min(1) }),
335
+ mutation_engine: z.object({ name: z.string().min(1), version: z.string().min(1) }).optional(),
336
+ composition: PolicyCompositionBindingSchema.optional(),
337
+ corpus_manifest: z.object({
338
+ id: z.string().regex(/^corpus_[a-f0-9]{10}$/),
339
+ content_hash: z.string().min(1),
340
+ }).strict().optional(),
341
+ corpus: z.object({
342
+ current_baseline: ProofFixtureRefSchema,
343
+ accepted_history: z.object({
344
+ from: z.string().min(1),
345
+ to: z.string().min(1),
346
+ first_parent: z.boolean().default(true),
347
+ max_commits: z.number().int().min(0).max(500),
348
+ exclude: z.array(z.string()).default([]),
349
+ }),
350
+ known_bad: z.array(ProofFixtureRefSchema).default([]),
351
+ known_good: z.array(ProofFixtureRefSchema).default([]),
352
+ }),
353
+ mutations: z.array(z.object({
354
+ operator: z.string().min(1),
355
+ base: z.string().min(1),
356
+ expected: PolicyEvaluationResultSchema,
357
+ required: z.boolean().default(true),
358
+ })).default([]),
359
+ budgets: z.object({
360
+ max_commits: z.number().int().min(0).max(500),
361
+ max_mutations: z.number().int().min(0).max(100),
362
+ max_minutes: z.number().int().min(1).max(120),
363
+ }),
364
+ expected: z.array(z.object({
365
+ leg: z.enum(["current_baseline", "known_bad", "known_good", "accepted_history", "mutations"]),
366
+ result: PolicyEvaluationResultSchema.optional(),
367
+ classification_required: z.boolean().default(false),
368
+ })).default([]),
369
+ evidence_refs: z.array(z.string()).default([]),
370
+ limitations: z.array(z.string()).default([]),
371
+ created_at: z.string().datetime({ offset: true }),
372
+ }).strict();
373
+ export const PolicyEvaluationSchema = z.object({
374
+ policy_id: z.string(),
375
+ policy_revision: z.number().int(),
376
+ result: PolicyEvaluationResultSchema,
377
+ evaluator: z.object({ name: z.string(), version: z.string() }),
378
+ repository: z.object({
379
+ base: z.string().optional(),
380
+ head: z.string(),
381
+ graph_hash: z.string(),
382
+ }),
383
+ matches: z.array(z.object({
384
+ file: z.string(),
385
+ line: z.number().int().optional(),
386
+ symbol: z.string().optional(),
387
+ relation_path: z.array(z.string()).optional(),
388
+ })),
389
+ explanation: z.string(),
390
+ evidence_refs: z.array(z.string()),
391
+ behavior: BehaviorExecutionSchema.optional(),
392
+ composition: PolicyCompositionBindingSchema.extend({
393
+ selected_policy_id: z.string().regex(/^pol_[a-f0-9]{10}$/),
394
+ applicable_policy_ids: z.array(z.string().regex(/^pol_[a-f0-9]{10}$/)).min(1),
395
+ member_evaluation_hashes: z.record(z.string().regex(/^pol_[a-f0-9]{10}$/), z.string().regex(/^sha1:[a-f0-9]{40}$/)),
396
+ }).strict().optional(),
397
+ deterministic_hash: z.string(),
398
+ });
399
+ export const ProofClassSchema = z.enum(["P0", "P1", "P2", "P3", "P4", "P5"]);
400
+ export const EvaluationSummarySchema = z.object({
401
+ total: z.number().int().min(0),
402
+ satisfied: z.number().int().min(0),
403
+ violated: z.number().int().min(0),
404
+ not_applicable: z.number().int().min(0),
405
+ unknown: z.number().int().min(0),
406
+ error: z.number().int().min(0),
407
+ receipt_hashes: z.array(z.string()).default([]),
408
+ });
409
+ export const ReplayReceiptSchema = z.object({
410
+ leg: z.enum(["current_baseline", "known_bad", "known_good", "accepted_history"]),
411
+ commit: z.string().regex(/^[a-f0-9]{40}$/),
412
+ expected: PolicyEvaluationResultSchema.optional(),
413
+ policy_hash: z.string().min(1),
414
+ evaluator: z.object({ name: z.string().min(1), version: z.string().min(1) }),
415
+ result: PolicyEvaluationResultSchema,
416
+ graph_hash: z.string().min(1).optional(),
417
+ evaluation_hash: z.string().min(1).optional(),
418
+ error_code: z.string().min(1).optional(),
419
+ behavior: BehaviorExecutionSchema.optional(),
420
+ deterministic_hash: z.string().min(1),
421
+ }).strict();
422
+ export const HistoryDispositionClassificationSchema = z.enum([
423
+ "true_positive_actionable",
424
+ "true_positive_accepted_exception",
425
+ "false_positive_selector",
426
+ "false_positive_semantics",
427
+ "false_positive_stale",
428
+ "unknown_insufficient_parser",
429
+ ]);
430
+ export const HistoryDispositionSchema = z.object({
431
+ id: z.string().regex(/^disp_[a-f0-9]{10}$/),
432
+ content_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
433
+ policy_id: z.string().regex(/^pol_[a-f0-9]{10}$/),
434
+ proof_id: z.string().regex(/^proof_[a-f0-9]{10}$/),
435
+ policy_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
436
+ plan_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
437
+ commit: z.string().regex(/^[a-f0-9]{40}$/),
438
+ receipt_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
439
+ classification: HistoryDispositionClassificationSchema,
440
+ actor: z.string().regex(/^(human|github|git):[^\s]+$/i, "history disposition requires an explicit human actor (human:, github:, or git:)"),
441
+ reason: z.string().trim().min(1).max(2000),
442
+ supersedes: z.string().regex(/^disp_[a-f0-9]{10}$/).nullable().default(null),
443
+ data_class: DataClassSchema,
444
+ created_at: z.string().datetime({ offset: true }),
445
+ }).strict();
446
+ export const ShadowEvaluationRecordSchema = z.object({
447
+ record_type: z.literal("evaluation"),
448
+ id: z.string().regex(/^shadow_[a-f0-9]{10}$/),
449
+ content_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
450
+ policy_id: z.string().regex(/^pol_[a-f0-9]{10}$/),
451
+ proof_id: z.string().regex(/^proof_[a-f0-9]{10}$/),
452
+ policy_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
453
+ plan_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
454
+ evaluation: PolicyEvaluationSchema,
455
+ also_detected_by: z.array(z.string().regex(/^pol_[a-f0-9]{10}$/)).default([]),
456
+ latency_ms: z.number().nonnegative(),
457
+ data_class: DataClassSchema,
458
+ observed_at: z.string().datetime({ offset: true }),
459
+ }).strict();
460
+ export const ShadowDispositionSchema = z.object({
461
+ record_type: z.literal("disposition"),
462
+ id: z.string().regex(/^sdisp_[a-f0-9]{10}$/),
463
+ content_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
464
+ shadow_id: z.string().regex(/^shadow_[a-f0-9]{10}$/),
465
+ policy_id: z.string().regex(/^pol_[a-f0-9]{10}$/),
466
+ proof_id: z.string().regex(/^proof_[a-f0-9]{10}$/),
467
+ policy_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
468
+ evaluation_hash: z.string().regex(/^sha1:[a-f0-9]{40}$/),
469
+ classification: HistoryDispositionClassificationSchema,
470
+ actor: z.string().regex(/^(human|github|git):[^\s]+$/i, "shadow disposition requires an explicit human actor (human:, github:, or git:)"),
471
+ reason: z.string().trim().min(1).max(2000),
472
+ supersedes: z.string().regex(/^sdisp_[a-f0-9]{10}$/).nullable().default(null),
473
+ data_class: DataClassSchema,
474
+ created_at: z.string().datetime({ offset: true }),
475
+ }).strict();
476
+ export const ShadowRecordSchema = z.discriminatedUnion("record_type", [ShadowEvaluationRecordSchema, ShadowDispositionSchema]);
477
+ export const MutationReceiptSchema = z.object({
478
+ id: z.string().regex(/^mut_[a-f0-9]{10}$/),
479
+ kind: z.enum(["primary", "control"]),
480
+ operator: z.string().min(1),
481
+ required: z.boolean(),
482
+ engine: z.object({ name: z.string().min(1), version: z.string().min(1) }),
483
+ policy_hash: z.string().min(1),
484
+ base_commit: z.string().min(1),
485
+ base_graph_hash: z.string().min(1),
486
+ mutated_graph_hash: z.string().min(1).optional(),
487
+ expected: PolicyEvaluationResultSchema,
488
+ result: PolicyEvaluationResultSchema,
489
+ passed: z.boolean(),
490
+ parseability: z.enum(["parseable", "unparseable", "not_applicable"]),
491
+ graph_diff: z.object({
492
+ added_symbols: z.array(z.string()).default([]),
493
+ removed_symbols: z.array(z.string()).default([]),
494
+ added_edges: z.array(z.string()).default([]),
495
+ removed_edges: z.array(z.string()).default([]),
496
+ }).strict(),
497
+ parser_control: z.object({
498
+ source_hash: z.string().min(1),
499
+ observed_target_calls: z.array(z.string()).default([]),
500
+ observed_target_imports: z.array(z.string()).default([]),
501
+ }).strict().optional(),
502
+ source_patch: z.object({
503
+ files: z.array(z.string()).min(1),
504
+ diff: z.string().min(1).max(65536),
505
+ diff_hash: z.string().min(1),
506
+ }).strict().optional(),
507
+ evaluation_hash: z.string().min(1).optional(),
508
+ error_code: z.string().min(1).optional(),
509
+ behavior: BehaviorExecutionSchema.optional(),
510
+ deterministic_hash: z.string().min(1),
511
+ }).strict();
512
+ const MutationControlSummarySchema = z.object({
513
+ total: z.number().int().min(0),
514
+ passed: z.number().int().min(0),
515
+ failed: z.number().int().min(0),
516
+ receipt_hashes: z.array(z.string()).default([]),
517
+ }).strict();
518
+ const ProjectChecksSchema = z.object({
519
+ build: z.enum(["not_run", "passed", "failed", "error"]).default("not_run"),
520
+ test: z.enum(["not_run", "passed", "failed", "error"]).default("not_run"),
521
+ required_for_evaluator_sensitivity: z.boolean().default(false),
522
+ }).strict();
523
+ export const PolicyProofSchema = z.object({
524
+ id: z.string().regex(/^proof_[a-f0-9]{10}$/),
525
+ plan_hash: z.string(),
526
+ policy_hash: z.string(),
527
+ evaluator: z.object({ name: z.string(), version: z.string() }),
528
+ mutation_engine: z.object({ name: z.string(), version: z.string() }).optional(),
529
+ composition: PolicyCompositionBindingSchema.optional(),
530
+ generated_at: z.string().datetime({ offset: true }),
531
+ current: EvaluationSummarySchema,
532
+ known_bad: EvaluationSummarySchema,
533
+ known_good: EvaluationSummarySchema,
534
+ accepted_history: EvaluationSummarySchema.extend({ classified_hits: z.array(z.string()).default([]) }),
535
+ mutations: EvaluationSummarySchema.extend({ operator_coverage: z.record(z.string(), z.number().int().min(0)).default({}) }),
536
+ replay_receipts: z.array(ReplayReceiptSchema).default([]),
537
+ mutation_receipts: z.array(MutationReceiptSchema).default([]),
538
+ mutation_controls: MutationControlSummarySchema.default({ total: 0, passed: 0, failed: 0, receipt_hashes: [] }),
539
+ project_checks: ProjectChecksSchema.default({ build: "not_run", test: "not_run", required_for_evaluator_sensitivity: false }),
540
+ limitations: z.array(z.string()).default([]),
541
+ proof_class: ProofClassSchema,
542
+ artifact_hashes: z.record(z.string(), z.string()).default({}),
543
+ data_class: DataClassSchema,
544
+ }).passthrough();
545
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1,106 @@
1
+ import { z } from "zod";
2
+ import { canonicalHash, canonicalJson } from "./canonical.js";
3
+ import { PolicyAssertionSchema } from "./schema.js";
4
+ const ScopeSchema = z.object({
5
+ repos: z.array(z.string()).default([]),
6
+ paths: z.array(z.string()).default([]),
7
+ components: z.array(z.string()).default([]),
8
+ }).strict();
9
+ const CompilerOutcomeSchema = z.object({
10
+ outcome: z.enum(["assertion", "uncompilable", "conflicted", "covered"]),
11
+ assertion: PolicyAssertionSchema.optional(),
12
+ scope: ScopeSchema.optional(),
13
+ uncertainty: z.array(z.string()).default([]),
14
+ conflicts: z.array(z.string()).default([]),
15
+ incumbent: z.string().nullable().default(null),
16
+ reason: z.string().default(""),
17
+ }).strict().superRefine((value, context) => {
18
+ if (value.outcome === "assertion" && (!value.assertion || !value.scope)) {
19
+ context.addIssue({ code: "custom", message: "assertion outcomes require exact assertion and scope" });
20
+ }
21
+ if (value.outcome !== "assertion" && (value.assertion || value.scope)) {
22
+ context.addIssue({ code: "custom", message: "non-assertion outcomes cannot smuggle assertion semantics" });
23
+ }
24
+ });
25
+ export const CompilerCaseBankSchema = z.object({
26
+ version: z.literal(1),
27
+ experiment: z.literal("EXP-03"),
28
+ threshold: z.number().min(0).max(1).default(0.7),
29
+ preregistered_metric: z.literal("intended supported assertion or honest non-minting classification"),
30
+ cases: z.array(z.object({
31
+ id: z.string().regex(/^exp03_[a-z0-9_]+$/),
32
+ family: z.string().min(1),
33
+ evidence: z.string().min(1),
34
+ expected: CompilerOutcomeSchema,
35
+ actual: CompilerOutcomeSchema,
36
+ }).strict()).min(20),
37
+ }).strict();
38
+ function semanticOutcome(value) {
39
+ return value.outcome === "assertion"
40
+ ? { outcome: value.outcome, assertion: value.assertion, scope: value.scope }
41
+ : { outcome: value.outcome, conflicts: [...value.conflicts].sort(), incumbent: value.incumbent };
42
+ }
43
+ function wilson(successes, total) {
44
+ if (!total)
45
+ return { low: 0, high: 0 };
46
+ const z95 = 1.959963984540054;
47
+ const p = successes / total;
48
+ const denominator = 1 + (z95 * z95) / total;
49
+ const centre = p + (z95 * z95) / (2 * total);
50
+ const spread = z95 * Math.sqrt((p * (1 - p) + (z95 * z95) / (4 * total)) / total);
51
+ return { low: (centre - spread) / denominator, high: (centre + spread) / denominator };
52
+ }
53
+ /** Score only exact semantic outcomes. Wording/reason text is diagnostic and
54
+ * never rescues a wrong assertion. An expected refusal that becomes any
55
+ * assertion is counted separately as a silent semantic substitution. */
56
+ export function scoreCompilerCaseBank(raw) {
57
+ const bank = CompilerCaseBankSchema.parse(raw);
58
+ const details = bank.cases.map((item) => {
59
+ const semanticMatch = canonicalJson(semanticOutcome(item.expected)) === canonicalJson(semanticOutcome(item.actual));
60
+ const uncertaintyMatch = item.actual.uncertainty.length >= item.expected.uncertainty.length;
61
+ const correct = semanticMatch && uncertaintyMatch;
62
+ const silentSubstitution = item.expected.outcome !== "assertion" && item.actual.outcome === "assertion";
63
+ return {
64
+ id: item.id,
65
+ family: item.family,
66
+ correct,
67
+ silent_substitution: silentSubstitution,
68
+ reason: correct ? "exact intended semantics" : silentSubstitution ? "unsupported meaning was silently substituted" : "actual compiler classification differs from the reviewed expectation",
69
+ };
70
+ });
71
+ const numerator = details.filter((item) => item.correct).length;
72
+ const denominator = details.length;
73
+ const rate = numerator / denominator;
74
+ const byOutcome = Object.fromEntries(["assertion", "uncompilable", "conflicted", "covered"].map((outcome) => {
75
+ const matching = bank.cases.map((item, index) => ({ item, detail: details[index] })).filter(({ item }) => item.expected.outcome === outcome);
76
+ return [outcome, { total: matching.length, correct: matching.filter(({ detail }) => detail.correct).length }];
77
+ }));
78
+ const body = {
79
+ experiment: "EXP-03",
80
+ denominator,
81
+ numerator,
82
+ incorrect: denominator - numerator,
83
+ silent_semantic_substitutions: details.filter((item) => item.silent_substitution).length,
84
+ rate,
85
+ threshold: bank.threshold,
86
+ risk_difference_vs_threshold: rate - bank.threshold,
87
+ wilson_95: wilson(numerator, denominator),
88
+ passed: rate >= bank.threshold && details.every((item) => !item.silent_substitution),
89
+ by_outcome: byOutcome,
90
+ cases: details,
91
+ };
92
+ return { ...body, deterministic_hash: canonicalHash(body) };
93
+ }
94
+ export function renderCompilerScorecard(card) {
95
+ const pct = (value) => `${(value * 100).toFixed(1)}%`;
96
+ return [
97
+ `EXP-03 Intent Compiler scorecard — ${card.passed ? "PASS" : "FAIL"}`,
98
+ ` primary: ${card.numerator}/${card.denominator} (${pct(card.rate)}) exact or honest outcomes`,
99
+ ` threshold: ${pct(card.threshold)} · risk difference: ${(card.risk_difference_vs_threshold * 100).toFixed(1)} pp`,
100
+ ` Wilson 95% interval: ${pct(card.wilson_95.low)}–${pct(card.wilson_95.high)}`,
101
+ ` silent semantic substitutions: ${card.silent_semantic_substitutions}`,
102
+ ...Object.entries(card.by_outcome).map(([outcome, result]) => ` ${outcome}: ${result.correct}/${result.total}`),
103
+ ` receipt: ${card.deterministic_hash}`,
104
+ ].join("\n");
105
+ }
106
+ //# sourceMappingURL=scorecard.js.map