@deftai/directive-core 0.104.0 → 0.105.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 (225) hide show
  1. package/dist/authz/classify.js +526 -42
  2. package/dist/check/cached-orchestrator.d.ts +1 -1
  3. package/dist/check/cached-orchestrator.js +2 -2
  4. package/dist/check/named-cause.d.ts +1 -1
  5. package/dist/check/named-cause.js +33 -3
  6. package/dist/codebase/default-extractor.d.ts +5 -0
  7. package/dist/codebase/default-extractor.js +7 -15
  8. package/dist/deposit/copy-tree.js +60 -0
  9. package/dist/deposit/python-free.js +26 -18
  10. package/dist/doctor/checks.d.ts +8 -0
  11. package/dist/doctor/checks.js +37 -25
  12. package/dist/doctor/doctor-state.d.ts +4 -0
  13. package/dist/doctor/doctor-state.js +7 -1
  14. package/dist/doctor/main.js +9 -19
  15. package/dist/doctor/types.d.ts +9 -0
  16. package/dist/fs/contained-write.d.ts +115 -1
  17. package/dist/fs/contained-write.js +241 -5
  18. package/dist/fs/mutation-ledger.d.ts +54 -0
  19. package/dist/fs/mutation-ledger.js +133 -0
  20. package/dist/fs/non-product-dirs.d.ts +79 -0
  21. package/dist/fs/non-product-dirs.js +96 -0
  22. package/dist/hooks/dispatcher.d.ts +3 -1
  23. package/dist/hooks/dispatcher.js +14 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +2 -0
  26. package/dist/init-deposit/agent-hooks.js +17 -15
  27. package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
  28. package/dist/init-deposit/core-guard-pin-content.js +393 -0
  29. package/dist/init-deposit/gitignore.js +9 -0
  30. package/dist/init-deposit/hygiene.d.ts +47 -11
  31. package/dist/init-deposit/hygiene.js +172 -37
  32. package/dist/init-deposit/legacy-detect.js +6 -2
  33. package/dist/init-deposit/refresh.d.ts +21 -1
  34. package/dist/init-deposit/refresh.js +106 -9
  35. package/dist/init-deposit/scaffold.js +28 -279
  36. package/dist/init-deposit/slash-deposit.js +13 -7
  37. package/dist/init-deposit/xbrief-projections.d.ts +8 -1
  38. package/dist/init-deposit/xbrief-projections.js +24 -11
  39. package/dist/intake/clause-derivation.d.ts +38 -1
  40. package/dist/intake/clause-derivation.js +211 -9
  41. package/dist/intake/issue-ingest.d.ts +3 -1
  42. package/dist/intake/issue-ingest.js +35 -6
  43. package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
  44. package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
  45. package/dist/literal-acceptance/capture.d.ts +10 -0
  46. package/dist/literal-acceptance/capture.js +224 -75
  47. package/dist/literal-acceptance/evaluate.d.ts +29 -4
  48. package/dist/literal-acceptance/evaluate.js +70 -2
  49. package/dist/literal-acceptance/index.d.ts +3 -3
  50. package/dist/literal-acceptance/index.js +3 -3
  51. package/dist/literal-acceptance/safety.d.ts +41 -0
  52. package/dist/literal-acceptance/safety.js +126 -2
  53. package/dist/literal-acceptance/types.d.ts +8 -0
  54. package/dist/orphan-active/evaluate.d.ts +7 -1
  55. package/dist/orphan-active/evaluate.js +102 -23
  56. package/dist/platform/platform-capabilities.js +2 -3
  57. package/dist/policy/base-branch.d.ts +37 -0
  58. package/dist/policy/base-branch.js +114 -0
  59. package/dist/policy/branch-sync.d.ts +80 -0
  60. package/dist/policy/branch-sync.js +211 -0
  61. package/dist/policy/ceremony-dial-escalation.js +1 -0
  62. package/dist/policy/ceremony-dial.js +4 -14
  63. package/dist/policy/delivery-branch.d.ts +5 -3
  64. package/dist/policy/delivery-branch.js +7 -3
  65. package/dist/policy/file-size-thresholds.d.ts +24 -0
  66. package/dist/policy/file-size-thresholds.js +23 -0
  67. package/dist/policy/forge-outage-retry.d.ts +47 -0
  68. package/dist/policy/forge-outage-retry.js +157 -0
  69. package/dist/policy/index.d.ts +7 -0
  70. package/dist/policy/index.js +66 -7
  71. package/dist/policy/project-invariants.d.ts +50 -0
  72. package/dist/policy/project-invariants.js +205 -0
  73. package/dist/policy/sync-default.d.ts +129 -0
  74. package/dist/policy/sync-default.js +452 -0
  75. package/dist/policy/sync-max-files.d.ts +93 -0
  76. package/dist/policy/sync-max-files.js +192 -0
  77. package/dist/pr-monitor/absent-required.d.ts +11 -0
  78. package/dist/pr-monitor/absent-required.js +61 -0
  79. package/dist/pr-monitor/constants.d.ts +7 -0
  80. package/dist/pr-monitor/constants.js +7 -0
  81. package/dist/pr-monitor/index.d.ts +2 -1
  82. package/dist/pr-monitor/index.js +2 -1
  83. package/dist/pr-monitor/main.js +4 -0
  84. package/dist/pr-monitor/monitor.js +24 -1
  85. package/dist/pr-wait-mergeable/classify.js +6 -0
  86. package/dist/preflight/evaluate.d.ts +4 -0
  87. package/dist/preflight/evaluate.js +35 -1
  88. package/dist/preflight/index.d.ts +4 -0
  89. package/dist/preflight/index.js +2 -0
  90. package/dist/preflight/intended-placement.d.ts +62 -0
  91. package/dist/preflight/intended-placement.js +275 -0
  92. package/dist/preflight/project-invariants-gate.d.ts +29 -0
  93. package/dist/preflight/project-invariants-gate.js +82 -0
  94. package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
  95. package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
  96. package/dist/product-first-done-gate/acceptance.d.ts +7 -4
  97. package/dist/product-first-done-gate/acceptance.js +79 -8
  98. package/dist/product-first-done-gate/empty-resolution.d.ts +4 -1
  99. package/dist/product-first-done-gate/empty-resolution.js +12 -1
  100. package/dist/product-first-done-gate/evaluate.d.ts +12 -0
  101. package/dist/product-first-done-gate/evaluate.js +254 -31
  102. package/dist/product-first-done-gate/index.d.ts +2 -0
  103. package/dist/product-first-done-gate/index.js +1 -0
  104. package/dist/release/build-dist.d.ts +101 -0
  105. package/dist/release/build-dist.js +428 -46
  106. package/dist/run-summary/emit.d.ts +16 -10
  107. package/dist/run-summary/emit.js +26 -15
  108. package/dist/run-summary/index.d.ts +3 -2
  109. package/dist/run-summary/index.js +2 -1
  110. package/dist/run-summary/session-id.d.ts +21 -0
  111. package/dist/run-summary/session-id.js +92 -0
  112. package/dist/run-summary/types.d.ts +29 -4
  113. package/dist/run-summary/types.js +1 -0
  114. package/dist/scope/acceptance-evidence.d.ts +19 -7
  115. package/dist/scope/acceptance-evidence.js +38 -11
  116. package/dist/scope/delivery-evidence.d.ts +3 -3
  117. package/dist/scope/delivery-evidence.js +22 -27
  118. package/dist/scope/index.d.ts +1 -0
  119. package/dist/scope/index.js +1 -0
  120. package/dist/scope/parent-lineage.js +7 -6
  121. package/dist/scope/project-invariant-coverage.d.ts +54 -0
  122. package/dist/scope/project-invariant-coverage.js +322 -0
  123. package/dist/scope/transition.js +1 -1
  124. package/dist/scope-provenance/compare-intent.d.ts +27 -0
  125. package/dist/scope-provenance/compare-intent.js +233 -0
  126. package/dist/scope-provenance/digest.d.ts +18 -1
  127. package/dist/scope-provenance/digest.js +16 -11
  128. package/dist/scope-provenance/evaluate.d.ts +5 -1
  129. package/dist/scope-provenance/evaluate.js +64 -4
  130. package/dist/scope-provenance/extract-intent.d.ts +43 -0
  131. package/dist/scope-provenance/extract-intent.js +262 -0
  132. package/dist/scope-provenance/index.d.ts +7 -1
  133. package/dist/scope-provenance/index.js +7 -1
  134. package/dist/scope-provenance/intent-digest.d.ts +12 -0
  135. package/dist/scope-provenance/intent-digest.js +33 -0
  136. package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
  137. package/dist/scope-provenance/intent-evaluate.js +149 -0
  138. package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
  139. package/dist/scope-provenance/json-tokenizer.js +225 -0
  140. package/dist/scope-provenance/known-machine.d.ts +38 -0
  141. package/dist/scope-provenance/known-machine.js +157 -0
  142. package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
  143. package/dist/scope-provenance/mint-artifacts.js +425 -0
  144. package/dist/session/ac-pass-banking.d.ts +4 -36
  145. package/dist/session/ac-pass-banking.js +30 -77
  146. package/dist/session/ac-pass-reuse.d.ts +53 -0
  147. package/dist/session/ac-pass-reuse.js +95 -0
  148. package/dist/session/git.js +19 -4
  149. package/dist/session/index.d.ts +4 -0
  150. package/dist/session/index.js +4 -0
  151. package/dist/session/occupancy-stress-worker.d.ts +7 -0
  152. package/dist/session/occupancy-stress-worker.js +36 -0
  153. package/dist/session/occupancy.d.ts +97 -0
  154. package/dist/session/occupancy.js +422 -0
  155. package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
  156. package/dist/session/product-state-hash.d.ts +26 -0
  157. package/dist/session/product-state-hash.js +683 -0
  158. package/dist/session/session-ready.d.ts +2 -0
  159. package/dist/session/session-ready.js +52 -30
  160. package/dist/session/session-start.d.ts +7 -0
  161. package/dist/session/session-start.js +71 -7
  162. package/dist/session/verify-ac-session-cache.d.ts +42 -0
  163. package/dist/session/verify-ac-session-cache.js +126 -0
  164. package/dist/session/verify-session-ritual.d.ts +5 -0
  165. package/dist/session/verify-session-ritual.js +9 -0
  166. package/dist/slash/openclaw-deposit.js +13 -9
  167. package/dist/slice/constants.d.ts +2 -2
  168. package/dist/slice/constants.js +2 -2
  169. package/dist/slice/existing.js +9 -3
  170. package/dist/slice/lock.d.ts +16 -1
  171. package/dist/slice/lock.js +137 -11
  172. package/dist/story-ready/evaluate.d.ts +2 -0
  173. package/dist/story-ready/evaluate.js +13 -0
  174. package/dist/swarm/complete-cohort.d.ts +3 -0
  175. package/dist/swarm/complete-cohort.js +59 -11
  176. package/dist/swarm/finalize-cohort.js +33 -2
  177. package/dist/swarm/launch.d.ts +22 -0
  178. package/dist/swarm/launch.js +155 -0
  179. package/dist/swarm/routing-set-cli.d.ts +11 -1
  180. package/dist/swarm/routing-set-cli.js +13 -3
  181. package/dist/swarm/routing-verify.d.ts +12 -0
  182. package/dist/swarm/routing-verify.js +59 -20
  183. package/dist/swarm/routing.d.ts +14 -0
  184. package/dist/swarm/routing.js +53 -3
  185. package/dist/telemetry-coverage/fake-trial.js +10 -0
  186. package/dist/telemetry-coverage/kinds.js +2 -0
  187. package/dist/triage/actions/index.js +4 -1
  188. package/dist/ts-check-lane/index.d.ts +3 -0
  189. package/dist/ts-check-lane/index.js +2 -0
  190. package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
  191. package/dist/ts-check-lane/progress-reporter.js +38 -0
  192. package/dist/ts-check-lane/progress.d.ts +32 -0
  193. package/dist/ts-check-lane/progress.js +57 -0
  194. package/dist/ts-check-lane/run-lane.d.ts +3 -1
  195. package/dist/ts-check-lane/run-lane.js +8 -6
  196. package/dist/validate-content/validate-links.d.ts +8 -0
  197. package/dist/validate-content/validate-links.js +9 -15
  198. package/dist/value/feedback-file.js +12 -2
  199. package/dist/vbrief-build/index.d.ts +1 -0
  200. package/dist/vbrief-build/index.js +1 -0
  201. package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
  202. package/dist/vbrief-build/project-invariants-io.js +41 -0
  203. package/dist/vbrief-reconcile/graph.js +18 -1
  204. package/dist/vbrief-reconcile/index.d.ts +1 -1
  205. package/dist/vbrief-reconcile/index.js +1 -1
  206. package/dist/vbrief-reconcile/types.d.ts +13 -0
  207. package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
  208. package/dist/vbrief-reconcile/umbrellas.js +261 -26
  209. package/dist/vbrief-validate/index.d.ts +1 -1
  210. package/dist/vbrief-validate/index.js +1 -1
  211. package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
  212. package/dist/vbrief-validate/plan-hooks.js +68 -0
  213. package/dist/verify-ac/evaluate.d.ts +7 -1
  214. package/dist/verify-ac/evaluate.js +34 -26
  215. package/dist/verify-ac/flag.d.ts +4 -0
  216. package/dist/verify-ac/flag.js +16 -0
  217. package/dist/verify-ac/index.d.ts +2 -2
  218. package/dist/verify-ac/index.js +2 -2
  219. package/dist/verify-source/contained-writes.js +2 -0
  220. package/dist/verify-source/verify-stubs.d.ts +15 -1
  221. package/dist/verify-source/verify-stubs.js +20 -16
  222. package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
  223. package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
  224. package/dist/xbrief-migrate/migrate-project.js +2 -5
  225. package/package.json +3 -3
@@ -5,6 +5,8 @@ import { createIssueComment, editIssueBody, editIssueCommentBody, fetchIssueBody
5
5
  import { fetchIssueStates, IssueState } from "../intake/reconcile-issues.js";
6
6
  import { hasArtifactSuffix, resolveLifecycleRoot, stripArtifactSuffix } from "../layout/resolve.js";
7
7
  import { call } from "../scm/call.js";
8
+ import { restCloseIssue } from "../scm/gh-rest.js";
9
+ import { readAll } from "../slice/record.js";
8
10
  import { extractIssueRef, parseGithubIssueUri } from "../triage/reconcile/parse-uri.js";
9
11
  import { isRepoMutationAllowed } from "./repo-guard.js";
10
12
  export const OPEN_FOLDERS = ["proposed", "pending", "active"];
@@ -307,33 +309,43 @@ export function classifyPassType(prevTotal, total) {
307
309
  function hasCurrentShape(body) {
308
310
  return HEADER_RE.test(body);
309
311
  }
312
+ function parseCommentPage(data) {
313
+ if (!Array.isArray(data))
314
+ return [];
315
+ const comments = [];
316
+ for (const entry of data) {
317
+ if (typeof entry === "object" &&
318
+ entry !== null &&
319
+ !Array.isArray(entry) &&
320
+ typeof entry.id === "number" &&
321
+ typeof entry.body === "string") {
322
+ const rec = entry;
323
+ comments.push({ id: rec.id, body: rec.body });
324
+ }
325
+ }
326
+ return comments;
327
+ }
310
328
  export class ScmUmbrellaClient {
311
329
  fetchComments(repo, issueNumber) {
312
- const proc = call(SCM_SOURCE, "api", [
313
- `repos/${repo}/issues/${issueNumber}/comments?per_page=100`,
314
- ]);
315
- if (proc.returncode !== 0) {
316
- throw new UmbrellaScmError(`list comments #${issueNumber} (${repo}) failed: ${(proc.stderr || "").trim()}`);
317
- }
318
- let data;
319
- try {
320
- data = JSON.parse(proc.stdout || "[]");
321
- }
322
- catch (exc) {
323
- throw new UmbrellaScmError(`list comments #${issueNumber} (${repo}) returned non-JSON: ${String(exc)}`);
324
- }
325
- if (!Array.isArray(data))
326
- return [];
327
330
  const comments = [];
328
- for (const entry of data) {
329
- if (typeof entry === "object" &&
330
- entry !== null &&
331
- !Array.isArray(entry) &&
332
- typeof entry.id === "number" &&
333
- typeof entry.body === "string") {
334
- const rec = entry;
335
- comments.push({ id: rec.id, body: rec.body });
331
+ for (let page = 1; page <= 50; page += 1) {
332
+ const proc = call(SCM_SOURCE, "api", [
333
+ `repos/${repo}/issues/${issueNumber}/comments?per_page=100&page=${page}`,
334
+ ]);
335
+ if (proc.returncode !== 0) {
336
+ throw new UmbrellaScmError(`list comments #${issueNumber} (${repo}) failed: ${(proc.stderr || "").trim()}`);
337
+ }
338
+ let data;
339
+ try {
340
+ data = JSON.parse(proc.stdout || "[]");
341
+ }
342
+ catch (exc) {
343
+ throw new UmbrellaScmError(`list comments #${issueNumber} (${repo}) returned non-JSON: ${String(exc)}`);
336
344
  }
345
+ const batch = parseCommentPage(data);
346
+ comments.push(...batch);
347
+ if (batch.length < 100)
348
+ break;
337
349
  }
338
350
  return comments;
339
351
  }
@@ -394,6 +406,15 @@ export class ScmUmbrellaClient {
394
406
  throw new UmbrellaScmError(`edit issue body #${issueNumber} (${repo}) failed: ${message}`);
395
407
  }
396
408
  }
409
+ closeIssue(repo, issueNumber) {
410
+ try {
411
+ restCloseIssue(repo, issueNumber, "completed");
412
+ }
413
+ catch (exc) {
414
+ const message = exc instanceof Error ? exc.message : String(exc);
415
+ throw new UmbrellaScmError(`close issue #${issueNumber} (${repo}) failed: ${message}`);
416
+ }
417
+ }
397
418
  }
398
419
  function planShape(epicData, index, options = {}) {
399
420
  const children = computeChildren(epicData, index, {
@@ -545,6 +566,147 @@ function reconcileOneEpic(epicData, index, options) {
545
566
  export function nowIso(date = new Date()) {
546
567
  return date.toISOString().replace(/\.\d{3}Z$/, "Z");
547
568
  }
569
+ const ALL_CHILDREN_MERGED = "all-children-merged";
570
+ /** Children listed on a slices.jsonl row (#3428). Last write of a duplicate `n` wins. */
571
+ export function childrenFromSliceRecord(record) {
572
+ const raw = record.children;
573
+ if (!Array.isArray(raw))
574
+ return [];
575
+ const byNumber = new Map();
576
+ for (const entry of raw) {
577
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
578
+ continue;
579
+ const rec = entry;
580
+ const n = rec.n;
581
+ if (typeof n !== "number" || !Number.isInteger(n) || n < 1)
582
+ continue;
583
+ byNumber.set(n, {
584
+ story_id: `#${n}`,
585
+ title: `#${n}`,
586
+ kind: "story",
587
+ folder: UNKNOWN_FOLDER,
588
+ depends_on: [],
589
+ issue_number: n,
590
+ });
591
+ }
592
+ return [...byNumber.values()];
593
+ }
594
+ /**
595
+ * Close only when the slice signal is all-children-merged, the umbrella is
596
+ * still open, and every child has forge state closed (#3428).
597
+ */
598
+ export function shouldCloseOnAllChildrenMerged(options) {
599
+ if (options.signal !== ALL_CHILDREN_MERGED)
600
+ return false;
601
+ if (options.umbrellaState !== "open")
602
+ return false;
603
+ if (options.children.length === 0)
604
+ return false;
605
+ if (options.forgeStates == null)
606
+ return false;
607
+ return options.children.every((c) => typeof c.issue_number === "number" && options.forgeStates?.get(c.issue_number) === "closed");
608
+ }
609
+ export function formatCloseComment(signal, children) {
610
+ const nums = children
611
+ .map((c) => c.issue_number)
612
+ .filter((n) => typeof n === "number")
613
+ .sort((a, b) => a - b)
614
+ .map((n) => `#${n}`);
615
+ return `expected_close_signal=${signal}; children: ${nums.join(", ")}`;
616
+ }
617
+ function loadLatestSliceRecords(vbriefDir) {
618
+ const logPath = join(vbriefDir, ".triage-cache", "slices.jsonl");
619
+ if (!existsSync(logPath))
620
+ return [];
621
+ const latest = new Map();
622
+ for (const rec of readAll({ path: logPath })) {
623
+ const n = rec.umbrella;
624
+ if (typeof n !== "number" || !Number.isInteger(n) || n < 1)
625
+ continue;
626
+ const [urlRepo] = parseGithubIssueUri(rec.umbrella_url);
627
+ latest.set(`${urlRepo ?? ""}:${n}`, rec);
628
+ }
629
+ return [...latest.values()];
630
+ }
631
+ function epicDataFromSlice(repo, number, children) {
632
+ const refs = [
633
+ { type: "x-xbrief/github-issue", uri: `https://github.com/${repo}/issues/${number}` },
634
+ ...children.map((c) => ({
635
+ type: "x-xbrief/github-issue",
636
+ uri: `https://github.com/${repo}/issues/${c.issue_number}`,
637
+ title: c.title,
638
+ })),
639
+ ];
640
+ return {
641
+ plan: {
642
+ id: `slice-umbrella-${number}`,
643
+ metadata: { kind: "epic" },
644
+ references: refs,
645
+ },
646
+ };
647
+ }
648
+ function maybeCloseSliceUmbrella(options) {
649
+ if (typeof options.client.closeIssue !== "function")
650
+ return "skipped";
651
+ if (typeof options.client.fetchIssueStates !== "function")
652
+ return "skipped";
653
+ const mutateGate = isRepoMutationAllowed(options.repo, options.projectRoot, {
654
+ allowCrossRepo: options.allowCrossRepo,
655
+ allowlist: options.repoAllowlist,
656
+ explicitRepo: options.explicitRepo ?? null,
657
+ });
658
+ if (!mutateGate.allowed) {
659
+ throw new Error(mutateGate.reason ?? `refusing cross-repo mutation on ${options.repo}`);
660
+ }
661
+ const childNumbers = options.children
662
+ .map((c) => c.issue_number)
663
+ .filter((n) => typeof n === "number");
664
+ const states = options.client.fetchIssueStates(options.repo, [...childNumbers, options.number]);
665
+ const umbrellaState = states.get(options.number) ?? null;
666
+ if (!shouldCloseOnAllChildrenMerged({
667
+ signal: options.signal,
668
+ children: options.children,
669
+ forgeStates: states,
670
+ umbrellaState,
671
+ })) {
672
+ return options.signal === ALL_CHILDREN_MERGED && umbrellaState === "closed"
673
+ ? "unchanged"
674
+ : "skipped";
675
+ }
676
+ const closeBody = formatCloseComment(ALL_CHILDREN_MERGED, options.children);
677
+ if (!options.dryRun) {
678
+ const alreadyCommented = options.client
679
+ .fetchComments(options.repo, options.number)
680
+ .some((c) => c.body.trim() === closeBody);
681
+ if (!alreadyCommented) {
682
+ options.client.createComment(options.repo, options.number, closeBody);
683
+ }
684
+ options.client.closeIssue(options.repo, options.number);
685
+ }
686
+ return "closed";
687
+ }
688
+ function pushUmbrellaChange(outcome, change) {
689
+ if (change.action === "unchanged" && change.close_action !== "closed") {
690
+ outcome.unchanged.push(change);
691
+ }
692
+ else {
693
+ outcome.changed.push(change);
694
+ }
695
+ }
696
+ function replaceCloseAction(outcome, repo, issueNumber, closeAction) {
697
+ const match = (c) => c.repo === repo && c.issue_number === issueNumber;
698
+ for (const list of [outcome.changed, outcome.unchanged]) {
699
+ const idx = list.findIndex(match);
700
+ if (idx < 0)
701
+ continue;
702
+ const prev = list[idx];
703
+ if (prev === undefined)
704
+ return;
705
+ list.splice(idx, 1);
706
+ pushUmbrellaChange(outcome, { ...prev, close_action: closeAction });
707
+ return;
708
+ }
709
+ }
548
710
  export function reconcileUmbrellas(projectRoot, options = {}) {
549
711
  const root = resolve(projectRoot);
550
712
  let vbriefDir;
@@ -616,7 +778,6 @@ export function reconcileUmbrellas(projectRoot, options = {}) {
616
778
  const key = `${effectiveRepo}:${number}`;
617
779
  if (seenIssues.has(key))
618
780
  continue;
619
- seenIssues.add(key);
620
781
  try {
621
782
  const change = reconcileOneEpic(data, index, {
622
783
  storyId,
@@ -630,6 +791,7 @@ export function reconcileUmbrellas(projectRoot, options = {}) {
630
791
  repoAllowlist: options.repoAllowlist,
631
792
  explicitRepo: options.repo ?? null,
632
793
  });
794
+ seenIssues.add(key);
633
795
  if (change.action === "unchanged")
634
796
  outcome.unchanged.push(change);
635
797
  else
@@ -643,6 +805,71 @@ export function reconcileUmbrellas(projectRoot, options = {}) {
643
805
  }
644
806
  }
645
807
  }
808
+ // slices.jsonl umbrellas have no epic xBRIEF (#3428). Last row per umbrella wins.
809
+ for (const record of loadLatestSliceRecords(vbriefDir)) {
810
+ const number = record.umbrella;
811
+ if (typeof number !== "number")
812
+ continue;
813
+ const [urlRepo] = parseGithubIssueUri(record.umbrella_url);
814
+ const effectiveRepo = urlRepo ?? options.repo ?? null;
815
+ if (effectiveRepo === null) {
816
+ outcome.skipped_no_ref.push(`slice-umbrella-${number}`);
817
+ continue;
818
+ }
819
+ const key = `${effectiveRepo}:${number}`;
820
+ const children = childrenFromSliceRecord(record);
821
+ const storyId = `slice-umbrella-${number}`;
822
+ try {
823
+ if (seenIssues.has(key)) {
824
+ const closeAction = maybeCloseSliceUmbrella({
825
+ signal: record.expected_close_signal,
826
+ children,
827
+ repo: effectiveRepo,
828
+ number,
829
+ client,
830
+ dryRun: options.dryRun ?? false,
831
+ projectRoot: root,
832
+ allowCrossRepo: options.allowCrossRepo ?? false,
833
+ repoAllowlist: options.repoAllowlist,
834
+ explicitRepo: options.repo ?? null,
835
+ });
836
+ replaceCloseAction(outcome, effectiveRepo, number, closeAction);
837
+ continue;
838
+ }
839
+ const change = reconcileOneEpic(epicDataFromSlice(effectiveRepo, number, children), index, {
840
+ storyId,
841
+ repo: effectiveRepo,
842
+ number,
843
+ client,
844
+ dryRun: options.dryRun ?? false,
845
+ now,
846
+ projectRoot: root,
847
+ allowCrossRepo: options.allowCrossRepo ?? false,
848
+ repoAllowlist: options.repoAllowlist,
849
+ explicitRepo: options.repo ?? null,
850
+ });
851
+ seenIssues.add(key);
852
+ const closeAction = maybeCloseSliceUmbrella({
853
+ signal: record.expected_close_signal,
854
+ children,
855
+ repo: effectiveRepo,
856
+ number,
857
+ client,
858
+ dryRun: options.dryRun ?? false,
859
+ projectRoot: root,
860
+ allowCrossRepo: options.allowCrossRepo ?? false,
861
+ repoAllowlist: options.repoAllowlist,
862
+ explicitRepo: options.repo ?? null,
863
+ });
864
+ pushUmbrellaChange(outcome, { ...change, close_action: closeAction });
865
+ }
866
+ catch (exc) {
867
+ outcome.errors.push({
868
+ story_id: storyId,
869
+ message: exc instanceof Error ? exc.message : String(exc),
870
+ });
871
+ }
872
+ }
646
873
  return [outcome.errors.length > 0 ? 1 : 0, outcome];
647
874
  }
648
875
  function formatChecklistSuffix(action) {
@@ -650,6 +877,11 @@ function formatChecklistSuffix(action) {
650
877
  return "";
651
878
  return `; checklist=${action}`;
652
879
  }
880
+ function formatCloseSuffix(action) {
881
+ if (action === undefined || action === "skipped")
882
+ return "";
883
+ return `; close=${action}`;
884
+ }
653
885
  export function renderUmbrellasReport(outcome) {
654
886
  const lines = ["vBRIEF reconcile umbrellas", ""];
655
887
  const suffix = outcome.dry_run ? " (dry-run)" : "";
@@ -657,7 +889,8 @@ export function renderUmbrellasReport(outcome) {
657
889
  if (outcome.changed.length > 0) {
658
890
  for (const c of outcome.changed) {
659
891
  lines.push(`- #${c.issue_number} (${c.repo}) [${c.story_id}]: ${c.action} -> pass-${c.pass_n}` +
660
- formatChecklistSuffix(c.checklist_action));
892
+ formatChecklistSuffix(c.checklist_action) +
893
+ formatCloseSuffix(c.close_action));
661
894
  }
662
895
  }
663
896
  else {
@@ -668,7 +901,8 @@ export function renderUmbrellasReport(outcome) {
668
901
  if (outcome.unchanged.length > 0) {
669
902
  for (const c of outcome.unchanged) {
670
903
  lines.push(`- #${c.issue_number} (${c.repo}) [${c.story_id}]: pass-${c.pass_n}` +
671
- formatChecklistSuffix(c.checklist_action));
904
+ formatChecklistSuffix(c.checklist_action) +
905
+ formatCloseSuffix(c.close_action));
672
906
  }
673
907
  }
674
908
  else {
@@ -696,6 +930,7 @@ export function umbrellasOutcomeToJson(outcome) {
696
930
  action: c.action,
697
931
  pass_n: c.pass_n,
698
932
  checklist_action: c.checklist_action ?? "skipped",
933
+ close_action: c.close_action ?? "skipped",
699
934
  });
700
935
  return {
701
936
  changed: outcome.changed.map(toChange),
@@ -6,7 +6,7 @@ export { validateFolderStatus } from "./folder-status.js";
6
6
  export type { ConformanceCliOptions, ValidateCliOptions } from "./main.js";
7
7
  export { cmdVbriefValidate, runConformance, runValidate } from "./main.js";
8
8
  export { validateOriginProvenance } from "./origin.js";
9
- export { validateSessionRitualStalenessHoursOnPlan, validateTriageRankingLabelsOnPlan, validateWipCapOnPlan, } from "./plan-hooks.js";
9
+ export { validateForgeOutageRetryMinutesOnPlan, validateProjectInvariantsOnPlan, validateSessionRitualStalenessHoursOnPlan, validateTriageRankingLabelsOnPlan, validateWipCapOnPlan, } from "./plan-hooks.js";
10
10
  export { validateProjectDefinition } from "./project-definition.js";
11
11
  export type { ExtensionEntry, ExtensionRoundtripEvaluateResult, ExtensionRoundtripFinding, } from "./roundtrip.js";
12
12
  export { collectExtensionEntries, EXTENSION_CONFORMANCE_FIXTURES_DIR, evaluateExtensionRoundtrip, findExtensionPreservationViolations, reEmitVbriefArtifact, renderExtensionRoundtripFinding, VbriefSchemaValidationError, } from "./roundtrip.js";
@@ -4,7 +4,7 @@ export { matchesFilenameConvention, validateFilename } from "./filename.js";
4
4
  export { validateFolderStatus } from "./folder-status.js";
5
5
  export { cmdVbriefValidate, runConformance, runValidate } from "./main.js";
6
6
  export { validateOriginProvenance } from "./origin.js";
7
- export { validateSessionRitualStalenessHoursOnPlan, validateTriageRankingLabelsOnPlan, validateWipCapOnPlan, } from "./plan-hooks.js";
7
+ export { validateForgeOutageRetryMinutesOnPlan, validateProjectInvariantsOnPlan, validateSessionRitualStalenessHoursOnPlan, validateTriageRankingLabelsOnPlan, validateWipCapOnPlan, } from "./plan-hooks.js";
8
8
  export { validateProjectDefinition } from "./project-definition.js";
9
9
  export { collectExtensionEntries, EXTENSION_CONFORMANCE_FIXTURES_DIR, evaluateExtensionRoundtrip, findExtensionPreservationViolations, reEmitVbriefArtifact, renderExtensionRoundtripFinding, VbriefSchemaValidationError, } from "./roundtrip.js";
10
10
  export { normalizeNarrativeKey, validateVbriefSchema } from "./schema.js";
@@ -2,6 +2,10 @@
2
2
  export declare function validateWipCapOnPlan(plan: unknown, filepath: string): string[];
3
3
  /** vbrief_validate hook for ``sessionRitualStalenessHours`` (#1348). */
4
4
  export declare function validateSessionRitualStalenessHoursOnPlan(plan: unknown, filepath: string): string[];
5
+ /** vbrief_validate hook for ``projectInvariants`` (#3425). */
6
+ export declare function validateProjectInvariantsOnPlan(plan: unknown, filepath: string): string[];
7
+ /** vbrief_validate hook for ``forgeOutageRetryMinutes`` (#3422). */
8
+ export declare function validateForgeOutageRetryMinutesOnPlan(plan: unknown, filepath: string): string[];
5
9
  /** vbrief_validate hook: validate ``plan.policy.triageRankingLabels`` (#1128). */
6
10
  export declare function validateTriageRankingLabelsOnPlan(plan: unknown, filepath: string): string[];
7
11
  /** vbrief_validate hook: validate ``plan.policy.runtimeAuthority`` (#1394). */
@@ -3,6 +3,7 @@ import { validateCoverageDebt } from "../policy/coverage-debt.js";
3
3
  import { validateHostHooks } from "../policy/host-hooks.js";
4
4
  import { validateHostSlashCommands } from "../policy/host-slash-commands.js";
5
5
  import { readPlanPolicy } from "../policy/plan-extensions.js";
6
+ import { parseProjectInvariants } from "../policy/project-invariants.js";
6
7
  import { validateRuntimeAuthority } from "../policy/runtime-authority.js";
7
8
  import { validateStalenessTickler } from "../policy/staleness-tickler.js";
8
9
  import { validateOpenClawProductCommands } from "../slash/openclaw-deposit.js";
@@ -93,6 +94,61 @@ export function validateSessionRitualStalenessHoursOnPlan(plan, filepath) {
93
94
  }
94
95
  return out;
95
96
  }
97
+ function validateForgeOutageRetryMinutes(value) {
98
+ if (value === null || value === undefined) {
99
+ return [];
100
+ }
101
+ if (typeof value !== "number" || !Number.isInteger(value)) {
102
+ const repr = typeof value === "string"
103
+ ? pyStrRepr(value)
104
+ : typeof value === "boolean"
105
+ ? value
106
+ ? "True"
107
+ : "False"
108
+ : String(value);
109
+ return [
110
+ "plan.policy.forgeOutageRetryMinutes must be an integer; got " +
111
+ `${pythonTypeName(value)} (${repr})`,
112
+ ];
113
+ }
114
+ if (value < 5) {
115
+ return [`plan.policy.forgeOutageRetryMinutes must be >= 5; got ${value}`];
116
+ }
117
+ return [];
118
+ }
119
+ /** vbrief_validate hook for ``projectInvariants`` (#3425). */
120
+ export function validateProjectInvariantsOnPlan(plan, filepath) {
121
+ if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
122
+ return [];
123
+ }
124
+ const policy = readPlanPolicy(plan);
125
+ if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
126
+ return [];
127
+ }
128
+ if (!("projectInvariants" in policy)) {
129
+ return [];
130
+ }
131
+ const { errors } = parseProjectInvariants(policy.projectInvariants);
132
+ return errors.map((err) => `${filepath}: ${err} (#3425)`);
133
+ }
134
+ /** vbrief_validate hook for ``forgeOutageRetryMinutes`` (#3422). */
135
+ export function validateForgeOutageRetryMinutesOnPlan(plan, filepath) {
136
+ if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
137
+ return [];
138
+ }
139
+ const policy = readPlanPolicy(plan);
140
+ if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
141
+ return [];
142
+ }
143
+ if (!("forgeOutageRetryMinutes" in policy)) {
144
+ return [];
145
+ }
146
+ const out = [];
147
+ for (const err of validateForgeOutageRetryMinutes(policy.forgeOutageRetryMinutes)) {
148
+ out.push(`${filepath}: ${err} (#3422)`);
149
+ }
150
+ return out;
151
+ }
96
152
  /** vbrief_validate hook: validate ``plan.policy.triageRankingLabels`` (#1128). */
97
153
  export function validateTriageRankingLabelsOnPlan(plan, filepath) {
98
154
  if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
@@ -283,6 +339,18 @@ export function runProjectDefinitionHooks(plan, filepath) {
283
339
  catch {
284
340
  /* hook must not break validation */
285
341
  }
342
+ try {
343
+ errors.push(...validateForgeOutageRetryMinutesOnPlan(plan, filepath));
344
+ }
345
+ catch {
346
+ /* hook must not break validation */
347
+ }
348
+ try {
349
+ errors.push(...validateProjectInvariantsOnPlan(plan, filepath));
350
+ }
351
+ catch {
352
+ /* hook must not break validation */
353
+ }
286
354
  try {
287
355
  errors.push(...validateStalenessTicklerOnPlan(plan, filepath));
288
356
  }
@@ -27,6 +27,11 @@ export interface OracleIntegrityResultFields {
27
27
  readonly code: number;
28
28
  readonly message: string;
29
29
  }
30
+ /**
31
+ * Walk-level method fingerprint: command list + cwd hash (#3397 / #3322).
32
+ * Same shape for fail and pass so an honest product fix does not flag.
33
+ */
34
+ export declare function methodFingerprintForWalk(commands: readonly string[], cwd: string): string;
30
35
  /** Stable prefix for product-oracle check ids emitted by verify:ac (#3322 / #3337). */
31
36
  export declare const VERIFY_AC_CHECK_ID_PREFIX = "verify:ac";
32
37
  /**
@@ -40,7 +45,8 @@ export declare function verifyAcCheckId(scopeKey?: string | null): string;
40
45
  /** Test seam: isolate stdout-dest buffer across cases. */
41
46
  export declare function resetInProcessVerificationBuffer(): void;
42
47
  /**
43
- * Record each executed acceptance command as a verification event (#3322).
48
+ * Record one walk-level verification event for the executed command set (#3322 / #3397).
49
+ * Fail and pass use the same method_fingerprint shape (command list + cwd hash).
44
50
  * Fail-open: missing dest / write errors never change the AC result.
45
51
  * Stdout dest also appends to the same-process buffer so evaluate can
46
52
  * inspect this invocation without re-reading a file.
@@ -7,14 +7,25 @@
7
7
  * attempts just emitted by this verify:ac — it does not treat non-file
8
8
  * dest as no evidence.
9
9
  */
10
- import { randomUUID } from "node:crypto";
10
+ import { createHash } from "node:crypto";
11
11
  import { existsSync, readFileSync } from "node:fs";
12
12
  import { resolve } from "node:path";
13
13
  import { parseRunSummaryJsonl, RunSummaryEmitter, resolveRunSummaryDestination, } from "../run-summary/index.js";
14
14
  import { flagPassAfterFailWithMethodChange, readVerificationAttempts, unresolvedMethodChangePasses, } from "./flag.js";
15
15
  function formatUnresolved(flag) {
16
+ const delta = typeof flag.resolved_command_count_delta === "number"
17
+ ? ` command_count_delta=${flag.resolved_command_count_delta}`
18
+ : "";
16
19
  return (`check_id=${flag.check_id} fail method=${flag.failed_method} ` +
17
- `then pass method=${flag.passed_method} without independent re-derivation`);
20
+ `then pass method=${flag.passed_method} without independent re-derivation${delta}`);
21
+ }
22
+ /**
23
+ * Walk-level method fingerprint: command list + cwd hash (#3397 / #3322).
24
+ * Same shape for fail and pass so an honest product fix does not flag.
25
+ */
26
+ export function methodFingerprintForWalk(commands, cwd) {
27
+ const cwdHash = createHash("sha256").update(cwd, "utf8").digest("hex");
28
+ return `${commands.join("\0")}\0${cwdHash}`;
18
29
  }
19
30
  /** Stable prefix for product-oracle check ids emitted by verify:ac (#3322 / #3337). */
20
31
  export const VERIFY_AC_CHECK_ID_PREFIX = "verify:ac";
@@ -36,11 +47,9 @@ export function verifyAcCheckId(scopeKey) {
36
47
  }
37
48
  /** Same-process verification JSONL when dest is stdout (`-`). */
38
49
  const inProcessVerificationLines = [];
39
- let inProcessStdoutSessionId;
40
50
  /** Test seam: isolate stdout-dest buffer across cases. */
41
51
  export function resetInProcessVerificationBuffer() {
42
52
  inProcessVerificationLines.length = 0;
43
- inProcessStdoutSessionId = undefined;
44
53
  }
45
54
  function inProcessVerificationText() {
46
55
  if (inProcessVerificationLines.length === 0) {
@@ -49,7 +58,8 @@ function inProcessVerificationText() {
49
58
  return inProcessVerificationLines.join("\n");
50
59
  }
51
60
  /**
52
- * Record each executed acceptance command as a verification event (#3322).
61
+ * Record one walk-level verification event for the executed command set (#3322 / #3397).
62
+ * Fail and pass use the same method_fingerprint shape (command list + cwd hash).
53
63
  * Fail-open: missing dest / write errors never change the AC result.
54
64
  * Stdout dest also appends to the same-process buffer so evaluate can
55
65
  * inspect this invocation without re-reading a file.
@@ -63,18 +73,7 @@ export function emitVerifyAcAttempts(options) {
63
73
  const env = options.env ?? process.env;
64
74
  const projectRoot = resolve(options.projectRoot);
65
75
  const dest = resolveRunSummaryDestination(projectRoot, { env });
66
- const explicitSession = options.sessionId?.trim() ||
67
- (typeof env.DEFT_SESSION_ID === "string" ? env.DEFT_SESSION_ID.trim() : "");
68
- let sessionId = explicitSession;
69
- if (!sessionId) {
70
- if (dest.kind === "stdout") {
71
- inProcessStdoutSessionId ??= randomUUID();
72
- sessionId = inProcessStdoutSessionId;
73
- }
74
- else {
75
- sessionId = randomUUID();
76
- }
77
- }
76
+ const sessionId = options.sessionId;
78
77
  const explicitCheck = typeof options.checkId === "string" && options.checkId.trim().length > 0
79
78
  ? options.checkId.trim()
80
79
  : null;
@@ -83,17 +82,26 @@ export function emitVerifyAcAttempts(options) {
83
82
  projectRoot,
84
83
  sessionId,
85
84
  env,
85
+ component: "verify-ac",
86
86
  writeStdout: options.writeStdout,
87
87
  });
88
- for (const run of options.runs) {
89
- const emitted = emitter.emitVerification({
90
- check_id: checkId,
91
- method_fingerprint: `${run.command}\0${run.cwd}`,
92
- outcome: run.ok ? "pass" : "fail",
93
- });
94
- if (dest.kind === "stdout" && emitted.line !== null) {
95
- inProcessVerificationLines.push(JSON.stringify(emitted.line));
96
- }
88
+ // One walk-level event: the method is the command set, not each command.
89
+ // Per-command emit was blind to a shrinking list that kept the first command (#3397).
90
+ const commands = options.runs.map((run) => run.command);
91
+ const firstCwd = options.runs[0]?.cwd ?? "";
92
+ // Hash the cwd key (joined only when cwds differ) so cwd paths never enter
93
+ // the fingerprint; commandCountFromFingerprint can still count commands.
94
+ const cwdKey = options.runs.every((run) => run.cwd === firstCwd)
95
+ ? firstCwd
96
+ : options.runs.map((run) => run.cwd).join("\0");
97
+ const outcome = options.runs.every((run) => run.ok) ? "pass" : "fail";
98
+ const emitted = emitter.emitVerification({
99
+ check_id: checkId,
100
+ method_fingerprint: methodFingerprintForWalk(commands, cwdKey),
101
+ outcome,
102
+ });
103
+ if (dest.kind === "stdout" && emitted.line !== null) {
104
+ inProcessVerificationLines.push(JSON.stringify(emitted.line));
97
105
  }
98
106
  }
99
107
  catch {
@@ -20,7 +20,11 @@ export interface FlaggedMethodChangePass {
20
20
  readonly failed_method: string;
21
21
  readonly passed_method: string;
22
22
  readonly independent_rederivation: boolean;
23
+ /** Pass count minus fail count when both fingerprints carry a command list (#3397). */
24
+ readonly resolved_command_count_delta?: number;
23
25
  }
26
+ /** Command count encoded in a walk fingerprint (`cmd\0cmd\0cwd-or-hash`). */
27
+ export declare function commandCountFromFingerprint(fingerprint: string): number;
24
28
  /** Extract well-formed verification attempts in stream order. */
25
29
  export declare function readVerificationAttempts(lines: readonly RunSummaryLine[]): VerificationAttempt[];
26
30
  /**
@@ -6,6 +6,19 @@
6
6
  * In-place comparison repair then pass is unresolved.
7
7
  */
8
8
  import { parseRunSummaryJsonl } from "../run-summary/index.js";
9
+ /** Command count encoded in a walk fingerprint (`cmd\0cmd\0cwd-or-hash`). */
10
+ export function commandCountFromFingerprint(fingerprint) {
11
+ const parts = fingerprint.split("\0");
12
+ if (parts.length <= 1) {
13
+ return 1;
14
+ }
15
+ const last = parts[parts.length - 1] ?? "";
16
+ const lastIsCwdOrHash = /^[0-9a-f]{8,64}$/i.test(last) ||
17
+ last.includes("/") ||
18
+ last.includes("\\") ||
19
+ last.includes(":");
20
+ return lastIsCwdOrHash ? Math.max(1, parts.length - 1) : parts.length;
21
+ }
9
22
  function asRecord(value) {
10
23
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
11
24
  return null;
@@ -74,11 +87,14 @@ export function flagPassAfterFailWithMethodChange(attempts) {
74
87
  }
75
88
  const other = [...prior].find((method) => method !== attempt.method_fingerprint);
76
89
  if (other !== undefined) {
90
+ const delta = commandCountFromFingerprint(attempt.method_fingerprint) -
91
+ commandCountFromFingerprint(other);
77
92
  flagged.push({
78
93
  check_id: attempt.check_id,
79
94
  failed_method: other,
80
95
  passed_method: attempt.method_fingerprint,
81
96
  independent_rederivation: attempt.independent_rederivation,
97
+ ...(delta !== 0 ? { resolved_command_count_delta: delta } : {}),
82
98
  });
83
99
  }
84
100
  if (attempt.independent_rederivation || other === undefined) {
@@ -5,6 +5,6 @@
5
5
  * by editing the comparison. Deterministic surface is the flagged event.
6
6
  */
7
7
  export { type AcceptanceClause, type AcceptanceClauseReading, type ClauseOutcome, type ClauseWalkReport, type ClauseWalkResult, deriveAcceptanceClauses, formatClauseWalkMessage, isScratchArtifactPath, readAcceptanceClauses, serializeAcceptanceClauses, stampDerivedClausesOnAcceptance, walkAcceptanceClauses, } from "./clauses.js";
8
- export { type EvaluateProductOracleIntegrityOptions, emitVerifyAcAttempts, evaluateProductOracleIntegrity, mergeOracleVerdict, type OracleIntegrityResultFields, type ProductOracleIntegrityVerdict, VERIFY_AC_CHECK_ID_PREFIX, verifyAcCheckId, } from "./evaluate.js";
9
- export { type FlaggedMethodChangePass, flagPassAfterFailFromJsonl, flagPassAfterFailWithMethodChange, readVerificationAttempts, unresolvedMethodChangePasses, type VerificationAttempt, } from "./flag.js";
8
+ export { type EvaluateProductOracleIntegrityOptions, emitVerifyAcAttempts, evaluateProductOracleIntegrity, mergeOracleVerdict, methodFingerprintForWalk, type OracleIntegrityResultFields, type ProductOracleIntegrityVerdict, VERIFY_AC_CHECK_ID_PREFIX, verifyAcCheckId, } from "./evaluate.js";
9
+ export { commandCountFromFingerprint, type FlaggedMethodChangePass, flagPassAfterFailFromJsonl, flagPassAfterFailWithMethodChange, readVerificationAttempts, unresolvedMethodChangePasses, type VerificationAttempt, } from "./flag.js";
10
10
  //# sourceMappingURL=index.d.ts.map