@davesheffer/hunch 1.7.0 → 1.7.1

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 (63) hide show
  1. package/README.md +214 -0
  2. package/bench/constitution-exp03-v1.json +70 -0
  3. package/dist/cli/index.js +1203 -24
  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 +948 -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 +115 -0
  31. package/dist/constitution/lifecycle.js +189 -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/replay.js +361 -0
  38. package/dist/constitution/replayCache.js +89 -0
  39. package/dist/constitution/replayWorker.js +34 -0
  40. package/dist/constitution/repository.js +533 -0
  41. package/dist/constitution/schema.js +545 -0
  42. package/dist/constitution/scorecard.js +106 -0
  43. package/dist/constitution/service.js +1149 -0
  44. package/dist/constitution/shadow.js +235 -0
  45. package/dist/constitution/sourceMutation.js +316 -0
  46. package/dist/constitution/structural.js +601 -0
  47. package/dist/core/autoreview.js +27 -3
  48. package/dist/core/dupdetect.js +10 -3
  49. package/dist/core/events.js +61 -0
  50. package/dist/core/externalImports.js +24 -0
  51. package/dist/core/hookpolicy.js +3 -0
  52. package/dist/core/relativeImports.js +33 -0
  53. package/dist/core/stats.js +115 -0
  54. package/dist/extractors/git.js +81 -0
  55. package/dist/extractors/indexer.js +39 -38
  56. package/dist/extractors/nativeTreeSitter.js +108 -0
  57. package/dist/extractors/parse.js +5 -15
  58. package/dist/integrations/claudemd.js +8 -1
  59. package/dist/integrations/gitignore.js +8 -0
  60. package/dist/integrations/providers.js +32 -10
  61. package/dist/integrations/sync.js +16 -1
  62. package/dist/mcp/server.js +284 -0
  63. package/package.json +5 -1
@@ -0,0 +1,487 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { readFileSync, statSync } from "node:fs";
3
+ import { basename, resolve } from "node:path";
4
+ import { shortHash } from "../core/ids.js";
5
+ import { parseDocAnchors } from "../core/docanchors.js";
6
+ import { toPosixTarget } from "../core/paths.js";
7
+ import { commitMeta, revExists, revParse } from "../extractors/git.js";
8
+ import { durationCutoff } from "./bootstrap.js";
9
+ import { canonicalHash } from "./canonical.js";
10
+ import { EvidenceEventSchema, EvidenceImportSchema, } from "./schema.js";
11
+ const MAX_INSTRUCTION_FILES = 64;
12
+ const MAX_INSTRUCTION_FILE_BYTES = 512 * 1024;
13
+ const MAX_INSTRUCTION_TOTAL_BYTES = 2 * 1024 * 1024;
14
+ const MAX_INSTRUCTION_HISTORY_COMMITS = 128;
15
+ const MAX_IMPORT_FILE_BYTES = 2 * 1024 * 1024;
16
+ function homeView(opts) {
17
+ return { publicOnly: opts.publicOnly, privateOnly: opts.privateOnly };
18
+ }
19
+ function normalizeRepoFile(file) {
20
+ const normalized = toPosixTarget(file.trim());
21
+ if (!normalized || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized) || normalized.split("/").includes("..")) {
22
+ throw new Error(`evidence file path must be repository-relative: ${file}`);
23
+ }
24
+ return normalized;
25
+ }
26
+ function coverageCompiler(related, policies, constraints, unsupportedReason) {
27
+ const refs = new Set(related);
28
+ const policy = policies.find((candidate) => refs.has(candidate.id)
29
+ || candidate.legacy_refs.some((ref) => refs.has(ref))
30
+ || candidate.evidence.some((ref) => refs.has(ref)));
31
+ if (policy)
32
+ return { status: "covered", policy: policy.id, reason: "Imported evidence explicitly relates to an existing deterministic Policy IR record." };
33
+ const constraint = constraints.find((candidate) => candidate.status === "active" && refs.has(candidate.id));
34
+ if (constraint)
35
+ return { status: "covered", policy: null, reason: "Imported evidence explicitly relates to an active deterministic legacy Constraint." };
36
+ return { status: "uncompilable", policy: null, reason: unsupportedReason };
37
+ }
38
+ function privateOnlyRelatedRef(store, repository, ref) {
39
+ if (ref.startsWith("pol_")) {
40
+ return !!repository.getPolicy(ref, { privateOnly: true }) && !repository.getPolicy(ref, { publicOnly: true });
41
+ }
42
+ const kind = ref.startsWith("dec_") ? "decisions"
43
+ : ref.startsWith("bug_") ? "bugs"
44
+ : ref.startsWith("con_") ? "constraints"
45
+ : null;
46
+ return !!kind && !!store.getPrivateRec(kind, ref) && !store.json.get(kind, ref);
47
+ }
48
+ function instructionFile(file) {
49
+ return /(^|\/)(AGENTS|CLAUDE|GEMINI)\.md$/i.test(file)
50
+ || /^\.github\/copilot-instructions\.md$/i.test(file)
51
+ || /^\.(cursor|windsurf)\/rules\/.+\.(md|mdc)$/i.test(file)
52
+ || /^(docs\/)?(adr|adrs|decisions)\/.+\.md$/i.test(file);
53
+ }
54
+ function committedInstructionFiles(root) {
55
+ try {
56
+ const raw = execFileSync("git", ["-C", root, "ls-tree", "-r", "-z", "--name-only", "HEAD"], {
57
+ encoding: "buffer",
58
+ maxBuffer: 16 * 1024 * 1024,
59
+ stdio: ["ignore", "pipe", "ignore"],
60
+ });
61
+ return raw.toString("utf8").split("\0").filter(instructionFile).sort();
62
+ }
63
+ catch {
64
+ throw new Error("instruction ingestion could not enumerate committed repository files");
65
+ }
66
+ }
67
+ function committedFileAt(root, revision, file, maxBytes) {
68
+ try {
69
+ const object = `${revision}:${file}`;
70
+ const size = Number(execFileSync("git", ["-C", root, "cat-file", "-s", object], {
71
+ encoding: "utf8",
72
+ stdio: ["ignore", "pipe", "ignore"],
73
+ }).trim());
74
+ if (!Number.isFinite(size) || size < 0 || size > maxBytes)
75
+ return null;
76
+ return execFileSync("git", ["-C", root, "cat-file", "blob", object], {
77
+ encoding: "utf8",
78
+ maxBuffer: maxBytes + 1,
79
+ stdio: ["ignore", "pipe", "ignore"],
80
+ });
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ }
86
+ function authoredInstructionContent(source) {
87
+ let authored = source.replace(/<!-- HUNCH:START[^]*?<!-- HUNCH:END -->/g, "");
88
+ authored = authored.replace(/^---\r?\n[^]*?\r?\n---\r?\n?/, "");
89
+ authored = authored.replace(/^# Copilot instructions\s*/i, "");
90
+ const substantive = authored.replace(/^\s{0,3}#{1,6}\s+.*$/gm, "").trim();
91
+ return substantive ? authored.trim() : null;
92
+ }
93
+ function authoredInstructionCommit(root, file, sourceHash) {
94
+ let commits;
95
+ try {
96
+ commits = execFileSync("git", [
97
+ "-C", root,
98
+ "log",
99
+ `--max-count=${MAX_INSTRUCTION_HISTORY_COMMITS}`,
100
+ "--format=%H",
101
+ "--",
102
+ file,
103
+ ], {
104
+ encoding: "utf8",
105
+ maxBuffer: 1024 * 1024,
106
+ stdio: ["ignore", "pipe", "ignore"],
107
+ }).split(/\r?\n/).filter(Boolean);
108
+ }
109
+ catch {
110
+ return null;
111
+ }
112
+ let introducedAt = null;
113
+ for (const commit of commits) {
114
+ const blob = committedFileAt(root, commit, file, MAX_INSTRUCTION_FILE_BYTES);
115
+ const authored = blob == null ? null : authoredInstructionContent(blob);
116
+ if (authored == null || canonicalHash(authored) !== sourceHash)
117
+ break;
118
+ introducedAt = commit;
119
+ }
120
+ return introducedAt;
121
+ }
122
+ function instructionEvents(root, dataClass, policies, constraints) {
123
+ const files = committedInstructionFiles(root);
124
+ const selected = files.slice(0, MAX_INSTRUCTION_FILES);
125
+ let excluded = files.length - selected.length;
126
+ let totalBytes = 0;
127
+ const pending = [];
128
+ for (const file of selected) {
129
+ const committed = committedFileAt(root, "HEAD", file, MAX_INSTRUCTION_FILE_BYTES);
130
+ const source = committed == null ? null : authoredInstructionContent(committed);
131
+ if (source == null || totalBytes + Buffer.byteLength(source, "utf8") > MAX_INSTRUCTION_TOTAL_BYTES) {
132
+ excluded++;
133
+ continue;
134
+ }
135
+ totalBytes += Buffer.byteLength(source, "utf8");
136
+ const sourceHash = canonicalHash(source);
137
+ const introducedAt = authoredInstructionCommit(root, file, sourceHash);
138
+ if (!introducedAt || !revExists(introducedAt, root)) {
139
+ excluded++;
140
+ continue;
141
+ }
142
+ const commit = revParse(`${introducedAt}^{commit}`, root);
143
+ const meta = commitMeta(commit, root);
144
+ if (!meta || !Number.isFinite(Date.parse(meta.date))) {
145
+ excluded++;
146
+ continue;
147
+ }
148
+ const anchors = parseDocAnchors(source);
149
+ const related = [...new Set(anchors.map((anchor) => anchor.pin).filter((pin) => !!pin))].sort();
150
+ const contentHash = canonicalHash({
151
+ kind: "instruction",
152
+ file,
153
+ commit,
154
+ source_hash: sourceHash,
155
+ anchors,
156
+ data_class: dataClass,
157
+ });
158
+ const event = EvidenceEventSchema.parse({
159
+ id: `ev_${shortHash(`instruction:${contentHash}`)}`,
160
+ kind: "instruction",
161
+ occurred_at: meta.date,
162
+ actor: meta.author,
163
+ repository: basename(root),
164
+ commit,
165
+ files: [file],
166
+ symbols: [],
167
+ text_ref: file,
168
+ diff_ref: `git:${commit}:${file}`,
169
+ related_records: related,
170
+ data_class: dataClass,
171
+ content_hash: contentHash,
172
+ compiler: coverageCompiler(related, policies, constraints, "Committed instruction/ADR content was hash-normalized, but it declares no exact supported structural assertion tied to an existing guard."),
173
+ provenance: {
174
+ source: "extracted",
175
+ confidence: 0.7,
176
+ evidence: [file, commit, sourceHash],
177
+ last_verified: meta.date,
178
+ },
179
+ });
180
+ pending.push({ occurredAt: meta.date, event, private: dataClass !== "public" });
181
+ }
182
+ return { scanned: files.length, excluded, pending };
183
+ }
184
+ function importEvents(root, file, store, repository, opts, policies, constraints, publicPolicies, publicConstraints) {
185
+ const target = resolve(root, file);
186
+ let raw;
187
+ try {
188
+ const size = statSync(target).size;
189
+ if (size > MAX_IMPORT_FILE_BYTES)
190
+ throw new Error("too large");
191
+ raw = readFileSync(target, "utf8");
192
+ }
193
+ catch {
194
+ throw new Error(`evidence import ${basename(file)} is unreadable or exceeds ${MAX_IMPORT_FILE_BYTES} bytes`);
195
+ }
196
+ let parsed;
197
+ try {
198
+ parsed = EvidenceImportSchema.parse(JSON.parse(raw));
199
+ }
200
+ catch (error) {
201
+ throw new Error(`invalid evidence import ${basename(file)}: ${error.message}`);
202
+ }
203
+ const pending = [];
204
+ for (const item of parsed.items) {
205
+ let dataClass = item.data_class;
206
+ if (opts.privateOnly && dataClass === "public")
207
+ dataClass = "private";
208
+ if (opts.publicOnly && dataClass !== "public") {
209
+ throw new Error(`evidence import ${basename(file)} contains ${dataClass} item ${item.id}; refusing public-only ingestion`);
210
+ }
211
+ if (dataClass !== "public" && !store.hasPrivate) {
212
+ throw new Error(`evidence import ${basename(file)} needs a configured private overlay for ${dataClass} item ${item.id}`);
213
+ }
214
+ let commit;
215
+ if (item.commit) {
216
+ if (!revExists(item.commit, root))
217
+ throw new Error(`evidence import item ${item.id} commit ${item.commit} does not resolve`);
218
+ commit = revParse(`${item.commit}^{commit}`, root);
219
+ }
220
+ const files = [...new Set(item.files.map(normalizeRepoFile))].sort();
221
+ const symbols = [...new Set(item.symbols)].sort();
222
+ const related = [...new Set(item.related_records)].sort();
223
+ if (dataClass === "public") {
224
+ const privateRef = related.find((ref) => privateOnlyRelatedRef(store, repository, ref));
225
+ if (privateRef)
226
+ throw new Error(`public evidence import item ${item.id} references private-only record ${privateRef}`);
227
+ }
228
+ const textHash = item.text ? canonicalHash(item.text) : undefined;
229
+ const textRef = item.text_ref ?? `export:${parsed.source}:${item.id}`;
230
+ const body = {
231
+ source: parsed.source,
232
+ external_id: item.id,
233
+ kind: item.kind,
234
+ occurred_at: item.occurred_at,
235
+ actor: item.actor,
236
+ commit,
237
+ files,
238
+ symbols,
239
+ text_ref: textRef,
240
+ text_hash: textHash,
241
+ related_records: related,
242
+ data_class: dataClass,
243
+ maintainer_confirmed: item.maintainer_confirmed,
244
+ };
245
+ const contentHash = canonicalHash(body);
246
+ const event = EvidenceEventSchema.parse({
247
+ id: `ev_${shortHash(`${parsed.source}:${contentHash}`)}`,
248
+ kind: item.kind,
249
+ occurred_at: item.occurred_at,
250
+ ...(item.actor ? { actor: item.actor } : {}),
251
+ repository: basename(root),
252
+ ...(commit ? { commit, diff_ref: `git:${commit}` } : {}),
253
+ files,
254
+ symbols,
255
+ text_ref: textRef,
256
+ related_records: related,
257
+ data_class: dataClass,
258
+ content_hash: contentHash,
259
+ compiler: coverageCompiler(related, dataClass === "public" ? publicPolicies : policies, dataClass === "public" ? publicConstraints : constraints, item.maintainer_confirmed
260
+ ? "Maintainer-confirmed review/instruction evidence was normalized, but no exact supported structural assertion or existing deterministic guard is linked."
261
+ : "External review/conversation evidence is not maintainer-confirmed and cannot become a policy candidate."),
262
+ provenance: {
263
+ source: item.maintainer_confirmed ? "human_confirmed+imported" : "imported",
264
+ confidence: item.maintainer_confirmed ? 1 : 0.5,
265
+ evidence: [parsed.source, basename(file), item.id, ...(commit ? [commit] : []), ...(textHash ? [textHash] : [])],
266
+ last_verified: item.occurred_at,
267
+ },
268
+ });
269
+ pending.push({ occurredAt: item.occurred_at, event, private: dataClass !== "public" });
270
+ }
271
+ return { scanned: parsed.items.length, pending };
272
+ }
273
+ function limit(value) {
274
+ if (value == null || !Number.isFinite(value))
275
+ return 100;
276
+ return Math.max(1, Math.min(200, Math.trunc(value)));
277
+ }
278
+ function exactRecords(store, opts) {
279
+ if (opts.publicOnly && opts.privateOnly)
280
+ throw new Error("choose only one of publicOnly or privateOnly");
281
+ if (opts.privateOnly) {
282
+ if (!store.hasPrivate)
283
+ throw new Error("private evidence ingestion needs a configured Hunch private overlay");
284
+ return { constraints: store.recsInHome("constraints", "private"), bugs: store.recsInHome("bugs", "private") };
285
+ }
286
+ if (opts.publicOnly)
287
+ return { constraints: store.json.loadAll("constraints"), bugs: store.json.loadAll("bugs") };
288
+ return { constraints: store.recs("constraints"), bugs: store.recs("bugs") };
289
+ }
290
+ function isPrivateRecord(store, kind, id, opts) {
291
+ if (opts.privateOnly)
292
+ return true;
293
+ if (opts.publicOnly)
294
+ return false;
295
+ return !!store.getPrivateRec(kind, id);
296
+ }
297
+ function correctionEvent(root, constraint, isPrivate) {
298
+ const occurredAt = constraint.valid_from ?? constraint.provenance.last_verified;
299
+ if (!occurredAt || !Number.isFinite(Date.parse(occurredAt)))
300
+ return null;
301
+ const contentHash = canonicalHash({
302
+ constraint: constraint.id,
303
+ statement: constraint.statement,
304
+ scope: constraint.scope,
305
+ forbids: constraint.forbids,
306
+ match: constraint.match,
307
+ source_decision: constraint.source_decision,
308
+ });
309
+ const event = EvidenceEventSchema.parse({
310
+ id: `ev_${shortHash(`correction:${contentHash}`)}`,
311
+ kind: "correction",
312
+ occurred_at: occurredAt,
313
+ repository: basename(root),
314
+ files: constraint.scope.filter((scope) => scope !== "**"),
315
+ symbols: constraint.forbids?.symbols ?? [],
316
+ text_ref: constraint.id,
317
+ related_records: [constraint.id, ...(constraint.source_decision ? [constraint.source_decision] : [])],
318
+ data_class: isPrivate ? "private" : "public",
319
+ content_hash: contentHash,
320
+ compiler: {
321
+ status: "covered",
322
+ policy: null,
323
+ reason: "Active human-confirmed legacy Constraint already delivers deterministic correction enforcement; Policy IR bridge remains explicit follow-on work.",
324
+ },
325
+ provenance: {
326
+ source: "derived",
327
+ confidence: 1,
328
+ evidence: [constraint.id, ...constraint.provenance.evidence],
329
+ last_verified: constraint.provenance.last_verified,
330
+ },
331
+ });
332
+ return { occurredAt, event, private: isPrivate };
333
+ }
334
+ function bugEvent(root, store, repository, bug, isPrivate, opts) {
335
+ const commit = bug.lineage.fixed_commit ?? bug.lineage.introduced_commit;
336
+ const meta = commit ? commitMeta(commit, root) : null;
337
+ const occurredAt = meta?.date ?? bug.provenance.last_verified;
338
+ if (!occurredAt || !Number.isFinite(Date.parse(occurredAt)))
339
+ return null;
340
+ const kind = bug.lineage.detected ? "test_failure" : "incident";
341
+ const related = [
342
+ bug.id,
343
+ bug.lineage.recurrence_of,
344
+ bug.lineage.spawned_decision,
345
+ bug.lineage.spawned_constraint,
346
+ ].filter((value) => !!value);
347
+ const contentHash = canonicalHash({
348
+ bug: bug.id,
349
+ root_cause: bug.root_cause,
350
+ files: bug.affected_files,
351
+ symbols: bug.affected_symbols,
352
+ lineage: bug.lineage,
353
+ });
354
+ const homeView = { publicOnly: opts.publicOnly, privateOnly: opts.privateOnly };
355
+ const policy = bug.lineage.spawned_decision
356
+ ? repository.listPolicies(homeView).find((candidate) => candidate.legacy_refs.includes(bug.lineage.spawned_decision))
357
+ : undefined;
358
+ const constraint = bug.lineage.spawned_constraint
359
+ ? opts.publicOnly
360
+ ? store.json.get("constraints", bug.lineage.spawned_constraint)
361
+ : opts.privateOnly
362
+ ? store.getPrivateRec("constraints", bug.lineage.spawned_constraint)
363
+ : store.getRec("constraints", bug.lineage.spawned_constraint)
364
+ : undefined;
365
+ const covered = !!policy || constraint?.status === "active";
366
+ const event = EvidenceEventSchema.parse({
367
+ id: `ev_${shortHash(`${kind}:${contentHash}`)}`,
368
+ kind,
369
+ occurred_at: occurredAt,
370
+ repository: basename(root),
371
+ ...(meta ? { actor: meta.author, commit: meta.sha, diff_ref: `git:${meta.sha}` } : {}),
372
+ files: bug.affected_files,
373
+ symbols: bug.affected_symbols,
374
+ text_ref: bug.id,
375
+ related_records: [...related, ...(policy ? [policy.id] : [])],
376
+ data_class: isPrivate ? "private" : "public",
377
+ content_hash: contentHash,
378
+ compiler: covered
379
+ ? {
380
+ status: "covered",
381
+ policy: policy?.id ?? null,
382
+ reason: policy
383
+ ? "Spawned decision already has equivalent Policy IR coverage."
384
+ : "Spawned active legacy Constraint already covers this failure; Policy IR bridge remains pending.",
385
+ }
386
+ : {
387
+ status: "uncompilable",
388
+ policy: null,
389
+ reason: "Incident/test evidence normalized, but no attributable supported assertion or existing deterministic guard is linked.",
390
+ },
391
+ provenance: {
392
+ source: "derived",
393
+ confidence: bug.provenance.confidence,
394
+ evidence: [bug.id, ...bug.provenance.evidence],
395
+ last_verified: bug.provenance.last_verified,
396
+ },
397
+ });
398
+ return { occurredAt, event, private: isPrivate };
399
+ }
400
+ /** Normalize existing local Hunch truth into Constitution EvidenceEvents. This
401
+ * adapter never synthesizes intent and never creates or activates a policy. */
402
+ export function ingestLocalEvidence(store, root, repository, opts = {}) {
403
+ const now = opts.now ?? new Date().toISOString();
404
+ const minDate = durationCutoff(opts.since ?? "90d", now);
405
+ const records = exactRecords(store, opts);
406
+ const view = homeView(opts);
407
+ const policies = repository.listPolicies(view);
408
+ const publicPolicies = repository.listPolicies({ publicOnly: true });
409
+ const publicConstraints = store.json.loadAll("constraints");
410
+ const pending = [];
411
+ let scanned = records.constraints.length + records.bugs.length;
412
+ let excluded = 0;
413
+ for (const constraint of records.constraints) {
414
+ if (constraint.status !== "active" || !constraint.provenance.source.includes("human_confirmed")) {
415
+ excluded++;
416
+ continue;
417
+ }
418
+ const item = correctionEvent(root, constraint, isPrivateRecord(store, "constraints", constraint.id, opts));
419
+ if (!item || Date.parse(item.occurredAt) < minDate)
420
+ excluded++;
421
+ else
422
+ pending.push(item);
423
+ }
424
+ for (const bug of records.bugs) {
425
+ const attributable = !!bug.lineage.detected || (!!bug.root_cause.trim() && bug.provenance.confidence >= 0.7);
426
+ if (!attributable) {
427
+ excluded++;
428
+ continue;
429
+ }
430
+ const item = bugEvent(root, store, repository, bug, isPrivateRecord(store, "bugs", bug.id, opts), opts);
431
+ if (!item || Date.parse(item.occurredAt) < minDate)
432
+ excluded++;
433
+ else
434
+ pending.push(item);
435
+ }
436
+ if (opts.instructions) {
437
+ const instructions = instructionEvents(root, opts.privateOnly ? "private" : "public", opts.privateOnly ? policies : publicPolicies, opts.privateOnly ? records.constraints : publicConstraints);
438
+ scanned += instructions.scanned;
439
+ excluded += instructions.excluded;
440
+ for (const item of instructions.pending) {
441
+ if (Date.parse(item.occurredAt) < minDate)
442
+ excluded++;
443
+ else
444
+ pending.push(item);
445
+ }
446
+ }
447
+ for (const file of opts.importFiles ?? []) {
448
+ const imported = importEvents(root, file, store, repository, opts, policies, records.constraints, publicPolicies, publicConstraints);
449
+ scanned += imported.scanned;
450
+ for (const item of imported.pending) {
451
+ if (Date.parse(item.occurredAt) < minDate)
452
+ excluded++;
453
+ else
454
+ pending.push(item);
455
+ }
456
+ }
457
+ pending.sort((a, b) => b.occurredAt.localeCompare(a.occurredAt) || a.event.id.localeCompare(b.event.id));
458
+ const unique = [...new Map(pending.map((item) => [item.event.id, item])).values()];
459
+ excluded += pending.length - unique.length;
460
+ const selected = unique.slice(0, limit(opts.maxEvents));
461
+ excluded += Math.max(0, unique.length - selected.length);
462
+ const report = {
463
+ scanned,
464
+ eligible: unique.length,
465
+ normalized: 0,
466
+ existing: 0,
467
+ covered: 0,
468
+ uncompilable: 0,
469
+ excluded,
470
+ events: [],
471
+ };
472
+ for (const item of selected) {
473
+ const existing = repository.getEvidence(item.event.id, view);
474
+ const event = existing ?? repository.putEvidence(item.event, { private: item.private });
475
+ if (existing)
476
+ report.existing++;
477
+ else
478
+ report.normalized++;
479
+ if (event.compiler?.status === "covered")
480
+ report.covered++;
481
+ if (event.compiler?.status === "uncompilable")
482
+ report.uncompilable++;
483
+ report.events.push(event);
484
+ }
485
+ return report;
486
+ }
487
+ //# sourceMappingURL=adapters.js.map
@@ -0,0 +1,17 @@
1
+ export function executableBehaviorAttestationError(policy, currentAttestations) {
2
+ if (policy.assertion.kind !== "executable-behavior")
3
+ return null;
4
+ const binding = policy.assertion.attestation;
5
+ const current = currentAttestations.find((attestation) => attestation.id === binding.id);
6
+ if (!current
7
+ || current.disposition !== "selected"
8
+ || current.content_hash !== binding.content_hash
9
+ || current.candidate_id !== binding.candidate_id
10
+ || current.candidate_hash !== binding.candidate_hash
11
+ || current.replay_id !== binding.replay_id
12
+ || current.replay_hash !== binding.replay_hash) {
13
+ return "executable behavior policy is not bound to a current exact selected human attestation";
14
+ }
15
+ return null;
16
+ }
17
+ //# sourceMappingURL=behaviorAttestationBinding.js.map