@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
@@ -16,6 +16,7 @@ import { spawnSync } from "node:child_process";
16
16
  import { existsSync, readFileSync, statSync } from "node:fs";
17
17
  import { basename, extname, join, resolve } from "node:path";
18
18
  import { hasArtifactSuffix, LIFECYCLE_DIR_NAMES } from "../layout/resolve.js";
19
+ import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
19
20
  // ---------------------------------------------------------------------------
20
21
  // Classification constants
21
22
  // ---------------------------------------------------------------------------
@@ -776,6 +777,7 @@ function gitDiff(projectRoot, baseRef) {
776
777
  cwd: resolve(projectRoot),
777
778
  encoding: "utf8",
778
779
  timeout: 30_000,
780
+ maxBuffer: SUBPROCESS_MAX_BUFFER,
779
781
  });
780
782
  if (result.error) {
781
783
  return [
@@ -107,7 +107,7 @@ export function cachePut(source, key, raw, options = {}) {
107
107
  const rawSize = Buffer.byteLength(rawText, "utf8");
108
108
  // #1870 Greptile P2: account for current-shape sidecar bytes in cap + meta
109
109
  // so usage does not underreport by the cumulative sidecar size.
110
- const plannedSidecar = buildCurrentShapeSidecar(raw);
110
+ const plannedSidecar = buildCurrentShapeSidecar(raw).sidecar;
111
111
  const plannedSidecarText = plannedSidecar !== null
112
112
  ? `${pythonJsonDump({
113
113
  ...plannedSidecar,
@@ -6,6 +6,7 @@ import { readCorePackageVersion } from "../engine-version.js";
6
6
  import { applyProductFirstGateMode, EMPTY_AC_CAUSE, EMPTY_AC_REMEDY, isHygieneGate, isProductAcGate, isSoftEmptyAcText, resolveProductFirstCheckMode, } from "../product-first-done-gate/index.js";
7
7
  import { RunSummaryEmitter } from "../run-summary/emit.js";
8
8
  import { runToolchainPreflight, SKIP_ALL_GATES, } from "../session/toolchain-preflight.js";
9
+ import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
9
10
  import { checkGateCliArgv, cliSpawnPlan, resolveGateDispatch, resolveGlobalCliBin, } from "./cli-native-gates.js";
10
11
  import { evaluateConsumerGateIntegrity, formatConsumerGateIntegrityFailure, } from "./consumer-gate-integrity.js";
11
12
  import { resolveCheckTarget } from "./context.js";
@@ -17,6 +18,7 @@ function captureSpawn(taskBin, args, opts) {
17
18
  cwd: opts.cwd,
18
19
  encoding: "utf8",
19
20
  env: opts.env ?? process.env,
21
+ maxBuffer: SUBPROCESS_MAX_BUFFER,
20
22
  });
21
23
  if (result.error !== undefined) {
22
24
  return {
@@ -40,6 +42,27 @@ function writeLines(lines, stream = "stderr") {
40
42
  write(`${line}\n`);
41
43
  }
42
44
  }
45
+ function firstImpactingMissingFinding(preflight) {
46
+ const missing = preflight?.findings.filter((finding) => !finding.present) ?? [];
47
+ const impacting = missing.filter((finding) => finding.impact !== "none");
48
+ const priority = [
49
+ "node",
50
+ "task",
51
+ "cli_dist",
52
+ "package_manager",
53
+ preflight?.packageManager,
54
+ "npm",
55
+ "pnpm",
56
+ ];
57
+ for (const tool of priority) {
58
+ if (tool === null || tool === undefined)
59
+ continue;
60
+ const finding = impacting.find((candidate) => candidate.tool === tool);
61
+ if (finding !== undefined)
62
+ return finding;
63
+ }
64
+ return impacting[0] ?? missing[0];
65
+ }
43
66
  /**
44
67
  * Run check gates sequentially with content-hash caching (#1713).
45
68
  * Falls back to fail-open execution for undeclared / non-cacheable gates.
@@ -135,6 +158,7 @@ export function dispatchCachedTaskCheck(frameworkRoot, projectRoot, options = {}
135
158
  composedGates: gates,
136
159
  consumerDeposit: target === "check:consumer",
137
160
  which,
161
+ env: options.env,
138
162
  })
139
163
  : options.preflight;
140
164
  const taskPresent = preflight === null || !preflight.findings.some((f) => f.tool === "task" && !f.present);
@@ -165,8 +189,9 @@ export function dispatchCachedTaskCheck(frameworkRoot, projectRoot, options = {}
165
189
  if (allSkipped) {
166
190
  const skipped = gates.map((g) => {
167
191
  const id = checkGateId(g);
168
- const cause = preflight?.findings.find((f) => !f.present)?.cause ?? "toolchain preflight degraded";
169
- const remedy = preflight?.findings.find((f) => !f.present)?.remedy ?? remedyForGate(id, cause);
192
+ const missing = firstImpactingMissingFinding(preflight);
193
+ const cause = missing?.cause ?? "toolchain preflight degraded";
194
+ const remedy = missing?.remedy ?? remedyForGate(id, cause);
170
195
  gateOutcomes.push({ id, status: "skipped", cause, remedy });
171
196
  return { id, cause, remedy };
172
197
  });
@@ -183,7 +208,7 @@ export function dispatchCachedTaskCheck(frameworkRoot, projectRoot, options = {}
183
208
  const gateId = checkGateId(gateSpec);
184
209
  // #3282: skip gates that require missing tools (e.g. pnpm-only suite).
185
210
  if (skipSet.has(gateId)) {
186
- const missing = preflight?.findings.find((f) => !f.present);
211
+ const missing = firstImpactingMissingFinding(preflight);
187
212
  const cause = missing?.cause ?? "toolchain preflight marked gate skippable";
188
213
  const remedy = missing?.remedy ?? remedyForGate(gateId, cause);
189
214
  process.stderr.write(`check: skipping gate ${gateId} (degraded) — cause: ${cause}; remedy: ${remedy}\n`);
@@ -15,6 +15,20 @@ export type CheckGateSpec = string | {
15
15
  * xbrief/completed instead of skipping (#3357).
16
16
  */
17
17
  export declare const PRODUCT_FIRST_AC_GATE: CheckGateSpec;
18
+ /**
19
+ * Candidate-scoped orphan-active for check aggregates (#3893).
20
+ *
21
+ * The unscoped sweep on the merge chokepoint gave a brief stranded by one
22
+ * merge authority over every other open PR, and N stranded briefs made N
23
+ * single-brief lifecycle PRs mutually unmergeable. It was never preventive for
24
+ * the candidate either: that candidate's linked PR still reads
25
+ * `merged_at: null` while its own gate runs.
26
+ *
27
+ * Bare `verify:orphan-active` stays repo-wide for `deft doctor`, manual runs,
28
+ * swarm finalize, and the after-merge `--issue N` DONE gate (#3429); the
29
+ * scoped form still falls back to the full sweep at the delivery tip.
30
+ */
31
+ export declare const ORPHAN_ACTIVE_MERGE_GATE: CheckGateSpec;
18
32
  export declare function checkGateId(spec: CheckGateSpec): string;
19
33
  /** Args for `task … --taskfile <path>` (optional `--` + public CLI flags). */
20
34
  export declare function checkGateSpawnArgs(spec: CheckGateSpec, taskfilePath: string): string[];
@@ -9,6 +9,23 @@ export const PRODUCT_FIRST_AC_GATE = {
9
9
  task: PRODUCT_AC_GATE_ID,
10
10
  args: ["--soft-missing-xbrief"],
11
11
  };
12
+ /**
13
+ * Candidate-scoped orphan-active for check aggregates (#3893).
14
+ *
15
+ * The unscoped sweep on the merge chokepoint gave a brief stranded by one
16
+ * merge authority over every other open PR, and N stranded briefs made N
17
+ * single-brief lifecycle PRs mutually unmergeable. It was never preventive for
18
+ * the candidate either: that candidate's linked PR still reads
19
+ * `merged_at: null` while its own gate runs.
20
+ *
21
+ * Bare `verify:orphan-active` stays repo-wide for `deft doctor`, manual runs,
22
+ * swarm finalize, and the after-merge `--issue N` DONE gate (#3429); the
23
+ * scoped form still falls back to the full sweep at the delivery tip.
24
+ */
25
+ export const ORPHAN_ACTIVE_MERGE_GATE = {
26
+ task: "verify:orphan-active",
27
+ args: ["--changed-only"],
28
+ };
12
29
  export function checkGateId(spec) {
13
30
  return typeof spec === "string" ? spec : spec.task;
14
31
  }
@@ -62,8 +79,9 @@ export const FRAMEWORK_CHECK_GATES = [
62
79
  // --- Fast preflight (seconds–few min) — #3188 ---
63
80
  "verify:branch",
64
81
  "verify:encoding",
82
+ "verify:closing-keywords",
65
83
  "verify:cache-fresh",
66
- "verify:orphan-active",
84
+ ORPHAN_ACTIVE_MERGE_GATE,
67
85
  "verify:completed-write-guard",
68
86
  "verify:license-sync",
69
87
  "verify:contract-drift",
@@ -73,7 +91,10 @@ export const FRAMEWORK_CHECK_GATES = [
73
91
  "verify:rule-ownership",
74
92
  "verify:biome-config",
75
93
  "verify:content-manifest",
94
+ // #3900 check 1: C1 declaration vs staged pack. Checks 2/3 compose C3/C2.
95
+ "verify:deposit-closure",
76
96
  "verify:skill-external-fetch-gate",
97
+ "verify:semantic-single-source",
77
98
  "verify:cursor-tier1",
78
99
  "verify:openclaw-tier1",
79
100
  "verify:go-freeze",
@@ -82,7 +103,9 @@ export const FRAMEWORK_CHECK_GATES = [
82
103
  // #3145: test/source boundary + approved-scope provenance + consumer gate composition
83
104
  "verify:test-boundary",
84
105
  "verify:scope-provenance",
85
- "verify:consumer-check-contract",
106
+ // #3893: fail-closed on merge-chokepoint gate scoping for the composition
107
+ // this repo owns; consumer deposits stay warn-only until `deft update`.
108
+ { task: "verify:consumer-check-contract", args: ["--framework-source"] },
86
109
  // #3362: dead-surface detector (warn-only this release; no --enforce)
87
110
  "verify:telemetry-coverage",
88
111
  "verify:vbrief-conformance",
@@ -111,7 +134,7 @@ export const CONSUMER_CHECK_GATES = [
111
134
  "verify:branch",
112
135
  "verify:cache-fresh",
113
136
  "verify:wip-cap",
114
- "verify:orphan-active",
137
+ ORPHAN_ACTIVE_MERGE_GATE,
115
138
  "verify:completed-write-guard",
116
139
  "doctor",
117
140
  "toolchain:check-consumer",
@@ -13,7 +13,7 @@ const GATE_REMEDIES = {
13
13
  "verify:wip-cap": "Demote stale pending scopes (task scope:demote) or raise plan.policy.wipCap deliberately",
14
14
  doctor: "Run task doctor and follow the named recovery steps",
15
15
  "toolchain:check": "Install missing maintainer tools reported by the gate (go, uv, git, gh, node, pnpm)",
16
- "toolchain:check-consumer": "Install missing consumer tools: go-task, git, gh, node, pnpm (corepack enable && corepack prepare pnpm@latest --activate)",
16
+ "toolchain:check-consumer": "Install the missing consumer tool reported by the gate; use npm from Node or enable pnpm with Corepack as declared by package.json",
17
17
  "ts:check-lane": "Fix lint/type/test failures; re-run task ts:check-lane",
18
18
  "vbrief:validate": "Fix xBRIEF/vBRIEF schema errors reported by the gate",
19
19
  "verify-strategy-output": "Re-run strategy output or fix non-conformant scope filenames / PROJECT-DEFINITION",
@@ -57,6 +57,12 @@ export function extractGateCause(stdout, stderr, exitCode, spawnError, gateId) {
57
57
  useful.push(line);
58
58
  }
59
59
  const gateHint = gateId?.trim() ?? "";
60
+ if (gateHint === "toolchain:check" || gateHint === "toolchain:check-consumer") {
61
+ const toolFailure = useful.find((line) => /^(?:package manager|go|uv|git|gh|node|npm|pnpm|task): (?:NOT FOUND|FAILED|ERROR)\b/i.test(line));
62
+ if (toolFailure !== undefined) {
63
+ return sanitizeCauseLine(toolFailure);
64
+ }
65
+ }
60
66
  if (gateHint.length > 0) {
61
67
  const named = useful.find((line) => line.includes(gateHint));
62
68
  if (named !== undefined) {
@@ -106,14 +112,23 @@ export function remedyForGate(gateId, cause) {
106
112
  return CLI_SPAWN_ERROR_REMEDY;
107
113
  }
108
114
  if (/task binary not found|cannot spawn go-task/i.test(cause)) {
109
- if (gateId.startsWith("verify:") || gateId.startsWith("verify-") || gateId === "doctor") {
115
+ if (gateId.startsWith("verify:") ||
116
+ gateId.startsWith("verify-") ||
117
+ gateId === "doctor" ||
118
+ gateId === "toolchain:check-consumer") {
110
119
  return CLI_SPAWN_ERROR_REMEDY;
111
120
  }
112
121
  return SPAWN_ERROR_REMEDY;
113
122
  }
114
- if (/pnpm binary not found|pnpm: NOT FOUND/i.test(cause)) {
123
+ if (/Unsupported DEFT_PACKAGE_MANAGER value/i.test(cause)) {
124
+ return "Set DEFT_PACKAGE_MANAGER to npm or pnpm, or unset it to use package.json; then re-run the consumer check";
125
+ }
126
+ if (/\bpnpm(?: binary not found|: (?:NOT FOUND|FAILED|ERROR))/i.test(cause)) {
115
127
  return "Enable pnpm: corepack enable && corepack prepare pnpm@latest --activate";
116
128
  }
129
+ if (/\bnpm(?: binary not found|: (?:NOT FOUND|FAILED|ERROR))/i.test(cause)) {
130
+ return "Install or repair Node 20+ (npm is bundled), then re-run the consumer check";
131
+ }
117
132
  return (GATE_REMEDIES[gateId] ??
118
133
  `Re-run the gate for details: task ${gateId} (or task check); fix the reported product/process defect`);
119
134
  }
@@ -8,7 +8,7 @@
8
8
  /** One remediation when check cannot target the just-completed brief (#3357). */
9
9
  export declare const SESSION_COMPLETED_AC_REMEDIATION = "Run task verify:ac -- xbrief/completed/<just-completed>.xbrief.json (a story completed this session; check must not soft-skip) (#3357)";
10
10
  /** Project-relative marker written by scope:complete for this session (#3357). */
11
- export declare const SESSION_COMPLETED_MARKER_REL: readonly [".deft", "last-completed.json"];
11
+ export declare const SESSION_COMPLETED_MARKER_REL: readonly [".deft", "cache", "last-completed.json"];
12
12
  export interface SessionCompletedMarker {
13
13
  readonly path: string;
14
14
  readonly sessionId: string;
@@ -13,7 +13,7 @@ import { parseTimestamp } from "../session/time.js";
13
13
  /** One remediation when check cannot target the just-completed brief (#3357). */
14
14
  export const SESSION_COMPLETED_AC_REMEDIATION = "Run task verify:ac -- xbrief/completed/<just-completed>.xbrief.json (a story completed this session; check must not soft-skip) (#3357)";
15
15
  /** Project-relative marker written by scope:complete for this session (#3357). */
16
- export const SESSION_COMPLETED_MARKER_REL = [".deft", "last-completed.json"];
16
+ export const SESSION_COMPLETED_MARKER_REL = [".deft", "cache", "last-completed.json"];
17
17
  function asRecord(value) {
18
18
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
19
19
  return value;
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { contentRoot } from "../content-root.js";
7
7
  import { resolveProjectDefinitionPath } from "../layout/resolve.js";
8
8
  import { readPlanPolicy } from "../policy/plan-extensions.js";
9
+ import { resolveCaptureFailureStderr, SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
9
10
  import { loadJsonFile } from "../verify-source/code-structure-validate.js";
10
11
  import { CODEBASE_MAP_SCHEMA_PATH } from "./constants.js";
11
12
  import { buildCodebaseMap, CodeStructureConfigError, configErrorToDict, defaultCodeStructurePath, fileSha256, } from "./default-extractor.js";
@@ -547,6 +548,7 @@ function runProviderCommand(command, cwd) {
547
548
  cwd,
548
549
  encoding: "utf8",
549
550
  timeout: 60_000,
551
+ maxBuffer: SUBPROCESS_MAX_BUFFER,
550
552
  stdio: ["ignore", "pipe", "pipe"],
551
553
  });
552
554
  return { returncode: 0, stdout, stderr: "" };
@@ -556,7 +558,11 @@ function runProviderCommand(command, cwd) {
556
558
  return {
557
559
  returncode: typeof e.status === "number" ? e.status : 1,
558
560
  stdout: typeof e.stdout === "string" ? e.stdout : "",
559
- stderr: typeof e.stderr === "string" ? e.stderr : String(e.message ?? err),
561
+ stderr: resolveCaptureFailureStderr({
562
+ captured: typeof e.stderr === "string" ? e.stderr : "",
563
+ status: e.status,
564
+ message: e.message ?? String(err),
565
+ }),
560
566
  };
561
567
  }
562
568
  }
@@ -10,6 +10,20 @@
10
10
  import { type CheckGateSpec } from "../check/gate-lists.js";
11
11
  /** Gates that #3145 requires on consumer check composition (beyond baseline). */
12
12
  export declare const REQUIRED_CONSUMER_ENFORCEMENT_GATES: readonly string[];
13
+ /**
14
+ * Gates whose composition on a check aggregate must carry a scoping argument
15
+ * (#3893).
16
+ *
17
+ * `verify:orphan-active` is repo-wide by default. Composed unscoped on a merge
18
+ * chokepoint it fails a candidate for residue another merge stranded, and N
19
+ * stranded briefs make N single-brief lifecycle PRs mutually unmergeable. It is
20
+ * not preventive for the candidate either: that candidate's linked PR still
21
+ * reads `merged_at: null` while its own gate runs.
22
+ *
23
+ * Fail-closed under `--framework-source`, where this repo owns the composition;
24
+ * a warning elsewhere while `deft update` re-deposits the Taskfile.
25
+ */
26
+ export declare const MERGE_CHOKEPOINT_SCOPED_GATE_ARGS: ReadonlyMap<string, string>;
13
27
  export interface ConsumerCheckContractFinding {
14
28
  readonly gateId: string;
15
29
  readonly surface: "check-task" | "ci-workflow" | "verify-taskfile";
@@ -150,11 +164,44 @@ export declare function stripTaskBodyComments(body: string): string;
150
164
  export declare function taskBodyInvokesCheckOrchestrator(body: string): boolean;
151
165
  /** True when the check / check:consumer / check:framework-source task invokes orchestrator. */
152
166
  export declare function taskfileInvokesCheckOrchestrator(text: string): boolean;
167
+ /** One `deps:` entry plus the YAML nested under it (`vars:` and friends). */
168
+ export interface CheckDepEntry {
169
+ readonly name: string;
170
+ readonly body: string;
171
+ }
172
+ /**
173
+ * Extract `deps:` entries from a go-task task definition, keeping each entry's
174
+ * nested body so an argument-carrying dep (#3893) is distinguishable from a
175
+ * bare one. Best-effort line parser.
176
+ */
177
+ export declare function extractCheckDepEntries(taskfileText: string, taskName: string): CheckDepEntry[];
153
178
  /**
154
179
  * Extract dependency task names from a go-task task definition body snippet.
155
180
  * Best-effort line parser for `deps:` list entries.
156
181
  */
157
182
  export declare function extractCheckDeps(taskfileText: string, taskName: string): string[];
183
+ /**
184
+ * Drop a YAML trailing comment and surrounding quotes from a scalar value.
185
+ * Honours double-quoted `\\"` escapes and single-quoted `''` doubling, so a
186
+ * value carrying a quote is not truncated before the argument that follows.
187
+ */
188
+ export declare function parseYamlScalar(raw: string): string;
189
+ /**
190
+ * Effective `CLI_ARGS` value for a `deps:` entry, or null when it sets none.
191
+ *
192
+ * Reads the key itself rather than the entry text, so the required argument
193
+ * appearing in a comment, a sibling variable, or a descriptive value cannot
194
+ * stand in for the argument the gate actually receives (#3893).
195
+ */
196
+ export declare function depEntryCliArgs(body: string): string | null;
197
+ /**
198
+ * True when the effective `CLI_ARGS` token list carries `requiredArg`.
199
+ *
200
+ * Exact token only: the guarded verbs accept the bare flag, so an equals form
201
+ * like `--changed-only=0` would exit 2 on an unrecognized argument while the
202
+ * contract reported the composition healthy.
203
+ */
204
+ export declare function cliArgsCarry(cliArgs: string | null, requiredArg: string): boolean;
158
205
  /**
159
206
  * Evaluate whether required enforcement gates are composed into consumer check/CI.
160
207
  */
@@ -17,6 +17,22 @@ export const REQUIRED_CONSUMER_ENFORCEMENT_GATES = [
17
17
  "verify:scope-provenance",
18
18
  "verify:consumer-check-contract",
19
19
  ];
20
+ /**
21
+ * Gates whose composition on a check aggregate must carry a scoping argument
22
+ * (#3893).
23
+ *
24
+ * `verify:orphan-active` is repo-wide by default. Composed unscoped on a merge
25
+ * chokepoint it fails a candidate for residue another merge stranded, and N
26
+ * stranded briefs make N single-brief lifecycle PRs mutually unmergeable. It is
27
+ * not preventive for the candidate either: that candidate's linked PR still
28
+ * reads `merged_at: null` while its own gate runs.
29
+ *
30
+ * Fail-closed under `--framework-source`, where this repo owns the composition;
31
+ * a warning elsewhere while `deft update` re-deposits the Taskfile.
32
+ */
33
+ export const MERGE_CHOKEPOINT_SCOPED_GATE_ARGS = new Map([
34
+ ["verify:orphan-active", "--changed-only"],
35
+ ]);
20
36
  /**
21
37
  * Relative path forms accepted for the deft-install canonical Taskfile include
22
38
  * (init-deposit `MINIMAL_TASKFILE` / `CANONICAL_TASKFILE_INCLUDE`).
@@ -575,16 +591,18 @@ export function taskfileInvokesCheckOrchestrator(text) {
575
591
  return false;
576
592
  }
577
593
  /**
578
- * Extract dependency task names from a go-task task definition body snippet.
579
- * Best-effort line parser for `deps:` list entries.
594
+ * Extract `deps:` entries from a go-task task definition, keeping each entry's
595
+ * nested body so an argument-carrying dep (#3893) is distinguishable from a
596
+ * bare one. Best-effort line parser.
580
597
  */
581
- export function extractCheckDeps(taskfileText, taskName) {
598
+ export function extractCheckDepEntries(taskfileText, taskName) {
582
599
  const lines = taskfileText.replace(/\r\n/g, "\n").split("\n");
583
- const deps = [];
600
+ const entries = [];
584
601
  let inTask = false;
585
602
  let taskIndent = 0;
586
603
  let inDeps = false;
587
604
  let depsIndent = 0;
605
+ let current = null;
588
606
  const taskRe = new RegExp(`^${escapeRegExp(taskName)}\\s*:`);
589
607
  for (const raw of lines) {
590
608
  const stripped = raw.trim();
@@ -604,23 +622,142 @@ export function extractCheckDeps(taskfileText, taskName) {
604
622
  if (/^deps\s*:/.test(stripped)) {
605
623
  inDeps = true;
606
624
  depsIndent = indent;
625
+ current = null;
607
626
  continue;
608
627
  }
609
- if (inDeps) {
610
- if (indent <= depsIndent && !stripped.startsWith("-")) {
611
- inDeps = false;
628
+ if (!inDeps)
629
+ continue;
630
+ if (indent <= depsIndent && !stripped.startsWith("-")) {
631
+ inDeps = false;
632
+ current = null;
633
+ }
634
+ else if (stripped.startsWith("-")) {
635
+ // - verify:branch OR - task: verify:branch
636
+ const m = stripped.match(/^-\s+task:\s*["']?([^"'#]+?)["']?\s*(?:#.*)?$/) ??
637
+ stripped.match(/^-\s+["']?([^"'#]+?)["']?\s*(?:#.*)?$/);
638
+ current = m?.[1] === undefined ? null : { name: m[1].trim(), lines: [] };
639
+ if (current !== null) {
640
+ entries.push(current);
612
641
  }
613
- else if (stripped.startsWith("-")) {
614
- // - verify:branch OR - task: verify:branch
615
- const m = stripped.match(/^-\s+task:\s*["']?([^"'#]+?)["']?\s*(?:#.*)?$/) ??
616
- stripped.match(/^-\s+["']?([^"'#]+?)["']?\s*(?:#.*)?$/);
617
- if (m?.[1]) {
618
- deps.push(m[1].trim());
642
+ }
643
+ else if (current !== null) {
644
+ // Keep indentation: the scoping check below needs to tell an immediate
645
+ // `vars:` property from deeper YAML (#3893).
646
+ current.lines.push(raw);
647
+ }
648
+ }
649
+ return entries.map((entry) => ({ name: entry.name, body: entry.lines.join("\n") }));
650
+ }
651
+ /**
652
+ * Extract dependency task names from a go-task task definition body snippet.
653
+ * Best-effort line parser for `deps:` list entries.
654
+ */
655
+ export function extractCheckDeps(taskfileText, taskName) {
656
+ return extractCheckDepEntries(taskfileText, taskName).map((entry) => entry.name);
657
+ }
658
+ /**
659
+ * Drop a YAML trailing comment and surrounding quotes from a scalar value.
660
+ * Honours double-quoted `\\"` escapes and single-quoted `''` doubling, so a
661
+ * value carrying a quote is not truncated before the argument that follows.
662
+ */
663
+ export function parseYamlScalar(raw) {
664
+ const text = raw.trim();
665
+ if (text.startsWith('"')) {
666
+ let out = "";
667
+ for (let i = 1; i < text.length; i += 1) {
668
+ const ch = text[i];
669
+ if (ch === "\\" && i + 1 < text.length) {
670
+ out += text[i + 1];
671
+ i += 1;
672
+ continue;
673
+ }
674
+ if (ch === '"')
675
+ return out;
676
+ out += ch;
677
+ }
678
+ return out;
679
+ }
680
+ if (text.startsWith("'")) {
681
+ let out = "";
682
+ for (let i = 1; i < text.length; i += 1) {
683
+ const ch = text[i];
684
+ if (ch === "'") {
685
+ if (text[i + 1] === "'") {
686
+ out += "'";
687
+ i += 1;
688
+ continue;
619
689
  }
690
+ return out;
620
691
  }
692
+ out += ch;
621
693
  }
694
+ return out;
622
695
  }
623
- return deps;
696
+ const comment = text.indexOf(" #");
697
+ return (comment === -1 ? text : text.slice(0, comment)).trim();
698
+ }
699
+ /**
700
+ * Inline-flow `vars: { CLI_ARGS: ... }` scalar. Quote escapes are honoured so a
701
+ * value carrying a quote is not truncated before the argument that follows.
702
+ */
703
+ const INLINE_CLI_ARGS_RE = /CLI_ARGS\s*:\s*("(?:\\.|[^"\\])*"|'(?:''|[^'])*'|[^,}]*)/;
704
+ /**
705
+ * Effective `CLI_ARGS` value for a `deps:` entry, or null when it sets none.
706
+ *
707
+ * Reads the key itself rather than the entry text, so the required argument
708
+ * appearing in a comment, a sibling variable, or a descriptive value cannot
709
+ * stand in for the argument the gate actually receives (#3893).
710
+ */
711
+ export function depEntryCliArgs(body) {
712
+ let varsIndent = null;
713
+ let propIndent = null;
714
+ let value = null;
715
+ for (const rawLine of body.split("\n")) {
716
+ const line = rawLine.trim();
717
+ if (line.length === 0 || line.startsWith("#"))
718
+ continue;
719
+ const indent = rawLine.length - rawLine.trimStart().length;
720
+ if (varsIndent !== null && indent <= varsIndent) {
721
+ varsIndent = null;
722
+ propIndent = null;
723
+ }
724
+ const varsBlock = /^vars\s*:\s*(.*)$/.exec(line);
725
+ if (varsBlock !== null) {
726
+ varsIndent = indent;
727
+ propIndent = null;
728
+ // Inline flow form: vars: { CLI_ARGS: "--changed-only" }
729
+ const inline = INLINE_CLI_ARGS_RE.exec(varsBlock[1] ?? "");
730
+ if (inline !== null) {
731
+ value = parseYamlScalar(inline[1] ?? "");
732
+ }
733
+ continue;
734
+ }
735
+ if (varsIndent === null)
736
+ continue;
737
+ propIndent ??= indent;
738
+ if (indent !== propIndent)
739
+ continue;
740
+ const cliArgs = /^CLI_ARGS\s*:\s*(.*)$/.exec(line);
741
+ if (cliArgs !== null) {
742
+ value = parseYamlScalar(cliArgs[1] ?? "");
743
+ }
744
+ }
745
+ return value;
746
+ }
747
+ /**
748
+ * True when the effective `CLI_ARGS` token list carries `requiredArg`.
749
+ *
750
+ * Exact token only: the guarded verbs accept the bare flag, so an equals form
751
+ * like `--changed-only=0` would exit 2 on an unrecognized argument while the
752
+ * contract reported the composition healthy.
753
+ */
754
+ export function cliArgsCarry(cliArgs, requiredArg) {
755
+ if (cliArgs === null)
756
+ return false;
757
+ return cliArgs
758
+ .split(/\s+/)
759
+ .filter((token) => token.length > 0)
760
+ .some((token) => token === requiredArg);
624
761
  }
625
762
  function remediationForMissing(gateId, surface) {
626
763
  return (`Add \`${gateId}\` to the consumer ${surface} composition (Taskfile check deps ` +
@@ -712,12 +849,41 @@ export function evaluateConsumerCheckContract(projectRoot, options = {}) {
712
849
  }
713
850
  let anyAggregateDefined = false;
714
851
  for (const target of checkTargets) {
715
- const deps = extractCheckDeps(rootTaskfile, target);
852
+ const entries = extractCheckDepEntries(rootTaskfile, target);
853
+ const deps = entries.map((entry) => entry.name);
716
854
  const body = extractTaskBody(rootTaskfile, target);
717
855
  const defined = deps.length > 0 || body.trim().length > 0;
718
856
  if (!defined)
719
857
  continue;
720
858
  anyAggregateDefined = true;
859
+ // Checked even on a trusted orchestrator body: a listed dep still runs, and
860
+ // unscoped on a merge chokepoint is the #3893 defect.
861
+ for (const [gateId, requiredArg] of MERGE_CHOKEPOINT_SCOPED_GATE_ARGS) {
862
+ // Every occurrence must carry the argument: a scoped entry followed by a
863
+ // bare duplicate still runs the repo-wide scan (#3893).
864
+ const listed = entries.filter((row) => row.name === gateId);
865
+ if (listed.length === 0)
866
+ continue;
867
+ if (listed.every((row) => cliArgsCarry(depEntryCliArgs(row.body), requiredArg)))
868
+ continue;
869
+ const finding = {
870
+ gateId,
871
+ surface: "check-task",
872
+ detail: `aggregate '${target}' composes ${gateId} without ${requiredArg}: an unscoped ` +
873
+ "repo-wide lifecycle scan on a merge chokepoint fails a candidate for residue " +
874
+ "another merge stranded (#3893)",
875
+ remediation: `Pass ${requiredArg} to ${gateId} in the '${target}' deps (go-task object form, ` +
876
+ `vars: CLI_ARGS: "${requiredArg}"). Bare ${gateId} stays repo-wide for doctor, ` +
877
+ "manual runs, and the after-merge --issue N run. " +
878
+ "See content/docs/consumer-check-contract.md (#3893).",
879
+ };
880
+ if (options.frameworkSource === true) {
881
+ findings.push(finding);
882
+ }
883
+ else {
884
+ soft.push(finding);
885
+ }
886
+ }
721
887
  const trustThis = taskInvokesOrchestrator(target) && verifyDefinesRequired;
722
888
  if (trustThis)
723
889
  continue;
@@ -4,7 +4,7 @@
4
4
  * Loads durable unit state and returns a single deterministic decision
5
5
  * before every delivery dispatch or retry.
6
6
  */
7
- import type { DeliveryUnitLedger, PreDispatchInput, PreDispatchResult } from "./types.js";
7
+ import type { DeliveryBudgetPolicy, DeliveryUnitLedger, PreDispatchInput, PreDispatchResult } from "./types.js";
8
8
  /**
9
9
  * Evaluate whether a delivery dispatch may proceed.
10
10
  *
@@ -15,6 +15,14 @@ import type { DeliveryUnitLedger, PreDispatchInput, PreDispatchResult } from "./
15
15
  * That keeps bounded override quota for dispatches that truly need it.
16
16
  */
17
17
  export declare function evaluatePreDispatch(ledger: DeliveryUnitLedger, input: PreDispatchInput): PreDispatchResult;
18
+ /**
19
+ * Elapsed budget for a run already queued or running (#3983).
20
+ * Pre-dispatch cannot see this case: an active attempt is DENY_DUPLICATE_ACTIVE.
21
+ */
22
+ export declare function evaluateInFlight(ledger: DeliveryUnitLedger, input?: {
23
+ readonly now?: string;
24
+ readonly policy?: Partial<DeliveryBudgetPolicy>;
25
+ }): PreDispatchResult;
18
26
  /**
19
27
  * Convenience: evaluate against ledger, and if blocked, return handoff suitable
20
28
  * for persistence via markBlocked + saveUnitLedger.