@deftai/directive-core 0.107.0 → 0.109.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 (271) hide show
  1. package/dist/architecture/sor-preflight.js +2 -0
  2. package/dist/cache/operations.js +1 -1
  3. package/dist/check/cached-orchestrator.js +28 -3
  4. package/dist/check/gate-lists.d.ts +14 -0
  5. package/dist/check/gate-lists.js +26 -3
  6. package/dist/check/named-cause.js +18 -3
  7. package/dist/check/session-completed-ac.d.ts +1 -1
  8. package/dist/check/session-completed-ac.js +1 -1
  9. package/dist/codebase/provider.js +7 -1
  10. package/dist/consumer-check-contract/evaluate.d.ts +47 -0
  11. package/dist/consumer-check-contract/evaluate.js +181 -15
  12. package/dist/delivery-attempt/evaluate.d.ts +9 -1
  13. package/dist/delivery-attempt/evaluate.js +69 -0
  14. package/dist/delivery-attempt/handoff.js +1 -1
  15. package/dist/delivery-attempt/index.d.ts +1 -1
  16. package/dist/delivery-attempt/index.js +1 -1
  17. package/dist/deposit/live-procedure-exclusions.d.ts +18 -0
  18. package/dist/deposit/live-procedure-exclusions.js +110 -0
  19. package/dist/deposit/live-procedure-targets.d.ts +45 -0
  20. package/dist/deposit/live-procedure-targets.js +274 -0
  21. package/dist/deposit/python-free.d.ts +6 -0
  22. package/dist/deposit/python-free.js +15 -0
  23. package/dist/deposit/rewrite-deposit-links.d.ts +42 -0
  24. package/dist/deposit/rewrite-deposit-links.js +148 -0
  25. package/dist/deposit/run-stage-content-pack.d.ts +2 -0
  26. package/dist/deposit/run-stage-content-pack.js +3 -0
  27. package/dist/deposit/stage-content-pack.d.ts +17 -0
  28. package/dist/deposit/stage-content-pack.js +91 -0
  29. package/dist/design-critique/citation-grammar.d.ts +60 -0
  30. package/dist/design-critique/citation-grammar.js +303 -0
  31. package/dist/design-critique/completed-arc-record.d.ts +62 -0
  32. package/dist/design-critique/completed-arc-record.js +277 -0
  33. package/dist/freshness/bind.d.ts +32 -3
  34. package/dist/freshness/bind.js +58 -12
  35. package/dist/hooks/classify/host-session-identity.d.ts +89 -0
  36. package/dist/hooks/classify/host-session-identity.js +467 -0
  37. package/dist/hooks/classify/index.d.ts +3 -2
  38. package/dist/hooks/classify/index.js +3 -2
  39. package/dist/hooks/classify/paths.d.ts +9 -0
  40. package/dist/hooks/classify/paths.js +39 -0
  41. package/dist/hooks/classify/stdin.d.ts +5 -0
  42. package/dist/hooks/classify/stdin.js +94 -1
  43. package/dist/hooks/dest-form.d.ts +20 -1
  44. package/dist/hooks/dest-form.js +158 -21
  45. package/dist/hooks/dispatcher.d.ts +58 -3
  46. package/dist/hooks/dispatcher.js +762 -73
  47. package/dist/hooks/fixtures/cases.d.ts +22 -2
  48. package/dist/hooks/fixtures/cases.js +228 -0
  49. package/dist/hooks/git-destructive-log.d.ts +32 -0
  50. package/dist/hooks/git-destructive-log.js +46 -0
  51. package/dist/hooks/index.d.ts +2 -0
  52. package/dist/hooks/index.js +2 -0
  53. package/dist/hooks/scope.js +3 -1
  54. package/dist/hooks/shell-write-targets.d.ts +10 -0
  55. package/dist/hooks/shell-write-targets.js +274 -0
  56. package/dist/hooks/tools.d.ts +20 -2
  57. package/dist/hooks/tools.js +30 -1
  58. package/dist/init-deposit/agent-hooks.d.ts +2 -1
  59. package/dist/init-deposit/agent-hooks.js +8 -1
  60. package/dist/init-deposit/gitignore.d.ts +7 -0
  61. package/dist/init-deposit/gitignore.js +24 -0
  62. package/dist/init-deposit/hook-runtime-travel.d.ts +77 -0
  63. package/dist/init-deposit/hook-runtime-travel.js +269 -0
  64. package/dist/init-deposit/init-deposit.js +3 -0
  65. package/dist/init-deposit/refresh.js +7 -0
  66. package/dist/init-deposit/runtime-writers.d.ts +14 -0
  67. package/dist/init-deposit/runtime-writers.js +33 -0
  68. package/dist/intake/clause-derivation.js +9 -3
  69. package/dist/intake/issue-ingest.d.ts +6 -1
  70. package/dist/intake/issue-ingest.js +83 -4
  71. package/dist/intake/platform-capabilities.d.ts +9 -2
  72. package/dist/intake/platform-capabilities.js +38 -9
  73. package/dist/intake/reconcile-issues.js +13 -13
  74. package/dist/lifecycle/brief-envelope.d.ts +25 -0
  75. package/dist/lifecycle/brief-envelope.js +42 -0
  76. package/dist/lifecycle/index.d.ts +1 -0
  77. package/dist/lifecycle/index.js +1 -0
  78. package/dist/literal-acceptance/evaluate.js +14 -5
  79. package/dist/literal-acceptance/index.d.ts +1 -1
  80. package/dist/literal-acceptance/index.js +1 -1
  81. package/dist/literal-acceptance/run.d.ts +2 -0
  82. package/dist/literal-acceptance/run.js +19 -1
  83. package/dist/orphan-active/candidate-scope.d.ts +53 -0
  84. package/dist/orphan-active/candidate-scope.js +157 -0
  85. package/dist/orphan-active/evaluate.d.ts +52 -0
  86. package/dist/orphan-active/evaluate.js +250 -128
  87. package/dist/orphan-active/index.d.ts +2 -0
  88. package/dist/orphan-active/index.js +2 -0
  89. package/dist/orphan-active/issue-state.d.ts +121 -0
  90. package/dist/orphan-active/issue-state.js +278 -0
  91. package/dist/platform/cursor-managed-runtime.d.ts +95 -0
  92. package/dist/platform/cursor-managed-runtime.js +241 -0
  93. package/dist/platform/index.d.ts +1 -0
  94. package/dist/platform/index.js +1 -0
  95. package/dist/platform/platform-capabilities.d.ts +23 -1
  96. package/dist/platform/platform-capabilities.js +55 -15
  97. package/dist/policy/ceremony-dial.js +5 -9
  98. package/dist/policy/host-hooks.js +4 -9
  99. package/dist/policy/merge-approval-head.js +7 -6
  100. package/dist/policy/org-force-on-migration.js +5 -10
  101. package/dist/policy/plan-extensions.d.ts +14 -2
  102. package/dist/policy/plan-extensions.js +24 -3
  103. package/dist/policy/product-signal.js +5 -10
  104. package/dist/policy/require-human-merge.js +5 -9
  105. package/dist/policy/resolve.js +77 -16
  106. package/dist/policy/value-feedback.js +9 -18
  107. package/dist/pr-closeout-attestable/evaluate.d.ts +80 -0
  108. package/dist/pr-closeout-attestable/evaluate.js +306 -0
  109. package/dist/pr-closeout-attestable/index.d.ts +2 -0
  110. package/dist/pr-closeout-attestable/index.js +2 -0
  111. package/dist/pr-closing-keywords/gh.js +32 -9
  112. package/dist/pr-closing-keywords/main.d.ts +1 -0
  113. package/dist/pr-closing-keywords/main.js +68 -3
  114. package/dist/pr-closing-keywords/types.d.ts +2 -0
  115. package/dist/pr-merge-readiness/gh.js +32 -9
  116. package/dist/pr-protected-issues/gh.d.ts +6 -2
  117. package/dist/pr-protected-issues/gh.js +42 -11
  118. package/dist/pr-wait-mergeable/cascade.d.ts +9 -1
  119. package/dist/pr-wait-mergeable/cascade.js +21 -1
  120. package/dist/pr-wait-mergeable/types.d.ts +2 -0
  121. package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
  122. package/dist/pr-wait-mergeable/wrappers.js +14 -3
  123. package/dist/preflight/evaluate.d.ts +15 -0
  124. package/dist/preflight/evaluate.js +29 -3
  125. package/dist/product-first-done-gate/acceptance-resolver.d.ts +14 -2
  126. package/dist/product-first-done-gate/acceptance-resolver.js +13 -2
  127. package/dist/product-first-done-gate/evaluate.js +23 -9
  128. package/dist/product-first-done-gate/types.js +2 -0
  129. package/dist/release/cli-drift-report.d.ts +68 -0
  130. package/dist/release/cli-drift-report.js +189 -0
  131. package/dist/release/consumer-hard-stops.d.ts +48 -0
  132. package/dist/release/consumer-hard-stops.js +140 -0
  133. package/dist/release/consumer-readiness-disclosure.d.ts +18 -0
  134. package/dist/release/consumer-readiness-disclosure.js +51 -0
  135. package/dist/release/index.d.ts +3 -0
  136. package/dist/release/index.js +3 -0
  137. package/dist/release/issue-state-fetch.d.ts +5 -3
  138. package/dist/release/issue-state-fetch.js +86 -21
  139. package/dist/release/native-steps.js +1 -0
  140. package/dist/release/pipeline.js +74 -0
  141. package/dist/release/run-consumer-readiness.d.ts +15 -0
  142. package/dist/release/run-consumer-readiness.js +24 -0
  143. package/dist/release/types.d.ts +20 -0
  144. package/dist/render/constants.d.ts +4 -0
  145. package/dist/render/constants.js +11 -8
  146. package/dist/render/export-spec.js +31 -5
  147. package/dist/render/index.d.ts +1 -1
  148. package/dist/render/index.js +1 -1
  149. package/dist/render/prd-render.d.ts +4 -1
  150. package/dist/render/prd-render.js +82 -23
  151. package/dist/render/project-render.js +14 -7
  152. package/dist/render/scope-outlook.d.ts +2 -0
  153. package/dist/render/scope-outlook.js +3 -0
  154. package/dist/render/spec-render.js +2 -2
  155. package/dist/resolution/package-manager.d.ts +33 -4
  156. package/dist/resolution/package-manager.js +210 -9
  157. package/dist/review-monitor/constants.d.ts +7 -0
  158. package/dist/review-monitor/constants.js +7 -0
  159. package/dist/review-monitor/github-lease.d.ts +98 -1
  160. package/dist/review-monitor/github-lease.js +186 -4
  161. package/dist/review-monitor/lease-comment.d.ts +36 -0
  162. package/dist/review-monitor/lease-comment.js +103 -2
  163. package/dist/run-summary/types.d.ts +2 -2
  164. package/dist/scm/build-command.d.ts +2 -2
  165. package/dist/scm/build-command.js +2 -2
  166. package/dist/scm/call-shape.d.ts +25 -0
  167. package/dist/scm/call-shape.js +59 -0
  168. package/dist/scm/call.d.ts +7 -4
  169. package/dist/scm/call.js +48 -9
  170. package/dist/scm/design-critique-chip.d.ts +1 -0
  171. package/dist/scm/design-critique-chip.js +22 -6
  172. package/dist/scm/gh-rest.d.ts +34 -13
  173. package/dist/scm/gh-rest.js +168 -15
  174. package/dist/scm/index.d.ts +2 -0
  175. package/dist/scm/index.js +2 -0
  176. package/dist/scm/readiness.d.ts +2 -0
  177. package/dist/scm/readiness.js +28 -4
  178. package/dist/scm/spawn-status.d.ts +33 -0
  179. package/dist/scm/spawn-status.js +53 -0
  180. package/dist/scope/acceptance-evidence.d.ts +1 -1
  181. package/dist/scope/project-definition-sync.js +14 -7
  182. package/dist/scope/transition.js +4 -14
  183. package/dist/session/ac-pass-banking.d.ts +2 -2
  184. package/dist/session/ac-pass-banking.js +2 -2
  185. package/dist/session/git.d.ts +24 -0
  186. package/dist/session/git.js +138 -2
  187. package/dist/session/host-session-owner.d.ts +53 -0
  188. package/dist/session/host-session-owner.js +89 -0
  189. package/dist/session/index.d.ts +1 -0
  190. package/dist/session/index.js +1 -0
  191. package/dist/session/occupancy.d.ts +259 -13
  192. package/dist/session/occupancy.js +877 -33
  193. package/dist/session/ritual-sentinel.d.ts +5 -0
  194. package/dist/session/ritual-sentinel.js +5 -0
  195. package/dist/session/session-ready.d.ts +5 -1
  196. package/dist/session/session-ready.js +98 -8
  197. package/dist/session/session-start.d.ts +5 -1
  198. package/dist/session/session-start.js +32 -17
  199. package/dist/session/toolchain-preflight.d.ts +13 -3
  200. package/dist/session/toolchain-preflight.js +93 -18
  201. package/dist/session/verify-ac-session-cache.d.ts +2 -2
  202. package/dist/session/verify-ac-session-cache.js +2 -2
  203. package/dist/session/verify-session-ritual.d.ts +11 -0
  204. package/dist/session/verify-session-ritual.js +60 -15
  205. package/dist/slice/constants.d.ts +1 -1
  206. package/dist/spec-authority/constants.d.ts +10 -2
  207. package/dist/spec-authority/constants.js +53 -8
  208. package/dist/spec-authority/resolver.d.ts +3 -0
  209. package/dist/spec-authority/resolver.js +55 -7
  210. package/dist/subprocess/max-buffer.d.ts +14 -0
  211. package/dist/subprocess/max-buffer.js +15 -0
  212. package/dist/swarm/complete-cohort.d.ts +2 -0
  213. package/dist/swarm/complete-cohort.js +21 -13
  214. package/dist/swarm/index.d.ts +1 -0
  215. package/dist/swarm/index.js +1 -0
  216. package/dist/swarm/launch-cli.js +53 -33
  217. package/dist/swarm/launch.d.ts +8 -0
  218. package/dist/swarm/launch.js +63 -43
  219. package/dist/swarm/pre-dispatch-cli.js +2 -1
  220. package/dist/swarm/pre-dispatch.js +10 -1
  221. package/dist/swarm/subagent-status-dir.d.ts +27 -0
  222. package/dist/swarm/subagent-status-dir.js +42 -0
  223. package/dist/swarm/worktrees.d.ts +17 -3
  224. package/dist/swarm/worktrees.js +48 -4
  225. package/dist/triage/evaluate/worktrees.js +153 -6
  226. package/dist/triage/scope/mutations-core.d.ts +2 -6
  227. package/dist/triage/scope/mutations-core.js +23 -55
  228. package/dist/triage/scope-drift/add-ignore.js +5 -39
  229. package/dist/triage/subscribe/index.d.ts +2 -6
  230. package/dist/triage/subscribe/index.js +15 -65
  231. package/dist/triage/welcome/writers.js +15 -74
  232. package/dist/umbrella-current-shape/index.d.ts +51 -3
  233. package/dist/umbrella-current-shape/index.js +106 -18
  234. package/dist/validate-content/deposit-required.d.ts +39 -0
  235. package/dist/validate-content/deposit-required.js +147 -0
  236. package/dist/validate-content/index.d.ts +1 -0
  237. package/dist/validate-content/index.js +1 -0
  238. package/dist/validate-content/validate-links.d.ts +2 -3
  239. package/dist/validate-content/validate-links.js +29 -2
  240. package/dist/vbrief-activate/activate.d.ts +7 -2
  241. package/dist/vbrief-activate/activate.js +26 -13
  242. package/dist/vbrief-build/index.d.ts +2 -1
  243. package/dist/vbrief-build/index.js +2 -1
  244. package/dist/vbrief-build/parity-scenarios.js +5 -5
  245. package/dist/vbrief-build/project-definition-io.d.ts +63 -3
  246. package/dist/vbrief-build/project-definition-io.js +357 -31
  247. package/dist/vbrief-build/project-definition-mutation.d.ts +32 -0
  248. package/dist/vbrief-build/project-definition-mutation.js +43 -0
  249. package/dist/vbrief-validate/conformance.d.ts +1 -0
  250. package/dist/vbrief-validate/conformance.js +85 -8
  251. package/dist/vbrief-validate/main.js +2 -0
  252. package/dist/vbrief-validate/precutover.js +5 -10
  253. package/dist/verify-ac/clauses.d.ts +62 -2
  254. package/dist/verify-ac/clauses.js +183 -111
  255. package/dist/verify-ac/evaluate.d.ts +9 -0
  256. package/dist/verify-ac/evaluate.js +32 -9
  257. package/dist/verify-ac/index.d.ts +1 -1
  258. package/dist/verify-ac/index.js +1 -1
  259. package/dist/verify-env/node-runtime.d.ts +8 -4
  260. package/dist/verify-env/node-runtime.js +9 -6
  261. package/dist/verify-env/toolchain-check.d.ts +35 -3
  262. package/dist/verify-env/toolchain-check.js +155 -36
  263. package/dist/verify-source/deposit-closure.d.ts +23 -0
  264. package/dist/verify-source/deposit-closure.js +162 -0
  265. package/dist/verify-source/index.d.ts +2 -0
  266. package/dist/verify-source/index.js +2 -0
  267. package/dist/verify-source/project-definition-mutation-boundary.d.ts +104 -0
  268. package/dist/verify-source/project-definition-mutation-boundary.js +326 -0
  269. package/dist/verify-source/semantic-single-source.d.ts +36 -0
  270. package/dist/verify-source/semantic-single-source.js +349 -0
  271. package/package.json +7 -3
@@ -3,8 +3,10 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
6
- import { resolveBinary } from "./binary.js";
6
+ import { defaultWhich } from "./binary.js";
7
+ import { classifyScmArgv, resolveBinaryForRole } from "./call-shape.js";
7
8
  import { pyRepr } from "./py-format.js";
9
+ import { classifySpawnStatus, formatScmSpawnDiagnostic, isAvailabilitySpawnFailure, } from "./spawn-status.js";
8
10
  export const DEFAULT_TIMEOUT_S = 60;
9
11
  /** Raised when the `"owner/repo"` argument is malformed. */
10
12
  export class InvalidRepoError extends Error {
@@ -23,10 +25,16 @@ export class GhRestError extends Error {
23
25
  endpoint;
24
26
  payload;
25
27
  hint;
28
+ binary;
26
29
  constructor(options) {
27
30
  const hint = options.hint ?? "";
28
- let msg = `gh api failed: endpoint=${pyRepr(options.endpoint)} ` +
29
- `exit=${options.exitCode} stderr=${pyRepr(options.stderr)}`;
31
+ const binary = options.binary ?? "gh";
32
+ const statusClass = classifySpawnStatus(options.exitCode);
33
+ let msg = `${binary} api failed: endpoint=${pyRepr(options.endpoint)} ` + `exit=${options.exitCode}`;
34
+ if (statusClass !== `exit ${options.exitCode}`) {
35
+ msg += ` (${statusClass})`;
36
+ }
37
+ msg += ` stderr=${pyRepr(options.stderr)}`;
30
38
  if (hint.length > 0) {
31
39
  msg += `; hint: ${hint}`;
32
40
  }
@@ -37,30 +45,70 @@ export class GhRestError extends Error {
37
45
  this.endpoint = options.endpoint;
38
46
  this.payload = options.payload;
39
47
  this.hint = hint;
48
+ this.binary = binary;
49
+ }
50
+ }
51
+ function defaultGhSpawn(command, args, options) {
52
+ return spawnSync(command, [...args], options);
53
+ }
54
+ function finalizeGhApiResult(binary, result) {
55
+ const rawStderr = typeof result.stderr === "string" ? result.stderr : "";
56
+ let stderr = rawStderr;
57
+ if (result.status === null && result.error && stderr.trim().length === 0) {
58
+ stderr = result.error.message ?? "";
40
59
  }
60
+ if (isAvailabilitySpawnFailure({
61
+ status: result.status,
62
+ error: result.error,
63
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
64
+ stderr: rawStderr,
65
+ }) &&
66
+ stderr.trim().length === 0) {
67
+ stderr = formatScmSpawnDiagnostic(binary, result.status, stderr, result.error);
68
+ }
69
+ return {
70
+ returncode: result.status ?? 1,
71
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
72
+ stderr,
73
+ binary,
74
+ rawStderr,
75
+ rawStatus: result.status,
76
+ error: result.error,
77
+ };
41
78
  }
42
79
  /** Single subprocess seam invoked by every helper. */
43
80
  export function runGhApi(args, options = {}) {
44
- const binary = resolveBinary(options.whichFn);
81
+ const whichFn = options.whichFn ?? defaultWhich;
82
+ const spawnFn = options.spawnFn ?? defaultGhSpawn;
83
+ const role = classifyScmArgv("api", args);
84
+ let binary = resolveBinaryForRole(role, whichFn);
45
85
  const timeoutMs = options.timeout !== undefined ? Math.round(options.timeout * 1000) : DEFAULT_TIMEOUT_S * 1000;
46
- const result = spawnSync(binary, ["api", ...args], {
86
+ const spawnOpts = {
47
87
  encoding: "utf8",
48
88
  timeout: timeoutMs,
49
89
  env: process.env,
50
90
  maxBuffer: SUBPROCESS_MAX_BUFFER,
51
91
  stdio: ["ignore", "pipe", "pipe"],
52
- });
53
- let stderr = typeof result.stderr === "string" ? result.stderr : "";
54
- // A spawn-level failure (e.g. ENOBUFS when stdout exceeds maxBuffer) yields a
55
- // null status and empty stderr; surface error.message so the GhRestError that
56
- // wraps this never reports a blank reason (#1867).
57
- if (result.status === null && result.error && stderr.trim().length === 0) {
58
- stderr = result.error.message;
92
+ };
93
+ const spawnOnce = (bin) => finalizeGhApiResult(bin, spawnFn(bin, ["api", ...args], spawnOpts));
94
+ let first = spawnOnce(binary);
95
+ if (role === "cached-get" &&
96
+ binary === "ghx" &&
97
+ isAvailabilitySpawnFailure({
98
+ status: first.rawStatus,
99
+ error: first.error,
100
+ stdout: first.stdout,
101
+ stderr: first.rawStderr,
102
+ }) &&
103
+ whichFn("gh") !== null) {
104
+ binary = "gh";
105
+ first = spawnOnce(binary);
59
106
  }
60
107
  return {
61
- returncode: result.status ?? 1,
62
- stdout: typeof result.stdout === "string" ? result.stdout : "",
63
- stderr,
108
+ returncode: first.returncode,
109
+ stdout: first.stdout,
110
+ stderr: first.stderr,
111
+ binary,
64
112
  };
65
113
  }
66
114
  export function splitRepo(repo) {
@@ -83,6 +131,7 @@ function execApi(args, options) {
83
131
  endpoint: options.endpoint,
84
132
  payload: options.payload,
85
133
  hint: options.hint ?? "",
134
+ binary: result.binary ?? "gh",
86
135
  });
87
136
  }
88
137
  const stdout = result.stdout.trim();
@@ -183,6 +232,7 @@ export const PUBLIC_HELPERS = [
183
232
  "restPrView",
184
233
  "restIssueList",
185
234
  "restIssueListPaginated",
235
+ "restIssueListOpenInventory",
186
236
  ];
187
237
  function writeJsonPayload(payload) {
188
238
  const dir = mkdtempSync(join(tmpdir(), "gh_rest_payload_"));
@@ -331,6 +381,109 @@ export function restPrView(repo, n, seams = {}) {
331
381
  whichFn: seams.whichFn,
332
382
  });
333
383
  }
384
+ const OPEN_INVENTORY_MAX_ISSUES = REST_PAGINATION_MAX_PAGES * REST_MAX_PER_PAGE;
385
+ function issueRowOrThrow(item, endpoint) {
386
+ if (typeof item !== "object" || item === null || Array.isArray(item)) {
387
+ throw new GhRestError({
388
+ stderr: "open-issue inventory row is not an object",
389
+ exitCode: 0,
390
+ endpoint,
391
+ payload: null,
392
+ hint: "REST issue list rows must be objects",
393
+ });
394
+ }
395
+ return item;
396
+ }
397
+ /** Flatten `gh api --paginate --slurp` issue-list output (pages or single page). */
398
+ export function flattenOpenInventoryPayload(parsed, endpoint) {
399
+ if (parsed.length === 0) {
400
+ return [];
401
+ }
402
+ const out = [];
403
+ const allPages = parsed.every((item) => Array.isArray(item));
404
+ if (allPages) {
405
+ for (const page of parsed) {
406
+ if (!Array.isArray(page)) {
407
+ continue;
408
+ }
409
+ for (const item of page) {
410
+ const row = issueRowOrThrow(item, endpoint);
411
+ if ("pull_request" in row) {
412
+ continue;
413
+ }
414
+ out.push(row);
415
+ }
416
+ }
417
+ return out;
418
+ }
419
+ for (const item of parsed) {
420
+ const row = issueRowOrThrow(item, endpoint);
421
+ if ("pull_request" in row) {
422
+ continue;
423
+ }
424
+ out.push(row);
425
+ }
426
+ return out;
427
+ }
428
+ /**
429
+ * Complete open-issue inventory in one `gh api --paginate --slurp` subprocess (#3752).
430
+ * Fail-closed on command failure, non-array JSON, buffer exhaustion, or cap hit.
431
+ * Pull-request rows are excluded.
432
+ */
433
+ export function restIssueListOpenInventory(repo, seams = {}) {
434
+ const [owner, name] = splitRepo(repo);
435
+ const endpoint = `repos/${owner}/${name}/issues?state=open&per_page=${REST_MAX_PER_PAGE}`;
436
+ const runner = seams.runGhApiFn ?? runGhApi;
437
+ const result = runner(["--paginate", "--slurp", endpoint], { timeout: 120 });
438
+ if (result.returncode !== 0) {
439
+ throw new GhRestError({
440
+ stderr: result.stderr.trim(),
441
+ exitCode: result.returncode,
442
+ endpoint,
443
+ payload: null,
444
+ hint: "verify gh auth and core REST quota; open-issue inventory must be complete",
445
+ binary: result.binary ?? "gh",
446
+ });
447
+ }
448
+ const stdout = result.stdout.trim();
449
+ if (stdout.length === 0) {
450
+ return [];
451
+ }
452
+ let parsed;
453
+ try {
454
+ parsed = JSON.parse(stdout);
455
+ }
456
+ catch (exc) {
457
+ const message = exc instanceof Error ? exc.message : String(exc);
458
+ throw new GhRestError({
459
+ stderr: `non-JSON open-issue inventory: ${message}`,
460
+ exitCode: 0,
461
+ endpoint,
462
+ payload: null,
463
+ hint: "gh api --paginate --slurp must return a JSON array",
464
+ });
465
+ }
466
+ if (!Array.isArray(parsed)) {
467
+ throw new GhRestError({
468
+ stderr: `unexpected top-level type ${typeof parsed}`,
469
+ exitCode: 0,
470
+ endpoint,
471
+ payload: null,
472
+ hint: "open-issue inventory must be a JSON array",
473
+ });
474
+ }
475
+ const out = flattenOpenInventoryPayload(parsed, endpoint);
476
+ if (out.length >= OPEN_INVENTORY_MAX_ISSUES) {
477
+ throw new GhRestError({
478
+ stderr: `open-issue inventory reached cap ${OPEN_INVENTORY_MAX_ISSUES}; pagination may be incomplete`,
479
+ exitCode: 0,
480
+ endpoint,
481
+ payload: null,
482
+ hint: "repo may exceed supported open-issue count; fail closed rather than truncate",
483
+ });
484
+ }
485
+ return out;
486
+ }
334
487
  export function restIssueListPaginated(repo, options = {}, seams = {}) {
335
488
  const cappedPerPage = Math.min(Math.max(1, options.perPage ?? REST_MAX_PER_PAGE), REST_MAX_PER_PAGE);
336
489
  const [owner, name] = splitRepo(repo);
@@ -2,6 +2,7 @@ export * from "./argv.js";
2
2
  export * from "./binary.js";
3
3
  export * from "./build-command.js";
4
4
  export * from "./call.js";
5
+ export * from "./call-shape.js";
5
6
  export * from "./constants.js";
6
7
  export * from "./design-critique-chip.js";
7
8
  export * from "./errors.js";
@@ -10,4 +11,5 @@ export * from "./main.js";
10
11
  export * from "./py-format.js";
11
12
  export * from "./readiness.js";
12
13
  export * from "./rest-dispatch.js";
14
+ export * from "./spawn-status.js";
13
15
  //# sourceMappingURL=index.d.ts.map
package/dist/scm/index.js CHANGED
@@ -2,6 +2,7 @@ export * from "./argv.js";
2
2
  export * from "./binary.js";
3
3
  export * from "./build-command.js";
4
4
  export * from "./call.js";
5
+ export * from "./call-shape.js";
5
6
  export * from "./constants.js";
6
7
  export * from "./design-critique-chip.js";
7
8
  export * from "./errors.js";
@@ -10,4 +11,5 @@ export * from "./main.js";
10
11
  export * from "./py-format.js";
11
12
  export * from "./readiness.js";
12
13
  export * from "./rest-dispatch.js";
14
+ export * from "./spawn-status.js";
13
15
  //# sourceMappingURL=index.js.map
@@ -41,6 +41,8 @@ export interface ScmReadinessReport {
41
41
  readonly githubAuthMode: string;
42
42
  /** Runtime mode from the #1557a probe. */
43
43
  readonly runtimeMode: string;
44
+ /** Stable id naming why the runtime was classified that way (#3859). */
45
+ readonly runtimeModeReason: string | null;
44
46
  /** Whether an injected token env var is present (value never reported). */
45
47
  readonly injectedTokenPresent: boolean;
46
48
  /** Probe depth used for this report. */
@@ -21,6 +21,7 @@
21
21
  import { spawnSync } from "node:child_process";
22
22
  import { findInjectedToken, GITHUB_AUTH_MODE_HOST_GH, GITHUB_AUTH_MODE_INJECTED_TOKEN, inferGithubAuthMode, validateGithubAuthForWorker, } from "../intake/github-auth-modes.js";
23
23
  import { getPlatformCapabilities, probeRuntimeCapabilities, } from "../intake/platform-capabilities.js";
24
+ import { EXPLICIT_SELECTION_REMEDIATION, GITHUB_AUTH_MODE_ENV, RUNTIME_REASON_CURSOR_MARKER_AMBIGUOUS, } from "../platform/cursor-managed-runtime.js";
24
25
  import { defaultWhich } from "./binary.js";
25
26
  import { BINARY_PREFERENCE } from "./constants.js";
26
27
  import { ScmStubError } from "./errors.js";
@@ -138,6 +139,7 @@ export function probeScmReadiness(options = {}) {
138
139
  authState: "binary-absent",
139
140
  githubAuthMode,
140
141
  runtimeMode: runtimeReport.runtimeMode,
142
+ runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
141
143
  injectedTokenPresent,
142
144
  depth,
143
145
  detail,
@@ -149,9 +151,11 @@ export function probeScmReadiness(options = {}) {
149
151
  }
150
152
  // Injected-token mode without a token is a hard not-ready even before auth status.
151
153
  if (githubAuthMode === GITHUB_AUTH_MODE_INJECTED_TOKEN && !injectedTokenPresent) {
154
+ const ambiguousRuntime = runtimeReport.runtimeModeReason === RUNTIME_REASON_CURSOR_MARKER_AMBIGUOUS;
152
155
  const detail = "injected-token mode requires GH_TOKEN, GITHUB_TOKEN, or GH_ENTERPRISE_TOKEN; " +
153
156
  `binary=${binary} present but SCM-dependent gates skipped ` +
154
- `(runtime_mode=${runtimeReport.runtimeMode})`;
157
+ `(runtime_mode=${runtimeReport.runtimeMode}` +
158
+ `${runtimeReport.runtimeModeReason ? `, reason=${runtimeReport.runtimeModeReason}` : ""})`;
155
159
  return {
156
160
  ready: false,
157
161
  binary,
@@ -159,10 +163,15 @@ export function probeScmReadiness(options = {}) {
159
163
  authState: "missing-token",
160
164
  githubAuthMode,
161
165
  runtimeMode: runtimeReport.runtimeMode,
166
+ runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
162
167
  injectedTokenPresent: false,
163
168
  depth,
164
169
  detail,
165
- remediation: REMEDIATION_MISSING_TOKEN,
170
+ // An ambiguous Cursor runtime is the #3859 case: name the explicit opt-in
171
+ // here, because this is where the operator actually meets the block.
172
+ remediation: ambiguousRuntime
173
+ ? `${EXPLICIT_SELECTION_REMEDIATION}\n\n${REMEDIATION_MISSING_TOKEN}`
174
+ : REMEDIATION_MISSING_TOKEN,
166
175
  skippedGates: [...SCM_DEPENDENT_GATES],
167
176
  login: null,
168
177
  failureKind: "missing_injected_token",
@@ -209,6 +218,7 @@ export function probeScmReadiness(options = {}) {
209
218
  authState: "authenticated",
210
219
  githubAuthMode: deep.githubAuthMode,
211
220
  runtimeMode: runtimeReport.runtimeMode,
221
+ runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
212
222
  injectedTokenPresent,
213
223
  depth,
214
224
  detail: `SCM ready: ${binary} present, ${deep.githubAuthMode} authenticated${loginPart} (deep)`,
@@ -226,6 +236,7 @@ export function probeScmReadiness(options = {}) {
226
236
  authState,
227
237
  githubAuthMode: deep.githubAuthMode,
228
238
  runtimeMode: runtimeReport.runtimeMode,
239
+ runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
229
240
  injectedTokenPresent,
230
241
  depth,
231
242
  detail: `SCM not ready: ${deep.detail}`,
@@ -247,6 +258,7 @@ export function probeScmReadiness(options = {}) {
247
258
  authState: "unknown",
248
259
  githubAuthMode,
249
260
  runtimeMode: runtimeReport.runtimeMode,
261
+ runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
250
262
  injectedTokenPresent,
251
263
  depth,
252
264
  detail,
@@ -268,6 +280,7 @@ export function probeScmReadiness(options = {}) {
268
280
  authState: "unauthenticated",
269
281
  githubAuthMode,
270
282
  runtimeMode: runtimeReport.runtimeMode,
283
+ runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
271
284
  injectedTokenPresent,
272
285
  depth,
273
286
  detail,
@@ -284,6 +297,7 @@ export function probeScmReadiness(options = {}) {
284
297
  authState: "authenticated",
285
298
  githubAuthMode,
286
299
  runtimeMode: runtimeReport.runtimeMode,
300
+ runtimeModeReason: runtimeReport.runtimeModeReason ?? null,
287
301
  injectedTokenPresent,
288
302
  depth,
289
303
  detail: `SCM ready: ${binary} present, ${githubAuthMode} authenticated (shallow)`,
@@ -302,6 +316,7 @@ export function scmReadinessToDict(report) {
302
316
  auth_state: report.authState,
303
317
  github_auth_mode: report.githubAuthMode,
304
318
  runtime_mode: report.runtimeMode,
319
+ runtime_mode_reason: report.runtimeModeReason,
305
320
  injected_token_present: report.injectedTokenPresent,
306
321
  depth: report.depth,
307
322
  detail: report.detail,
@@ -324,7 +339,14 @@ export function formatScmReadinessLines(report) {
324
339
  }
325
340
  lines.push(`[deft scm] ${report.detail}`);
326
341
  if (report.skippedGates.length > 0) {
327
- lines.push(`[deft scm] skipped gates: ${report.skippedGates.join(", ")}`);
342
+ // Name the classification reason next to the skip: a silent skip is what
343
+ // makes this failure costly, because work-selection surfaces go dark (#3859).
344
+ const reason = report.runtimeModeReason ? ` (reason: ${report.runtimeModeReason})` : "";
345
+ lines.push(`[deft scm] skipped gates: ${report.skippedGates.join(", ")}${reason}`);
346
+ if (report.runtimeModeReason === RUNTIME_REASON_CURSOR_MARKER_AMBIGUOUS) {
347
+ lines.push(`[deft scm] runtime is ambiguous; set ${GITHUB_AUTH_MODE_ENV}=host-gh to authorize ` +
348
+ "the host gh credential store on a machine you control (#3859)");
349
+ }
328
350
  }
329
351
  lines.push("[deft scm] run SCM-dependent gates only after auth is ready, or from a matched env; " +
330
352
  "see content/scm/github.md § Mismatched/headless SCM readiness (#2275)");
@@ -387,13 +409,15 @@ export function requireScmReady(options = {}) {
387
409
  if (hermeticAuthSkip) {
388
410
  // Production path uses assertScmBinaryPresent (not test-only).
389
411
  const binary = assertScmBinaryPresent(options.whichFn);
412
+ const hermeticRuntime = options.runtimeReport ?? getPlatformCapabilities();
390
413
  const report = {
391
414
  ready: true,
392
415
  binary,
393
416
  binaryPath: options.whichFn?.(binary) ?? null,
394
417
  authState: "unknown",
395
418
  githubAuthMode: GITHUB_AUTH_MODE_HOST_GH,
396
- runtimeMode: (options.runtimeReport ?? getPlatformCapabilities()).runtimeMode,
419
+ runtimeMode: hermeticRuntime.runtimeMode,
420
+ runtimeModeReason: hermeticRuntime.runtimeModeReason ?? null,
397
421
  injectedTokenPresent: findInjectedToken(options.env ?? process.env) !== null,
398
422
  depth: "shallow",
399
423
  detail: `SCM binary present (${binary}); auth status not checked (hermetic)`,
@@ -0,0 +1,33 @@
1
+ import type { WhichFn } from "./binary.js";
2
+ import type { ScmBinaryRole } from "./call-shape.js";
3
+ /** Windows NTSTATUS STATUS_DLL_INIT_FAILED (#3737). Unsigned and signed 32-bit. */
4
+ export declare const STATUS_DLL_INIT_FAILED = 3221225794;
5
+ export declare function unsignedExitStatus(status: number): number;
6
+ export declare function classifySpawnStatus(status: number | null | undefined, error?: {
7
+ readonly message?: string;
8
+ readonly code?: string;
9
+ }): string;
10
+ export declare function isAvailabilitySpawnFailure(result: {
11
+ readonly status: number | null | undefined;
12
+ readonly error?: {
13
+ readonly message?: string;
14
+ readonly code?: string;
15
+ };
16
+ readonly stdout?: string;
17
+ readonly stderr?: string;
18
+ }): boolean;
19
+ export declare function formatScmSpawnDiagnostic(binary: string, status: number | null | undefined, stderr: string, error?: {
20
+ readonly message?: string;
21
+ readonly code?: string;
22
+ }): string;
23
+ /** Return `gh` when a cached-get ghx spawn failed and live gh is on PATH. */
24
+ export declare function ghxSpawnFallbackBinary(role: ScmBinaryRole, invoked: string, whichFn: WhichFn, failure: {
25
+ readonly status: number | null | undefined;
26
+ readonly error?: {
27
+ readonly message?: string;
28
+ readonly code?: string;
29
+ };
30
+ readonly stdout?: string;
31
+ readonly stderr?: string;
32
+ }): string | null;
33
+ //# sourceMappingURL=spawn-status.d.ts.map
@@ -0,0 +1,53 @@
1
+ /** Windows NTSTATUS STATUS_DLL_INIT_FAILED (#3737). Unsigned and signed 32-bit. */
2
+ export const STATUS_DLL_INIT_FAILED = 0xc0000142;
3
+ const STATUS_DLL_INIT_FAILED_SIGNED = -1073741502;
4
+ export function unsignedExitStatus(status) {
5
+ return status >>> 0;
6
+ }
7
+ export function classifySpawnStatus(status, error) {
8
+ if (typeof status !== "number") {
9
+ if (typeof error?.code === "string" && error.code.length > 0) {
10
+ return error.code;
11
+ }
12
+ if (typeof error?.message === "string" && error.message.trim().length > 0) {
13
+ return error.message.trim();
14
+ }
15
+ return "spawn-failed";
16
+ }
17
+ const unsigned = unsignedExitStatus(status);
18
+ if (unsigned === STATUS_DLL_INIT_FAILED || status === STATUS_DLL_INIT_FAILED_SIGNED) {
19
+ return "0xC0000142 STATUS_DLL_INIT_FAILED";
20
+ }
21
+ if (unsigned >= 0xc0000000) {
22
+ return `0x${unsigned.toString(16).toUpperCase()}`;
23
+ }
24
+ return `exit ${status}`;
25
+ }
26
+ export function isAvailabilitySpawnFailure(result) {
27
+ if (result.error !== undefined) {
28
+ return true;
29
+ }
30
+ if (typeof result.status !== "number") {
31
+ return true;
32
+ }
33
+ const unsigned = unsignedExitStatus(result.status);
34
+ const emptyOut = (result.stdout ?? "").trim().length === 0 && (result.stderr ?? "").trim().length === 0;
35
+ return (emptyOut &&
36
+ (unsigned === STATUS_DLL_INIT_FAILED || result.status === STATUS_DLL_INIT_FAILED_SIGNED));
37
+ }
38
+ export function formatScmSpawnDiagnostic(binary, status, stderr, error) {
39
+ const cls = classifySpawnStatus(status, error);
40
+ const err = stderr.trim();
41
+ if (err.length > 0) {
42
+ return `${binary} failed (${cls}): ${err}`;
43
+ }
44
+ return `${binary} failed (${cls}); stderr empty`;
45
+ }
46
+ /** Return `gh` when a cached-get ghx spawn failed and live gh is on PATH. */
47
+ export function ghxSpawnFallbackBinary(role, invoked, whichFn, failure) {
48
+ if (role !== "cached-get" || invoked !== "ghx" || whichFn("gh") === null) {
49
+ return null;
50
+ }
51
+ return isAvailabilitySpawnFailure(failure) ? "gh" : null;
52
+ }
53
+ //# sourceMappingURL=spawn-status.js.map
@@ -58,7 +58,7 @@ export interface AcceptanceEvidenceGateResult {
58
58
  readonly message: string;
59
59
  readonly reports: readonly CriterionAcceptanceReport[];
60
60
  /** How the #3357 walk obtained AC (#3387). */
61
- readonly servedFrom?: "bank" | "cache" | "executed";
61
+ readonly servedFrom?: "bank" | "cache" | "executed" | "refused";
62
62
  /** Which acceptance check decided the walk (#3497). Undefined when no walk ran. */
63
63
  readonly predicate?: AcceptancePredicate;
64
64
  }
@@ -1,16 +1,23 @@
1
1
  import { dirname, resolve } from "node:path";
2
- import { resolveProjectDefinitionPath } from "../layout/resolve.js";
3
- import { atomicWriteProjectDefinition, loadProjectDefinitionForMutation, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
2
+ import { withProjectDefinitionMutation } from "../vbrief-build/project-definition-mutation.js";
4
3
  import { syncRegistryArtifactAfterScopeMove } from "./registry-artifact-sync.js";
5
4
  /** Fail-closed sync of PROJECT-DEFINITION after a lifecycle move (#1527 / #2131). */
6
5
  export function syncProjectDefinitionAfterScopeMove(scopeData, oldPath, newPath, vbriefRoot, targetStatus) {
7
6
  const projectRoot = dirname(resolve(vbriefRoot));
8
7
  try {
9
- projectDefinitionMutationLock(projectRoot, () => {
10
- const projectDefPath = resolveProjectDefinitionPath(projectRoot);
11
- syncRegistryArtifactAfterScopeMove(projectDefPath, scopeData, oldPath, newPath, vbriefRoot, targetStatus, {
12
- loadForMutation: () => loadProjectDefinitionForMutation(projectRoot),
13
- persist: (path, data) => atomicWriteProjectDefinition(path, data),
8
+ withProjectDefinitionMutation(projectRoot, (mutation) => {
9
+ syncRegistryArtifactAfterScopeMove(
10
+ // The artifact the lock captured, not an independent re-resolution (#3796).
11
+ mutation.artifactPath, scopeData, oldPath, newPath, vbriefRoot, targetStatus, {
12
+ loadForMutation: () => [mutation.load(), mutation.artifactPath],
13
+ persist: (path, data) => {
14
+ // The registry sync echoes back the path it was handed, so a
15
+ // mismatch means something re-resolved the artifact mid-section.
16
+ if (resolve(path) !== resolve(mutation.artifactPath)) {
17
+ throw new Error("refusing to persist PROJECT-DEFINITION to a path other than the locked artifact");
18
+ }
19
+ mutation.persist(data);
20
+ },
14
21
  });
15
22
  });
16
23
  return null;
@@ -5,6 +5,7 @@ import { InstrumentedVbriefCrud, persistCrudMetrics } from "../eval/crud-telemet
5
5
  import { assertProjectionContained, ProjectionContainmentError, } from "../fs/projection-containment.js";
6
6
  import { applyClauseDerivationToPlan, maybeEmitAcceptanceStampFromChange, } from "../intake/clause-derivation.js";
7
7
  import { hasArtifactSuffix } from "../layout/resolve.js";
8
+ import { stampExistingEnvelopes } from "../lifecycle/brief-envelope.js";
8
9
  import { evaluateCompletedPlanConsistency } from "../lifecycle/completed-consistency.js";
9
10
  import { evaluateLiteralAcceptanceFromPlan } from "../literal-acceptance/index.js";
10
11
  import { evaluateAcceptanceActivateGate } from "./acceptance-activate-gate.js";
@@ -45,18 +46,6 @@ function advanceNonTerminalOwnItems(items, targetStatus) {
45
46
  advanceNonTerminalOwnItems(obj.items, targetStatus);
46
47
  }
47
48
  }
48
- /**
49
- * Refresh the document envelope `updated` stamp to match `plan.updated`.
50
- * Stamps whichever of xBRIEFInfo (v0.8) / vBRIEFInfo (v0.6) is present — never creates one (#2862 / #2346).
51
- */
52
- function stampEnvelopeUpdated(data, nowIso) {
53
- for (const key of ["xBRIEFInfo", "vBRIEFInfo"]) {
54
- const env = data[key];
55
- if (typeof env === "object" && env !== null && !Array.isArray(env)) {
56
- env.updated = nowIso;
57
- }
58
- }
59
- }
60
49
  export function runTransition(action, filePath, now = new Date(), options = {}) {
61
50
  if (!(action in TRANSITIONS)) {
62
51
  const valid = Object.keys(TRANSITIONS).sort().join(", ");
@@ -255,7 +244,8 @@ export function runTransition(action, filePath, now = new Date(), options = {})
255
244
  planObj.status = targetStatus;
256
245
  planObj.updated = nowIso;
257
246
  // Keep the envelope clock aligned with plan.updated on every mutating transition (#2862).
258
- stampEnvelopeUpdated(data, nowIso);
247
+ // Stamps whichever envelope is present, never creates one (#3933 / #2346).
248
+ stampExistingEnvelopes(data, nowIso);
259
249
  // Reconcile the completing brief's own plan.items (mirrors #1527 / #2566 registry sync) (#2862).
260
250
  // On complete, items only reach here when #3240 evidence/disposition gate passed.
261
251
  if (OWN_ITEMS_RECONCILE_ACTIONS.has(act)) {
@@ -412,7 +402,7 @@ function restampCompletedBrief(args) {
412
402
  }
413
403
  planObj.status = "completed";
414
404
  planObj.updated = nowIso;
415
- stampEnvelopeUpdated(data, nowIso);
405
+ stampExistingEnvelopes(data, nowIso);
416
406
  stampCompletionMetadata(planObj, projectRoot, nowIso, {
417
407
  completedSessionId: resolveCompletionSessionId(projectRoot),
418
408
  });
@@ -12,8 +12,8 @@
12
12
  */
13
13
  import { type AcPassBankingConfig } from "../policy/ac-pass-banking.js";
14
14
  import type { HardEffortBudget, VerificationDepthPolicy } from "./effort-budget.js";
15
- /** Durable bank ledger directory (gitignored under `.deft/`). */
16
- export declare const AC_PASS_BANK_DIR = ".deft/ac-pass-banks";
15
+ /** Durable bank ledger directory (under already-ignored `.deft/cache/`). */
16
+ export declare const AC_PASS_BANK_DIR = ".deft/cache/ac-pass-banks";
17
17
  export declare const AC_PASS_BANK_SCHEMA_VERSION: 1;
18
18
  /** Env path for optional JSONL run summary (#3282). Unset = silent. */
19
19
  export declare const ENV_RUN_SUMMARY_PATH = "DEFT_RUN_SUMMARY_PATH";
@@ -18,8 +18,8 @@ import { DEFAULT_SURPLUS_THRESHOLD, resolveAcPassBanking, } from "../policy/ac-p
18
18
  import { RunSummaryEmitter } from "../run-summary/emit.js";
19
19
  import { detectHardEffortBudget, formatDeepeningSkippedNote, recommendVerificationDepth, } from "./effort-budget.js";
20
20
  import { gitHead } from "./git.js";
21
- /** Durable bank ledger directory (gitignored under `.deft/`). */
22
- export const AC_PASS_BANK_DIR = ".deft/ac-pass-banks";
21
+ /** Durable bank ledger directory (under already-ignored `.deft/cache/`). */
22
+ export const AC_PASS_BANK_DIR = ".deft/cache/ac-pass-banks";
23
23
  export const AC_PASS_BANK_SCHEMA_VERSION = 1;
24
24
  /** Env path for optional JSONL run summary (#3282). Unset = silent. */
25
25
  export const ENV_RUN_SUMMARY_PATH = "DEFT_RUN_SUMMARY_PATH";
@@ -5,11 +5,35 @@ export interface GitRunResult {
5
5
  }
6
6
  export type GitRunner = (projectRoot: string, args: readonly string[]) => GitRunResult;
7
7
  export declare const defaultGitRunner: GitRunner;
8
+ /**
9
+ * Serve a dispatch's repeated ref reads from one `git` child (#3736).
10
+ *
11
+ * The host mutation gate resolved HEAD, the worktree root, and the branch with
12
+ * a separate spawn each, twice over; on a loaded Windows box a spawn measured a
13
+ * 2.2s p50, so every concurrent agent's hook slowed every other agent's hook.
14
+ * The probe is lazy on purpose: it only fires for a read it can actually
15
+ * answer, so a caller that never asks for context never pays for it.
16
+ */
17
+ export declare function memoizeGitRunner(runGit?: GitRunner): GitRunner;
8
18
  export declare function gitHead(projectRoot: string, runGit?: GitRunner): {
9
19
  head: string | null;
10
20
  error: string | null;
11
21
  };
12
22
  export declare function worktreePath(projectRoot: string, runGit?: GitRunner): string;
23
+ /**
24
+ * Walk from `targetPath` to the nearest existing directory. A write that
25
+ * creates a missing nested file is the common case; git cannot use a
26
+ * nonexistent cwd.
27
+ */
28
+ export declare function existingAncestorDir(targetPath: string): string | null;
29
+ /**
30
+ * Failure-expressing sibling of {@link worktreePath}. Returns null instead of
31
+ * falling back to `startDir` so a missing git tree is distinguishable from
32
+ * success (#3794).
33
+ */
34
+ export declare function worktreePathOrNull(startDir: string, runGit?: GitRunner): string | null;
35
+ /** Absolute `--git-common-dir` for `projectRoot`, or null on failure. */
36
+ export declare function gitCommonDir(projectRoot: string, runGit?: GitRunner): string | null;
13
37
  /** True when `ancestor` is reachable from `descendant` (same commit counts). */
14
38
  export declare function gitIsAncestor(projectRoot: string, ancestor: string, descendant: string, runGit?: GitRunner): boolean | null;
15
39
  /**