@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
@@ -6,7 +6,7 @@
6
6
  * the missing derive step.
7
7
  */
8
8
  import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter, } from "../run-summary/index.js";
9
- import { deriveAcceptanceClauses, readAcceptanceClauses, serializeAcceptanceClauses, } from "../verify-ac/clauses.js";
9
+ import { collectPlanItemAcceptanceSurface, deriveAcceptanceClauses, readAcceptanceClauses, serializeAcceptanceClauses, } from "../verify-ac/clauses.js";
10
10
  /** One-line remediation when a stamp has no statement-traceable clause (#3398). */
11
11
  export const CLAUSE_STAMP_IMPLEMENTATION_ONLY_REMEDIATION = "derive clauses from the statement's testable constraints";
12
12
  export const MISSING_AMBIGUITY_ATTESTATION_CAUSE = "missing_ambiguity_attestation";
@@ -72,7 +72,11 @@ export function collectTaskStatementFromPlan(plan) {
72
72
  continue;
73
73
  }
74
74
  const narrative = asRecord(rec.narrative);
75
- const text = narrative?.Acceptance;
75
+ const declared = narrative?.Acceptance;
76
+ // #3826: criteria commonly live in item.title with an empty narrative object.
77
+ // Reading only narrative.Acceptance left them out of the statement entirely,
78
+ // so a clause derived from a title could not trace back to it.
79
+ const text = isNonEmptyString(declared) ? declared : rec.title;
76
80
  if (isNonEmptyString(text)) {
77
81
  parts.push(text.trim());
78
82
  }
@@ -296,7 +300,9 @@ export function applyClauseDerivationToPlan(plan, options = {}) {
296
300
  notice: "",
297
301
  };
298
302
  }
299
- const clauses = deriveAcceptanceClauses(collectTaskStatementFromPlan(plan));
303
+ const clauses = deriveAcceptanceClauses(collectTaskStatementFromPlan(plan), {
304
+ itemSurface: collectPlanItemAcceptanceSurface(plan),
305
+ });
300
306
  if (clauses.length === 0) {
301
307
  return { applied: false, clauses: [], notice: "" };
302
308
  }
@@ -1,5 +1,5 @@
1
1
  import { type ScanFlag } from "../cache/scanner.js";
2
- import { type IssueComment as ShapeIssueComment } from "../umbrella-current-shape/index.js";
2
+ import { type CurrentShapeNullReason, type IssueComment as ShapeIssueComment } from "../umbrella-current-shape/index.js";
3
3
  import { LEGACY_ARTIFACT_SUFFIX, LEGACY_INFO_ROOT_KEY, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY } from "../xbrief-migrate/constants.js";
4
4
  import { type ScmCallFn } from "./reconcile-issues.js";
5
5
  /** Reference type pointing at the canonical current-shape comment permalink (#1870). */
@@ -42,6 +42,11 @@ export declare function mapIngestCommentsToShapeComments(comments: readonly Issu
42
42
  export declare function selectIngestCurrentShape(comments: readonly IssueComment[]): (ShapeIssueComment & {
43
43
  pass: number;
44
44
  }) | null;
45
+ /**
46
+ * Classify why `selectIngestCurrentShape` returned null so ingest can report a
47
+ * reason instead of silently omitting the CurrentShape narrative (#3934).
48
+ */
49
+ export declare function describeIngestCurrentShapeNull(comments: readonly IssueComment[]): CurrentShapeNullReason;
45
50
  /** Count maintainer current-shape comments (lint surface for !=1). */
46
51
  export declare function countIngestCurrentShapeComments(comments: readonly IssueComment[]): number;
47
52
  export declare function bodyControlCharacterLabels(body: string): string[];
@@ -2,6 +2,7 @@ import { lstatSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSyn
2
2
  import { basename, dirname, join, resolve } from "node:path";
3
3
  import { cacheGet } from "../cache/operations.js";
4
4
  import { scan } from "../cache/scanner.js";
5
+ import { assertCompletedArcAllowsIngest, DesignCritiqueIngestBlockedError, } from "../design-critique/completed-arc-record.js";
5
6
  import { assertWriteTargetSafe, ProjectionContainmentError } from "../fs/projection-containment.js";
6
7
  import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
7
8
  import { captureAndAttachLiteralAcceptance } from "../literal-acceptance/index.js";
@@ -10,7 +11,7 @@ import { stampAcceptanceFromLiteralCapture } from "../product-first-done-gate/in
10
11
  import { call } from "../scm/call.js";
11
12
  import { resolveProjectRoot } from "../scope/project-context.js";
12
13
  import { resolveProjectRepo } from "../slice/project-context.js";
13
- import { countMaintainerCurrentShapeComments, mapIssueCommentEntry, RAW_ISSUE_COMMENTS_KEY, selectCurrentShapeComment, } from "../umbrella-current-shape/index.js";
14
+ import { countMaintainerCurrentShapeComments, describeCurrentShapeNull, mapIssueCommentEntry, RAW_ISSUE_COMMENTS_KEY, selectCurrentShapeComment, } from "../umbrella-current-shape/index.js";
14
15
  import { slugify, TODAY } from "../vbrief-build/build.js";
15
16
  import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
16
17
  import { stampDerivedClausesOnAcceptance } from "../verify-ac/clauses.js";
@@ -65,6 +66,13 @@ export function mapIngestCommentsToShapeComments(comments) {
65
66
  export function selectIngestCurrentShape(comments) {
66
67
  return selectCurrentShapeComment(mapIngestCommentsToShapeComments(comments));
67
68
  }
69
+ /**
70
+ * Classify why `selectIngestCurrentShape` returned null so ingest can report a
71
+ * reason instead of silently omitting the CurrentShape narrative (#3934).
72
+ */
73
+ export function describeIngestCurrentShapeNull(comments) {
74
+ return describeCurrentShapeNull(mapIngestCommentsToShapeComments(comments));
75
+ }
68
76
  /** Count maintainer current-shape comments (lint surface for !=1). */
69
77
  export function countIngestCurrentShapeComments(comments) {
70
78
  return countMaintainerCurrentShapeComments(mapIngestCommentsToShapeComments(comments));
@@ -408,6 +416,17 @@ export function buildIssueVbrief(issue, status, repoUrl, options = {}) {
408
416
  // Scan shape body under the same fail-closed contract as Overview (#2306).
409
417
  narratives.CurrentShape = scanUntrustedIngestText(number, currentShape.body);
410
418
  }
419
+ else {
420
+ // #3934: a thread whose only shape comments failed the #2307 authorship
421
+ // filter is otherwise indistinguishable from a thread that never had one,
422
+ // so a cache-reading agent falls back to the stale body. The note is
423
+ // framework-authored and carries comment ids plus normalized author
424
+ // associations only -- no comment text -- so it needs no quarantine scan.
425
+ const shapeNullReason = describeIngestCurrentShapeNull(commentThread);
426
+ if (shapeNullReason.kind === "non-maintainer-shape") {
427
+ narratives.CurrentShapeUnavailable = shapeNullReason.message;
428
+ }
429
+ }
411
430
  if (labelNames.length > 0) {
412
431
  narratives.Labels = labelNames.join(", ");
413
432
  }
@@ -639,7 +658,8 @@ export function fetchSingleIssue(repo, number, options = {}) {
639
658
  return null;
640
659
  }
641
660
  if (result.returncode !== 0) {
642
- process.stderr.write(`Error: gh CLI failed fetching #${number}: ${result.stderr.trim()}\n`);
661
+ const invoked = result.args?.[0] ?? "gh";
662
+ process.stderr.write(`Error: ${invoked} failed fetching #${number}: ${result.stderr.trim()}\n`);
643
663
  return null;
644
664
  }
645
665
  try {
@@ -703,6 +723,41 @@ function resolveIngestProjectRoot(vbriefDir, cwd) {
703
723
  }
704
724
  return null;
705
725
  }
726
+ function issueLabelNames(issue) {
727
+ const labels = issue.labels;
728
+ if (!Array.isArray(labels))
729
+ return [];
730
+ const names = [];
731
+ for (const lbl of labels) {
732
+ if (typeof lbl === "string") {
733
+ names.push(lbl);
734
+ continue;
735
+ }
736
+ if (lbl !== null && typeof lbl === "object" && !Array.isArray(lbl)) {
737
+ const name = lbl.name;
738
+ if (typeof name === "string")
739
+ names.push(name);
740
+ }
741
+ }
742
+ return names;
743
+ }
744
+ function threadCommentsFromIssue(issue) {
745
+ const raw = issue[ISSUE_COMMENT_THREAD_KEY];
746
+ if (!Array.isArray(raw))
747
+ return [];
748
+ const out = [];
749
+ for (const entry of raw) {
750
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry))
751
+ continue;
752
+ const rec = entry;
753
+ const id = Number(rec.id);
754
+ if (!Number.isSafeInteger(id) || id <= 0)
755
+ continue;
756
+ const body = typeof rec.body === "string" ? rec.body : "";
757
+ out.push({ id, body });
758
+ }
759
+ return out;
760
+ }
706
761
  export function ingestOne(issue, options) {
707
762
  const number = Number(issue.number);
708
763
  const refs = options.existingRefs ?? scanProvenanceRefs(options.vbriefDir);
@@ -719,6 +774,11 @@ export function ingestOne(issue, options) {
719
774
  cwd: options.cwd,
720
775
  cacheRoot: options.cacheRoot,
721
776
  });
777
+ assertCompletedArcAllowsIngest({
778
+ issueNumber: number,
779
+ labels: issueLabelNames(enriched),
780
+ comments: threadCommentsFromIssue(enriched),
781
+ });
722
782
  const emissionLayout = resolveIngestEmissionLayout(options.vbriefDir);
723
783
  const [vbrief, folder] = buildIssueVbrief(enriched, options.status, options.repoUrl, {
724
784
  infoRootKey: emissionLayout.infoRootKey,
@@ -774,6 +834,7 @@ export function ingestBulk(issues, options) {
774
834
  duplicate: [],
775
835
  dryrun: [],
776
836
  failed: [],
837
+ blocked: [],
777
838
  notices: [],
778
839
  };
779
840
  for (const issue of filtered) {
@@ -790,6 +851,11 @@ export function ingestBulk(issues, options) {
790
851
  process.stderr.write(`${exc.message}\n`);
791
852
  continue;
792
853
  }
854
+ if (exc instanceof DesignCritiqueIngestBlockedError) {
855
+ summary.blocked.push(`#${exc.issueNumber}`);
856
+ process.stderr.write(`${exc.message}\n`);
857
+ continue;
858
+ }
793
859
  throw exc;
794
860
  }
795
861
  const [result, path, msg] = ingested;
@@ -861,7 +927,8 @@ export function issueIngestMain(args) {
861
927
  const duplicate = summary.duplicate;
862
928
  const dryrun = summary.dryrun;
863
929
  const failed = summary.failed ?? [];
864
- process.stdout.write(`issue:ingest bulk summary: ${created.length} created, ${duplicate.length} duplicate, ${dryrun.length} dry-run, ${failed.length} refused (total considered: ${summary.total})\n`);
930
+ const blocked = summary.blocked ?? [];
931
+ process.stdout.write(`issue:ingest bulk summary: ${created.length} created, ${duplicate.length} duplicate, ${dryrun.length} dry-run, ${failed.length} refused, ${blocked.length} blocked (total considered: ${summary.total})\n`);
865
932
  for (const entry of created) {
866
933
  process.stdout.write(` CREATED ${entry}\n`);
867
934
  }
@@ -877,8 +944,16 @@ export function issueIngestMain(args) {
877
944
  for (const entry of failed) {
878
945
  process.stdout.write(` REFUSED ${entry} (quarantine scanner hard-fail; nothing written)\n`);
879
946
  }
947
+ for (const entry of blocked) {
948
+ process.stdout.write(` BLOCKED ${entry} (design-critique completed-arc record; nothing written)\n`);
949
+ }
880
950
  // #2306: fail closed on any quarantine hard-fail in the batch.
881
- return failed.length > 0 ? 2 : 0;
951
+ // Design-critique protocol holds are exit 1, not the scanner fatal path.
952
+ if (failed.length > 0)
953
+ return 2;
954
+ if (blocked.length > 0)
955
+ return 1;
956
+ return 0;
882
957
  }
883
958
  const issue = fetchIssue(repo, args.number, { cwd: projectRoot });
884
959
  if (issue === null) {
@@ -901,6 +976,10 @@ export function issueIngestMain(args) {
901
976
  process.stderr.write(`${exc.message}\n`);
902
977
  return 2;
903
978
  }
979
+ if (exc instanceof DesignCritiqueIngestBlockedError) {
980
+ process.stderr.write(`${exc.message}\n`);
981
+ return 1;
982
+ }
904
983
  throw exc;
905
984
  }
906
985
  process.stdout.write(`${msg}\n`);
@@ -1,11 +1,18 @@
1
1
  /** Minimal runtime capability probe for github_auth_modes (#1557a / #1784). */
2
+ import { type ManagedRuntimeProbe } from "../platform/cursor-managed-runtime.js";
2
3
  export declare const RUNTIME_MODE_LOCAL_UNSANDBOXED = "local-unsandboxed";
3
4
  export declare const RUNTIME_MODE_CURSOR_NATIVE_SANDBOX = "cursor-native-sandbox";
4
5
  export declare const RUNTIME_MODE_CLOUD_HEADLESS = "cloud-headless";
5
6
  export declare const KNOWN_RUNTIME_MODES: Set<string>;
6
7
  export interface RuntimeCapabilityReport {
7
8
  readonly runtimeMode: string;
9
+ /** Stable id naming why `runtimeMode` was chosen; surfaced by scm:status (#3859). */
10
+ readonly runtimeModeReason?: string;
8
11
  }
9
- export declare function probeRuntimeCapabilities(environ?: NodeJS.ProcessEnv): RuntimeCapabilityReport;
10
- export declare function getPlatformCapabilities(): RuntimeCapabilityReport;
12
+ export interface ProbeRuntimeCapabilityOptions {
13
+ /** Injectable managed-runtime probe (#3859). Defaults to the metadata read. */
14
+ readonly managedRuntimeProbe?: ManagedRuntimeProbe;
15
+ }
16
+ export declare function probeRuntimeCapabilities(environ?: NodeJS.ProcessEnv, options?: ProbeRuntimeCapabilityOptions): RuntimeCapabilityReport;
17
+ export declare function getPlatformCapabilities(options?: ProbeRuntimeCapabilityOptions): RuntimeCapabilityReport;
11
18
  //# sourceMappingURL=platform-capabilities.d.ts.map
@@ -1,4 +1,5 @@
1
1
  /** Minimal runtime capability probe for github_auth_modes (#1557a / #1784). */
2
+ import { hasExplicitHostGhSelection, probeManagedRuntime, RUNTIME_REASON_CI_MARKER, RUNTIME_REASON_CURSOR_MARKER_AMBIGUOUS, RUNTIME_REASON_CURSOR_SANDBOX_MARKER, RUNTIME_REASON_EXPLICIT_HOST_GH, RUNTIME_REASON_MANAGED_RUNTIME_PROBE, RUNTIME_REASON_NO_RUNTIME_MARKER, } from "../platform/cursor-managed-runtime.js";
2
3
  export const RUNTIME_MODE_LOCAL_UNSANDBOXED = "local-unsandboxed";
3
4
  export const RUNTIME_MODE_CURSOR_NATIVE_SANDBOX = "cursor-native-sandbox";
4
5
  export const RUNTIME_MODE_CLOUD_HEADLESS = "cloud-headless";
@@ -11,24 +12,52 @@ const TRUTHY = new Set(["1", "true", "yes", "on"]);
11
12
  function isTruthy(value) {
12
13
  return value !== undefined && TRUTHY.has(value.trim().toLowerCase());
13
14
  }
14
- function classifyRuntimeMode(environ = process.env) {
15
+ /**
16
+ * Classify the execution runtime.
17
+ *
18
+ * Ordering is load-bearing (#3859):
19
+ * 1. CI markers, so a CI runner is never reclassified by a Cursor signal.
20
+ * 2. A positive Cursor managed-runtime read, above every other Cursor signal.
21
+ * 3. Cursor native sandbox.
22
+ * 4. A Cursor marker with no managed read leaves the runtime AMBIGUOUS. Only
23
+ * an explicit operator/dispatcher selection resolves it to local; absent
24
+ * that, the verdict is unchanged from before #3859.
25
+ */
26
+ function classifyRuntimeMode(environ = process.env, options = {}) {
15
27
  for (const key of ["GROK_BUILD", "GITHUB_ACTIONS", "CI", "BUILDKITE", "DEFT_AGENT_RUNTIME"]) {
16
28
  if (isTruthy(environ[key])) {
17
- return RUNTIME_MODE_CLOUD_HEADLESS;
29
+ return { mode: RUNTIME_MODE_CLOUD_HEADLESS, reason: RUNTIME_REASON_CI_MARKER };
18
30
  }
19
31
  }
32
+ const managedProbe = options.managedRuntimeProbe ?? probeManagedRuntime;
33
+ if (managedProbe(environ).verdict === "managed") {
34
+ return {
35
+ mode: RUNTIME_MODE_CLOUD_HEADLESS,
36
+ reason: RUNTIME_REASON_MANAGED_RUNTIME_PROBE,
37
+ };
38
+ }
20
39
  if (isTruthy(environ.CURSOR_SANDBOX) || isTruthy(environ.CURSOR_SANDBOX_LANDLOCK_STATUS)) {
21
- return RUNTIME_MODE_CURSOR_NATIVE_SANDBOX;
40
+ return {
41
+ mode: RUNTIME_MODE_CURSOR_NATIVE_SANDBOX,
42
+ reason: RUNTIME_REASON_CURSOR_SANDBOX_MARKER,
43
+ };
22
44
  }
23
45
  if (isTruthy(environ.CURSOR_AGENT) || isTruthy(environ.CURSOR_COMPOSER)) {
24
- return RUNTIME_MODE_CLOUD_HEADLESS;
46
+ if (hasExplicitHostGhSelection(environ)) {
47
+ return { mode: RUNTIME_MODE_LOCAL_UNSANDBOXED, reason: RUNTIME_REASON_EXPLICIT_HOST_GH };
48
+ }
49
+ return {
50
+ mode: RUNTIME_MODE_CLOUD_HEADLESS,
51
+ reason: RUNTIME_REASON_CURSOR_MARKER_AMBIGUOUS,
52
+ };
25
53
  }
26
- return RUNTIME_MODE_LOCAL_UNSANDBOXED;
54
+ return { mode: RUNTIME_MODE_LOCAL_UNSANDBOXED, reason: RUNTIME_REASON_NO_RUNTIME_MARKER };
27
55
  }
28
- export function probeRuntimeCapabilities(environ = process.env) {
29
- return { runtimeMode: classifyRuntimeMode(environ) };
56
+ export function probeRuntimeCapabilities(environ = process.env, options = {}) {
57
+ const { mode, reason } = classifyRuntimeMode(environ, options);
58
+ return { runtimeMode: mode, runtimeModeReason: reason };
30
59
  }
31
- export function getPlatformCapabilities() {
32
- return probeRuntimeCapabilities();
60
+ export function getPlatformCapabilities(options = {}) {
61
+ return probeRuntimeCapabilities(process.env, options);
33
62
  }
34
63
  //# sourceMappingURL=platform-capabilities.js.map
@@ -3,12 +3,12 @@ import { mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSy
3
3
  import { join, resolve } from "node:path";
4
4
  import { assertProjectionContained, assertWriteTargetSafe, ProjectionContainmentError, } from "../fs/projection-containment.js";
5
5
  import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
6
+ import { missingEnvelopeMessage, stampExistingEnvelopes } from "../lifecycle/brief-envelope.js";
6
7
  import { call } from "../scm/call.js";
7
8
  import { updateDecomposedChildBackReferences } from "../scope/decomposed-refs.js";
8
9
  import { resolveProjectRoot } from "../scope/project-context.js";
9
10
  import { resolveProjectRepo } from "../slice/project-context.js";
10
11
  import { FOLDER_ALLOWED_STATUSES } from "../vbrief-validate/constants.js";
11
- import { LEGACY_INFO_ROOT_KEY, MIGRATED_INFO_ROOT_KEY } from "../xbrief-migrate/constants.js";
12
12
  export const LIFECYCLE_FOLDERS = [
13
13
  "proposed",
14
14
  "pending",
@@ -713,14 +713,13 @@ export function applyLifecycleFixes(vbriefDir, report, projectRoot = null) {
713
713
  plan.status = terminalStatus;
714
714
  const stamp = utcNowIso();
715
715
  // Stamp `updated` into whichever info envelope the file already uses (#2346).
716
- // Canonical v0.8 briefs use `xBRIEFInfo`; stamping `vBRIEFInfo` unconditionally
717
- // appended a stray, version-less `vBRIEFInfo` block that then failed
718
- // `vbrief:validate` ('vBRIEFInfo.version' must be one of ..., got 'undefined').
719
- // Legacy briefs (or files without either envelope) keep the `vBRIEFInfo` key.
720
- const infoKey = MIGRATED_INFO_ROOT_KEY in data ? MIGRATED_INFO_ROOT_KEY : LEGACY_INFO_ROOT_KEY;
721
- const info = (data[infoKey] ?? {});
722
- data[infoKey] = info;
723
- info.updated = stamp;
716
+ // A file carrying neither envelope is already invalid; creating one here hid
717
+ // that cause behind a manufactured version-less `vBRIEFInfo` (#3933), so the
718
+ // sweep now refuses it by name and leaves the artifact in place.
719
+ if (stampExistingEnvelopes(data, stamp).length === 0) {
720
+ failures.push(missingEnvelopeMessage(relPath));
721
+ continue;
722
+ }
724
723
  plan.updated = stamp;
725
724
  propagateItemStatus(plan.items, terminalStatus, stamp);
726
725
  try {
@@ -780,12 +779,13 @@ export function repairCompletedStatusDrift(vbriefDir, drift, projectRoot = null)
780
779
  continue;
781
780
  }
782
781
  const stamp = utcNowIso();
782
+ // Same envelope policy as the closed-issue sweep above (#3933).
783
+ if (stampExistingEnvelopes(data, stamp).length === 0) {
784
+ failures.push(missingEnvelopeMessage(entry.rel_path));
785
+ continue;
786
+ }
783
787
  plan.status = "completed";
784
788
  plan.updated = stamp;
785
- const infoKey = MIGRATED_INFO_ROOT_KEY in data ? MIGRATED_INFO_ROOT_KEY : LEGACY_INFO_ROOT_KEY;
786
- const info = (data[infoKey] ?? {});
787
- data[infoKey] = info;
788
- info.updated = stamp;
789
789
  propagateItemStatus(plan.items, "completed", stamp);
790
790
  try {
791
791
  writeFileSync(src, `${JSON.stringify(data, null, 2)}\n`, "utf8");
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shared document-envelope policy for lifecycle mutators (#3933).
3
+ *
4
+ * A brief carries one document envelope: `xBRIEFInfo` on canonical v0.8
5
+ * artifacts, `vBRIEFInfo` on legacy v0.6 ones. A mutator stamps whichever
6
+ * envelope the artifact already has and creates none. Creating one on a v0.8
7
+ * brief appends a version-less `vBRIEFInfo`, and `validateVbriefSchema`
8
+ * resolves `vBRIEFInfo` first, so the manufactured block wins and the brief the
9
+ * mutator just wrote fails validation (#2346 / #2862 / #3933).
10
+ */
11
+ import { LEGACY_INFO_ROOT_KEY, MIGRATED_INFO_ROOT_KEY } from "../xbrief-migrate/constants.js";
12
+ export type BriefEnvelopeKey = typeof MIGRATED_INFO_ROOT_KEY | typeof LEGACY_INFO_ROOT_KEY;
13
+ /** Envelope keys in canonical order: v0.8 first, legacy v0.6 second. */
14
+ export declare const BRIEF_ENVELOPE_KEYS: readonly BriefEnvelopeKey[];
15
+ /** Envelope keys the artifact already carries as JSON objects. */
16
+ export declare function presentEnvelopeKeys(data: Record<string, unknown>): BriefEnvelopeKey[];
17
+ /**
18
+ * Stamp `updated` on every envelope the artifact already carries; create none.
19
+ * Returns the stamped keys so a caller can refuse an envelope-less artifact by
20
+ * name instead of manufacturing one.
21
+ */
22
+ export declare function stampExistingEnvelopes(data: Record<string, unknown>, nowIso: string): BriefEnvelopeKey[];
23
+ /** Refusal naming both accepted envelopes, for a caller-supplied subject (#3933). */
24
+ export declare function missingEnvelopeMessage(subject: string): string;
25
+ //# sourceMappingURL=brief-envelope.d.ts.map
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Shared document-envelope policy for lifecycle mutators (#3933).
3
+ *
4
+ * A brief carries one document envelope: `xBRIEFInfo` on canonical v0.8
5
+ * artifacts, `vBRIEFInfo` on legacy v0.6 ones. A mutator stamps whichever
6
+ * envelope the artifact already has and creates none. Creating one on a v0.8
7
+ * brief appends a version-less `vBRIEFInfo`, and `validateVbriefSchema`
8
+ * resolves `vBRIEFInfo` first, so the manufactured block wins and the brief the
9
+ * mutator just wrote fails validation (#2346 / #2862 / #3933).
10
+ */
11
+ import { LEGACY_INFO_ROOT_KEY, MIGRATED_INFO_ROOT_KEY } from "../xbrief-migrate/constants.js";
12
+ /** Envelope keys in canonical order: v0.8 first, legacy v0.6 second. */
13
+ export const BRIEF_ENVELOPE_KEYS = [
14
+ MIGRATED_INFO_ROOT_KEY,
15
+ LEGACY_INFO_ROOT_KEY,
16
+ ];
17
+ /** Envelope keys the artifact already carries as JSON objects. */
18
+ export function presentEnvelopeKeys(data) {
19
+ return BRIEF_ENVELOPE_KEYS.filter((key) => {
20
+ const env = data[key];
21
+ return typeof env === "object" && env !== null && !Array.isArray(env);
22
+ });
23
+ }
24
+ /**
25
+ * Stamp `updated` on every envelope the artifact already carries; create none.
26
+ * Returns the stamped keys so a caller can refuse an envelope-less artifact by
27
+ * name instead of manufacturing one.
28
+ */
29
+ export function stampExistingEnvelopes(data, nowIso) {
30
+ const stamped = presentEnvelopeKeys(data);
31
+ for (const key of stamped) {
32
+ data[key].updated = nowIso;
33
+ }
34
+ return stamped;
35
+ }
36
+ /** Refusal naming both accepted envelopes, for a caller-supplied subject (#3933). */
37
+ export function missingEnvelopeMessage(subject) {
38
+ return (`${subject} carries neither \`${MIGRATED_INFO_ROOT_KEY}\` (v0.8) nor ` +
39
+ `\`${LEGACY_INFO_ROOT_KEY}\` (v0.6) -- malformed. Lifecycle mutators refuse ` +
40
+ "rather than create one (#3933).");
41
+ }
42
+ //# sourceMappingURL=brief-envelope.js.map
@@ -1,3 +1,4 @@
1
+ export * from "./brief-envelope.js";
1
2
  export * from "./completed-consistency.js";
2
3
  export * from "./completed-tracked-on-delivery.js";
3
4
  export * from "./completed-write-guard.js";
@@ -1,3 +1,4 @@
1
+ export * from "./brief-envelope.js";
1
2
  export * from "./completed-consistency.js";
2
3
  export * from "./completed-tracked-on-delivery.js";
3
4
  export * from "./completed-write-guard.js";
@@ -189,16 +189,25 @@ export function evaluateLiteralAcceptanceFromPlan(plan, options = {}) {
189
189
  // Rejected stated commands are fail-closed for completion (#3267 Greptile conf residual).
190
190
  // A safety-rejected shell-shaped acceptance line is not "optional diagnostic" — operators
191
191
  // must promote a safe alternative or remove the stated command from the task statement.
192
+ const mergedRejected = [];
193
+ const seenRejected = new Set();
194
+ for (const row of [...(result.rejected ?? []), ...resolved.rejected]) {
195
+ const key = `${row.command}\0${row.reason}`;
196
+ if (seenRejected.has(key))
197
+ continue;
198
+ seenRejected.add(key);
199
+ mergedRejected.push(row);
200
+ }
192
201
  let ok = result.ok;
193
202
  let code = result.code;
194
- let message = appendRejectedNote(result.message, resolved.rejected);
195
- if (resolved.rejected.length > 0) {
203
+ let message = appendRejectedNote(result.message, mergedRejected);
204
+ if (mergedRejected.length > 0) {
196
205
  ok = false;
197
206
  if (code === 0)
198
207
  code = 1;
199
- const ledger = formatRejectedLedger(resolved.rejected);
208
+ const ledger = formatRejectedLedger(mergedRejected);
200
209
  message =
201
- `Literal acceptance-command gate FAILED (#3267): ${resolved.rejected.length} ` +
210
+ `Literal acceptance-command gate FAILED (#3267): ${mergedRejected.length} ` +
202
211
  `safety-rejected stated command(s) block completion until resolved ` +
203
212
  `(promote a safe alternative or remove from the task statement).\n` +
204
213
  ledger +
@@ -212,7 +221,7 @@ export function evaluateLiteralAcceptanceFromPlan(plan, options = {}) {
212
221
  ...result,
213
222
  ok,
214
223
  code,
215
- rejected: resolved.rejected,
224
+ rejected: mergedRejected,
216
225
  advisoryRejected: resolved.advisoryRejected,
217
226
  transcriptPromptSkipped: resolved.transcriptPromptSkipped,
218
227
  message,
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export { attachLiteralAcceptanceCommands, type CaptureLiteralAcceptanceResult, captureAndAttachLiteralAcceptance, captureLiteralAcceptanceCommands, captureLiteralAcceptanceCommandsDetailed, formatRejectedLedger, hasStructuredAcceptanceCommands, INLINE_PROSE_MENTION_REASON, isInlineProseMention, isProseDerivedRejection, readNotAcceptanceCommands, readStoredLiteralAcceptanceCommands, readStoredLiteralAcceptanceDetailed, } from "./capture.js";
8
8
  export { appendLiteralAcceptanceAdvisory, type EvaluateLiteralAcceptanceOptions, evaluateLiteralAcceptanceFromPath, evaluateLiteralAcceptanceFromPlan, formatLiteralAcceptanceAdvisory, isLiteralAcceptanceRequiredAtCeremonyDepth, LITERAL_ACCEPTANCE_ADVISORY_MARKER, type ResolvedLiteralAcceptance, resolveLiteralAcceptanceCommands, resolveLiteralAcceptanceDetailed, stripLiteralAcceptanceAdvisory, } from "./evaluate.js";
9
- export { defaultLiteralAcceptanceRunner, runLiteralAcceptanceCommand, runLiteralAcceptanceCommands, } from "./run.js";
9
+ export { defaultLiteralAcceptanceRunner, isSafetyRefusalRun, runLiteralAcceptanceCommand, runLiteralAcceptanceCommands, } from "./run.js";
10
10
  export { type CommandSafetyResult, evaluateCommandSafety, evaluateNoopDenylist, evaluateStampAcceptanceSafety, isExecutableLiteralSource, isNoopRefusalReason, isVerbatimStatementSpan, NOOP_ACCEPTANCE_REMEDIATION, REJECTED_NOOP_OUTCOME, type RejectedNoopOutcome, type StampAcceptanceCommand, type StampAcceptanceSafetyInput, type StampAcceptanceSafetyResult, type StampSourceRung, } from "./safety.js";
11
11
  export { EXECUTABLE_LITERAL_SOURCES, LITERAL_ACCEPTANCE_METADATA_KEY, LITERAL_ACCEPTANCE_METADATA_KEY_CAMEL, LITERAL_ACCEPTANCE_NOT_COMMANDS_METADATA_KEY, LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY, type LiteralAcceptanceCommand, type LiteralAcceptanceGateResult, type LiteralAcceptanceRunner, type LiteralAcceptanceRunResult, type LiteralAcceptanceSource, type RejectedLiteralCommand, } from "./types.js";
12
12
  //# sourceMappingURL=index.d.ts.map
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export { attachLiteralAcceptanceCommands, captureAndAttachLiteralAcceptance, captureLiteralAcceptanceCommands, captureLiteralAcceptanceCommandsDetailed, formatRejectedLedger, hasStructuredAcceptanceCommands, INLINE_PROSE_MENTION_REASON, isInlineProseMention, isProseDerivedRejection, readNotAcceptanceCommands, readStoredLiteralAcceptanceCommands, readStoredLiteralAcceptanceDetailed, } from "./capture.js";
8
8
  export { appendLiteralAcceptanceAdvisory, evaluateLiteralAcceptanceFromPath, evaluateLiteralAcceptanceFromPlan, formatLiteralAcceptanceAdvisory, isLiteralAcceptanceRequiredAtCeremonyDepth, LITERAL_ACCEPTANCE_ADVISORY_MARKER, resolveLiteralAcceptanceCommands, resolveLiteralAcceptanceDetailed, stripLiteralAcceptanceAdvisory, } from "./evaluate.js";
9
- export { defaultLiteralAcceptanceRunner, runLiteralAcceptanceCommand, runLiteralAcceptanceCommands, } from "./run.js";
9
+ export { defaultLiteralAcceptanceRunner, isSafetyRefusalRun, runLiteralAcceptanceCommand, runLiteralAcceptanceCommands, } from "./run.js";
10
10
  export { evaluateCommandSafety, evaluateNoopDenylist, evaluateStampAcceptanceSafety, isExecutableLiteralSource, isNoopRefusalReason, isVerbatimStatementSpan, NOOP_ACCEPTANCE_REMEDIATION, REJECTED_NOOP_OUTCOME, } from "./safety.js";
11
11
  export { EXECUTABLE_LITERAL_SOURCES, LITERAL_ACCEPTANCE_METADATA_KEY, LITERAL_ACCEPTANCE_METADATA_KEY_CAMEL, LITERAL_ACCEPTANCE_NOT_COMMANDS_METADATA_KEY, LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY, } from "./types.js";
12
12
  //# sourceMappingURL=index.js.map
@@ -6,6 +6,8 @@
6
6
  * Self-chosen verification is supplementary — this gate only runs stored literals.
7
7
  */
8
8
  import type { LiteralAcceptanceCommand, LiteralAcceptanceGateResult, LiteralAcceptanceRunner, LiteralAcceptanceRunResult } from "./types.js";
9
+ /** True when the row is a pre-execution safety refusal, not a product measurement (#3615). */
10
+ export declare function isSafetyRefusalRun(run: Pick<LiteralAcceptanceRunResult, "exitCode" | "detail">): boolean;
9
11
  /** Default shell runner: exact command string via platform shell. */
10
12
  export declare function defaultLiteralAcceptanceRunner(input: {
11
13
  readonly command: string;
@@ -9,6 +9,13 @@ import { spawnSync } from "node:child_process";
9
9
  import { isAbsolute, resolve } from "node:path";
10
10
  import { isInlineProseMention } from "./capture.js";
11
11
  import { evaluateCommandSafety, isExecutableLiteralSource } from "./safety.js";
12
+ /** True when the row is a pre-execution safety refusal, not a product measurement (#3615). */
13
+ export function isSafetyRefusalRun(run) {
14
+ return run.exitCode === 2 && /^refused:\s*/i.test(run.detail);
15
+ }
16
+ function safetyRefusalReason(run) {
17
+ return run.detail.replace(/^refused:\s*/i, "").trim();
18
+ }
12
19
  /** Default shell runner: exact command string via platform shell. */
13
20
  export function defaultLiteralAcceptanceRunner(input) {
14
21
  // Windows: cmd.exe /c; POSIX: shell true uses sh -c. Exact string preserved.
@@ -173,6 +180,7 @@ export function runLiteralAcceptanceCommands(commands, options) {
173
180
  };
174
181
  }
175
182
  const runs = [];
183
+ const rejected = [];
176
184
  for (const cmd of executable) {
177
185
  if (typeof cmd.command !== "string" || cmd.command.trim().length === 0) {
178
186
  return {
@@ -181,9 +189,18 @@ export function runLiteralAcceptanceCommands(commands, options) {
181
189
  message: "Literal acceptance-command gate config error: empty command entry (#3267)",
182
190
  commands,
183
191
  runs,
192
+ rejected: rejected.length > 0 ? rejected : undefined,
184
193
  };
185
194
  }
186
- runs.push(runLiteralAcceptanceCommand(cmd, options));
195
+ const run = runLiteralAcceptanceCommand(cmd, options);
196
+ runs.push(run);
197
+ if (isSafetyRefusalRun(run)) {
198
+ rejected.push({
199
+ command: run.command,
200
+ reason: safetyRefusalReason(run),
201
+ sourceSpan: cmd.sourceSpan ?? null,
202
+ });
203
+ }
187
204
  }
188
205
  const failed = runs.filter((r) => !r.ok);
189
206
  if (failed.length === 0) {
@@ -213,6 +230,7 @@ export function runLiteralAcceptanceCommands(commands, options) {
213
230
  lines.join("\n"),
214
231
  commands,
215
232
  runs,
233
+ rejected: rejected.length > 0 ? rejected : undefined,
216
234
  };
217
235
  }
218
236
  function truncate(s, max) {
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Candidate diff ownership for the merge-chokepoint orphan run (#3893).
3
+ *
4
+ * The unscoped sweep enumerates every running brief under `active/`. Composed
5
+ * on `check:merge` that gives one stranded brief merge authority over every
6
+ * open PR, and N stranded briefs make N single-brief lifecycle PRs mutually
7
+ * unmergeable — each leaves the other orphan. The pre-merge run cannot see the
8
+ * residue its own candidate is about to create either: that candidate's linked
9
+ * PR still reads `merged_at: null` while the gate runs.
10
+ *
11
+ * Scoping the merge run to the candidate's own diff removes the coupling
12
+ * without touching the detector. Repo-wide truth stays enforced at the
13
+ * delivery tip (HEAD on the delivery line falls back to the full sweep) and
14
+ * after merge (`--issue N`, #3429).
15
+ */
16
+ import { type GitRunner } from "../session/git.js";
17
+ export type CandidateScope = {
18
+ readonly kind: "diff";
19
+ /** Ref the candidate was diffed against (e.g. `origin/master`). */
20
+ readonly baseRef: string;
21
+ /** Absolute, platform-normalized brief paths this candidate owns. */
22
+ readonly paths: ReadonlySet<string>;
23
+ } | {
24
+ readonly kind: "sweep";
25
+ /** Why this run stayed repo-wide; always printed with the verdict. */
26
+ readonly reason: string;
27
+ };
28
+ export interface CandidateScopeOptions {
29
+ /** Explicit base ref; defaults to `origin/<deliveryBranch>` then the branch. */
30
+ readonly baseRef?: string | null;
31
+ readonly runGit?: GitRunner;
32
+ }
33
+ /**
34
+ * One comparable form for a git path and a readdir path.
35
+ *
36
+ * Only the directory is canonicalized: the file itself may be a deletion in
37
+ * the candidate diff and no longer exist on disk.
38
+ */
39
+ export declare function normalizeScopePath(path: string): string;
40
+ /**
41
+ * Base ref for the candidate diff: explicit override, else `origin/<branch>`,
42
+ * else the local delivery branch. Null when none resolves.
43
+ */
44
+ export declare function resolveCandidateBaseRef(projectRoot: string, override: string | null | undefined, runGit: GitRunner): string | null;
45
+ /**
46
+ * Brief paths under `activeDir` that the candidate's own diff touches.
47
+ *
48
+ * Falls back to the repo-wide sweep — never to a narrower scan — when git,
49
+ * the base ref, or the diff is unavailable, so the flag cannot silently
50
+ * shrink what a merge gate looks at.
51
+ */
52
+ export declare function resolveCandidateScope(projectRoot: string, activeDir: string, options?: CandidateScopeOptions): CandidateScope;
53
+ //# sourceMappingURL=candidate-scope.d.ts.map