@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
@@ -1,4 +1,4 @@
1
- import { DEFAULT_STALE_MINUTES, REVIEW_OWNER_MARKER_END, REVIEW_OWNER_MARKER_START, } from "./constants.js";
1
+ import { DEFAULT_STALE_MINUTES, PASS_MARKER_KIND, REVIEW_OWNER_MARKER_END, REVIEW_OWNER_MARKER_START, } from "./constants.js";
2
2
  export function parseIso8601Utc(value) {
3
3
  if (typeof value !== "string" || value.length === 0) {
4
4
  return null;
@@ -17,7 +17,7 @@ export function parseIso8601Utc(value) {
17
17
  }
18
18
  return parsed;
19
19
  }
20
- const FIELD_RE = /^(owner|monitor_agent_id|head_sha|started_at|expires_at|platform_primitive|ended_at):\s*(.*)$/;
20
+ const FIELD_RE = /^(kind|owner|monitor_agent_id|agent_id|pass_kind|ceiling|head_sha|started_at|expires_at|platform_primitive|ended_at):\s*(.*)$/;
21
21
  /** True when the comment body contains the review-owner marker block. */
22
22
  export function hasReviewOwnerMarker(body) {
23
23
  return body.includes(REVIEW_OWNER_MARKER_START);
@@ -42,8 +42,17 @@ function parseFieldBlock(body) {
42
42
  }
43
43
  return fields;
44
44
  }
45
+ /** Marker `kind:` value, or "" when the block declares none (ownership-lease shape). */
46
+ function markerKind(body) {
47
+ return parseFieldBlock(body).kind ?? "";
48
+ }
45
49
  export function parseReviewOwnerLease(body) {
46
50
  const fields = parseFieldBlock(body);
51
+ // An advisory pass mark shares the marker but must never satisfy the
52
+ // review-monitor ownership gate (#3607).
53
+ if ((fields.kind ?? "") === PASS_MARKER_KIND) {
54
+ return null;
55
+ }
47
56
  const owner = fields.owner ?? "";
48
57
  const monitorAgentId = fields.monitor_agent_id ?? "";
49
58
  const startedAt = fields.started_at ?? "";
@@ -128,6 +137,11 @@ export function mapCommentEntry(entry) {
128
137
  if (!hasReviewOwnerMarker(rec.body)) {
129
138
  return null;
130
139
  }
140
+ // Pass marks are a disjoint population on the same marker; excluding them here
141
+ // keeps `registerReviewMonitor` from overwriting one as its lease anchor (#3607).
142
+ if (markerKind(rec.body) === PASS_MARKER_KIND) {
143
+ return null;
144
+ }
131
145
  return {
132
146
  id: rec.id,
133
147
  body: rec.body,
@@ -149,4 +163,91 @@ export function findActiveLeaseComment(comments, options = {}) {
149
163
  }
150
164
  return selectWinningReviewOwnerComment(active);
151
165
  }
166
+ export function parsePassOpenMarker(body) {
167
+ if (!hasReviewOwnerMarker(body)) {
168
+ return null;
169
+ }
170
+ const fields = parseFieldBlock(body);
171
+ if ((fields.kind ?? "") !== PASS_MARKER_KIND) {
172
+ return null;
173
+ }
174
+ const owner = fields.owner ?? "";
175
+ const passKind = fields.pass_kind ?? "";
176
+ const startedAt = fields.started_at ?? "";
177
+ const expiresAt = fields.expires_at ?? "";
178
+ if (owner.length === 0 ||
179
+ passKind.length === 0 ||
180
+ startedAt.length === 0 ||
181
+ expiresAt.length === 0) {
182
+ return null;
183
+ }
184
+ const agentId = fields.agent_id ?? "";
185
+ const ceiling = fields.ceiling ?? "";
186
+ const endedAt = fields.ended_at ?? "";
187
+ return {
188
+ kind: PASS_MARKER_KIND,
189
+ pass_kind: passKind,
190
+ owner,
191
+ agent_id: agentId.length > 0 ? agentId : null,
192
+ ceiling: ceiling.length > 0 ? ceiling : null,
193
+ started_at: startedAt,
194
+ expires_at: expiresAt,
195
+ ended_at: endedAt.length > 0 ? endedAt : null,
196
+ };
197
+ }
198
+ export function renderPassOpenComment(marker) {
199
+ const optional = (name, value) => value !== null && value.length > 0 ? `${name}: ${value}\n` : "";
200
+ return (`${REVIEW_OWNER_MARKER_START}\n` +
201
+ `kind: ${PASS_MARKER_KIND}\n` +
202
+ `pass_kind: ${marker.pass_kind}\n` +
203
+ `owner: ${marker.owner}\n` +
204
+ optional("agent_id", marker.agent_id) +
205
+ optional("ceiling", marker.ceiling) +
206
+ `started_at: ${marker.started_at}\n` +
207
+ `expires_at: ${marker.expires_at}\n` +
208
+ optional("ended_at", marker.ended_at) +
209
+ `${REVIEW_OWNER_MARKER_END}`);
210
+ }
211
+ /** Expired or synthesis-cleared marks are ignored on read; a mark self-clears (#3607). */
212
+ export function isPassMarkerActive(marker, now = new Date()) {
213
+ if (marker.ended_at !== null) {
214
+ return false;
215
+ }
216
+ const expires = parseIso8601Utc(marker.expires_at);
217
+ if (expires === null) {
218
+ return false;
219
+ }
220
+ return now.getTime() <= expires.getTime();
221
+ }
222
+ export function mapPassCommentEntry(entry) {
223
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
224
+ return null;
225
+ }
226
+ const rec = entry;
227
+ if (typeof rec.id !== "number" || typeof rec.body !== "string") {
228
+ return null;
229
+ }
230
+ const marker = parsePassOpenMarker(rec.body);
231
+ if (marker === null) {
232
+ return null;
233
+ }
234
+ return {
235
+ id: rec.id,
236
+ body: rec.body,
237
+ createdAt: typeof rec.created_at === "string" ? rec.created_at : "",
238
+ authorLogin: typeof rec.author_login === "string" ? rec.author_login : "",
239
+ marker,
240
+ };
241
+ }
242
+ /** Oldest comment id wins when two marks race, matching the lease (#2814 / #3607). */
243
+ export function selectWinningPassComment(comments) {
244
+ if (comments.length === 0) {
245
+ return null;
246
+ }
247
+ return [...comments].sort((a, b) => a.id - b.id)[0] ?? null;
248
+ }
249
+ export function findActivePassComment(comments, options = {}) {
250
+ const active = comments.filter((comment) => comment.marker !== null && isPassMarkerActive(comment.marker, options.now));
251
+ return selectWinningPassComment(active);
252
+ }
152
253
  //# sourceMappingURL=lease-comment.js.map
@@ -126,8 +126,8 @@ export interface AcceptanceRunSummaryPayload {
126
126
  readonly none_stated?: boolean;
127
127
  readonly clause_count?: number;
128
128
  readonly clause_outcomes?: readonly AcceptanceClauseOutcomeRow[];
129
- /** bank | cache | executed — how verify:ac obtained this result (#3387). */
130
- readonly served_from?: "bank" | "cache" | "executed";
129
+ /** bank | cache | executed | refused — how verify:ac obtained this result (#3387 / #3615). */
130
+ readonly served_from?: "bank" | "cache" | "executed" | "refused";
131
131
  /** Config-error cause when outcome is config-error (#3559). */
132
132
  readonly cause?: string;
133
133
  /** Reuse-gate miss cause when served_from is executed (#3558). */
@@ -1,7 +1,7 @@
1
- import { resolveBinary } from "./binary.js";
1
+ import { resolveBinaryForArgv } from "./call-shape.js";
2
2
  export interface BuildCommandOptions {
3
3
  readonly binary?: string;
4
- readonly whichFn?: Parameters<typeof resolveBinary>[0];
4
+ readonly whichFn?: Parameters<typeof resolveBinaryForArgv>[2];
5
5
  }
6
6
  /**
7
7
  * Construct the underlying `[binary, namespace, verb, *extra]` argv.
@@ -1,4 +1,4 @@
1
- import { resolveBinary } from "./binary.js";
1
+ import { resolveBinaryForArgv } from "./call-shape.js";
2
2
  import { ALLOWED_ISSUE_VERBS, ALLOWED_NAMESPACES } from "./constants.js";
3
3
  import { ScmStubError } from "./errors.js";
4
4
  import { pyRepr, pyTuple } from "./py-format.js";
@@ -17,7 +17,7 @@ export function buildCommand(namespace, verb, extra, options = {}) {
17
17
  `${pyTuple(ALLOWED_ISSUE_VERBS)}. The v1 stub only exposes these four; ` +
18
18
  "additional scm:issue:* commands belong on #881.");
19
19
  }
20
- const resolved = options.binary ?? resolveBinary(options.whichFn ?? undefined);
20
+ const resolved = options.binary ?? resolveBinaryForArgv(namespace, [verb, ...extra], options.whichFn);
21
21
  return [resolved, namespace, verb, ...extra];
22
22
  }
23
23
  //# sourceMappingURL=build-command.js.map
@@ -0,0 +1,25 @@
1
+ import { type WhichFn } from "./binary.js";
2
+ /** Surfaces ghx may serve versus surfaces that must use live gh (#3737). */
3
+ export type ScmBinaryRole = "cached-get" | "live-gh";
4
+ /**
5
+ * Classify an SCM argv for ghx eligibility.
6
+ *
7
+ * ghx is a cached read-only GET proxy that accepts a single positional path.
8
+ * Extra positionals are rejected (`accepts 1 arg(s), received 2`). Flags
9
+ * (`--method`, `--paginate`, `--jq`, `--input`, `-X`, ...) are not the cache
10
+ * shape. Non-`api` verbs are live `gh`.
11
+ */
12
+ export declare function classifyScmArgv(verb: string, args?: readonly string[]): ScmBinaryRole;
13
+ /**
14
+ * Resolve a binary for a classified role.
15
+ *
16
+ * cached-get uses the PATH ladder (`resolveBinary`, ghx then gh).
17
+ * live-gh pins `gh` — the same preference as `resolveLiveGh`,
18
+ * `resolveAuthProbeBinary`, and `GH_ONLY_WHICH`. If `gh` is absent, throw
19
+ * rather than fall through to ghx (a mutation or flag-rich GET is not a
20
+ * cache-proxy shape).
21
+ */
22
+ export declare function resolveBinaryForRole(role: ScmBinaryRole, whichFn?: WhichFn): string;
23
+ /** Classify argv then resolve. Compose with `resolveBinary`; do not probe health. */
24
+ export declare function resolveBinaryForArgv(verb: string, args?: readonly string[], whichFn?: WhichFn): string;
25
+ //# sourceMappingURL=call-shape.d.ts.map
@@ -0,0 +1,59 @@
1
+ import { defaultWhich, resolveBinary } from "./binary.js";
2
+ import { ScmStubError } from "./errors.js";
3
+ /**
4
+ * Classify an SCM argv for ghx eligibility.
5
+ *
6
+ * ghx is a cached read-only GET proxy that accepts a single positional path.
7
+ * Extra positionals are rejected (`accepts 1 arg(s), received 2`). Flags
8
+ * (`--method`, `--paginate`, `--jq`, `--input`, `-X`, ...) are not the cache
9
+ * shape. Non-`api` verbs are live `gh`.
10
+ */
11
+ export function classifyScmArgv(verb, args = []) {
12
+ if (verb !== "api") {
13
+ return "live-gh";
14
+ }
15
+ const positionals = [];
16
+ for (let i = 0; i < args.length; i += 1) {
17
+ const token = args[i];
18
+ if (token === undefined) {
19
+ continue;
20
+ }
21
+ if (token === "--") {
22
+ positionals.push(...args.slice(i + 1));
23
+ break;
24
+ }
25
+ if (token.startsWith("-")) {
26
+ return "live-gh";
27
+ }
28
+ positionals.push(token);
29
+ }
30
+ if (positionals.length !== 1) {
31
+ return "live-gh";
32
+ }
33
+ return "cached-get";
34
+ }
35
+ /**
36
+ * Resolve a binary for a classified role.
37
+ *
38
+ * cached-get uses the PATH ladder (`resolveBinary`, ghx then gh).
39
+ * live-gh pins `gh` — the same preference as `resolveLiveGh`,
40
+ * `resolveAuthProbeBinary`, and `GH_ONLY_WHICH`. If `gh` is absent, throw
41
+ * rather than fall through to ghx (a mutation or flag-rich GET is not a
42
+ * cache-proxy shape).
43
+ */
44
+ export function resolveBinaryForRole(role, whichFn = defaultWhich) {
45
+ if (role === "live-gh") {
46
+ if (whichFn("gh") !== null) {
47
+ return "gh";
48
+ }
49
+ throw new ScmStubError("gh not found on PATH; this call shape requires live gh, not the ghx cache proxy. " +
50
+ "install GitHub CLI (https://cli.github.com/) or pass GH_TOKEN into a matched env and re-run. " +
51
+ "Refs #3737 / #2275.");
52
+ }
53
+ return resolveBinary(whichFn);
54
+ }
55
+ /** Classify argv then resolve. Compose with `resolveBinary`; do not probe health. */
56
+ export function resolveBinaryForArgv(verb, args = [], whichFn = defaultWhich) {
57
+ return resolveBinaryForRole(classifyScmArgv(verb, args), whichFn);
58
+ }
59
+ //# sourceMappingURL=call-shape.js.map
@@ -1,4 +1,4 @@
1
- import { resolveBinary } from "./binary.js";
1
+ import { type WhichFn } from "./binary.js";
2
2
  /** Mirrors Python `subprocess.CompletedProcess`. */
3
3
  export interface CompletedProcess {
4
4
  readonly args: readonly string[];
@@ -13,7 +13,7 @@ export interface CallOptions {
13
13
  readonly timeout?: number;
14
14
  readonly cwd?: string;
15
15
  readonly binary?: string;
16
- readonly whichFn?: Parameters<typeof resolveBinary>[0];
16
+ readonly whichFn?: WhichFn;
17
17
  readonly env?: NodeJS.ProcessEnv;
18
18
  readonly input?: string;
19
19
  }
@@ -21,10 +21,13 @@ export interface CallOptions {
21
21
  * Source-aware SCM invocation -- partial down-payment on #445 / #935 Workstream 6.
22
22
  * Mirrors `scripts/scm.py::call`.
23
23
  *
24
- * Binary absence still throws ScmStubError via resolveBinary with the #2275
25
- * diagnostic. Full auth readiness is enforced at SCM CLI entry points
24
+ * Binary absence still throws ScmStubError via resolveBinaryForRole with the
25
+ * #2275 diagnostic. Full auth readiness is enforced at SCM CLI entry points
26
26
  * (`scm/main`, issue-ingest, reconcile-issues) via requireScmReady — not on
27
27
  * every call() — so unit tests that inject binary/seams stay hermetic.
28
+ *
29
+ * Call-shape selection (#3737): ghx only for single-path GET. An explicit
30
+ * `binary` override skips selection and spawn-failure fallback.
28
31
  */
29
32
  export declare function call(source: string, verb: string, args?: readonly string[] | null, options?: CallOptions): CompletedProcess;
30
33
  //# sourceMappingURL=call.d.ts.map
package/dist/scm/call.js CHANGED
@@ -1,43 +1,82 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { resolveBinary } from "./binary.js";
2
+ import { resolveCaptureFailureStderr, SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
3
+ import { defaultWhich } from "./binary.js";
4
+ import { classifyScmArgv, resolveBinaryForRole } from "./call-shape.js";
3
5
  import { SUPPORTED_CALL_SOURCES } from "./constants.js";
4
6
  import { pyRepr } from "./py-format.js";
7
+ import { formatScmSpawnDiagnostic, isAvailabilitySpawnFailure } from "./spawn-status.js";
5
8
  /**
6
9
  * Source-aware SCM invocation -- partial down-payment on #445 / #935 Workstream 6.
7
10
  * Mirrors `scripts/scm.py::call`.
8
11
  *
9
- * Binary absence still throws ScmStubError via resolveBinary with the #2275
10
- * diagnostic. Full auth readiness is enforced at SCM CLI entry points
12
+ * Binary absence still throws ScmStubError via resolveBinaryForRole with the
13
+ * #2275 diagnostic. Full auth readiness is enforced at SCM CLI entry points
11
14
  * (`scm/main`, issue-ingest, reconcile-issues) via requireScmReady — not on
12
15
  * every call() — so unit tests that inject binary/seams stay hermetic.
16
+ *
17
+ * Call-shape selection (#3737): ghx only for single-path GET. An explicit
18
+ * `binary` override skips selection and spawn-failure fallback.
13
19
  */
14
20
  export function call(source, verb, args = null, options = {}) {
15
21
  if (!SUPPORTED_CALL_SOURCES.includes(source)) {
16
22
  throw new Error(`source=${pyRepr(source)} not yet supported; ` +
17
23
  "see #445 / #935 Workstream 6 for the abstraction.");
18
24
  }
19
- const resolved = options.binary ?? resolveBinary(options.whichFn);
20
- const argv = [resolved, verb, ...(args ?? [])];
25
+ const whichFn = options.whichFn ?? defaultWhich;
26
+ const role = classifyScmArgv(verb, args ?? []);
27
+ const explicitBinary = options.binary;
28
+ let resolved = explicitBinary ?? resolveBinaryForRole(role, whichFn);
29
+ const extra = args ?? [];
21
30
  const captureOutput = options.captureOutput ?? true;
22
31
  const timeoutMs = options.timeout !== undefined ? Math.round(options.timeout * 1000) : undefined;
23
- const result = spawnSync(resolved, [verb, ...(args ?? [])], {
32
+ const spawnOnce = (bin) => spawnSync(bin, [verb, ...extra], {
24
33
  cwd: options.cwd,
25
34
  env: options.env ?? process.env,
26
35
  input: options.input,
27
36
  encoding: (options.text ?? true) ? "utf8" : undefined,
28
37
  timeout: timeoutMs,
38
+ maxBuffer: SUBPROCESS_MAX_BUFFER,
29
39
  stdio: captureOutput ? ["pipe", "pipe", "pipe"] : "inherit",
30
40
  });
41
+ let result = spawnOnce(resolved);
42
+ if (explicitBinary === undefined &&
43
+ role === "cached-get" &&
44
+ resolved === "ghx" &&
45
+ isAvailabilitySpawnFailure({
46
+ status: result.status,
47
+ error: result.error,
48
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
49
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
50
+ }) &&
51
+ whichFn("gh") !== null) {
52
+ resolved = "gh";
53
+ result = spawnOnce(resolved);
54
+ }
55
+ const argv = [resolved, verb, ...extra];
56
+ const captured = typeof result.stderr === "string" ? result.stderr : "";
57
+ let stderr = resolveCaptureFailureStderr({
58
+ captured,
59
+ status: result.status,
60
+ message: result.error?.message,
61
+ });
62
+ if (isAvailabilitySpawnFailure({
63
+ status: result.status,
64
+ error: result.error,
65
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
66
+ stderr: captured,
67
+ }) &&
68
+ stderr.trim().length === 0) {
69
+ stderr = formatScmSpawnDiagnostic(resolved, result.status, stderr, result.error);
70
+ }
31
71
  if (options.check && result.status !== 0) {
32
- const message = typeof result.stderr === "string" ? result.stderr : "";
33
- const error = new Error(message || `Process exited with code ${result.status}`);
72
+ const error = new Error(stderr || `Process exited with code ${result.status}`);
34
73
  throw error;
35
74
  }
36
75
  return {
37
76
  args: argv,
38
77
  returncode: result.status ?? 1,
39
78
  stdout: typeof result.stdout === "string" ? result.stdout : "",
40
- stderr: typeof result.stderr === "string" ? result.stderr : "",
79
+ stderr,
41
80
  };
42
81
  }
43
82
  //# sourceMappingURL=call.js.map
@@ -7,6 +7,7 @@
7
7
  import { type DesignCritiqueCatalogChip } from "../design-critique/exclusive-chip.js";
8
8
  import type { LabelClient } from "../vbrief-reconcile/types.js";
9
9
  export declare const DESIGN_CRITIQUE_CHIP_VERB: "design-critique-chip";
10
+ export declare const CHIP_APPLY_MISS_TOKEN = "chip apply missed (non-blocking convenience)";
10
11
  export declare const DESIGN_CRITIQUE_CHIP_USAGE: string;
11
12
  export interface DesignCritiqueChipArgs {
12
13
  readonly issue: number;
@@ -7,14 +7,16 @@
7
7
  import { spawnSync } from "node:child_process";
8
8
  import { applyDesignCritiqueCatalogChip, } from "../design-critique/exclusive-chip.js";
9
9
  import { parseGithubOwnerRepo } from "../policy/sync-default.js";
10
- import { ScmLabelClient, ScmLabelError } from "../vbrief-reconcile/labels.js";
10
+ import { ScmLabelClient } from "../vbrief-reconcile/labels.js";
11
11
  import { extractFlag, extractValueFlag } from "./argv.js";
12
12
  import { InvalidRepoError, splitRepo } from "./gh-rest.js";
13
13
  import { pyRepr } from "./py-format.js";
14
14
  export const DESIGN_CRITIQUE_CHIP_VERB = "design-critique-chip";
15
+ export const CHIP_APPLY_MISS_TOKEN = "chip apply missed (non-blocking convenience)";
15
16
  export const DESIGN_CRITIQUE_CHIP_USAGE = "usage: scm issue design-critique-chip --issue N --chip triage-ready|mechanism-shaped [--repo OWNER/NAME] [--json]\n" +
16
17
  " Parent attach of design-critique:triage-ready / recut mechanism-shaped.\n" +
17
- " Closed catalog remaining-set replace. One write. Other facets stay.\n";
18
+ " Closed catalog remaining-set replace. One write. Other facets stay.\n" +
19
+ " Apply miss is non-blocking convenience; ingest is not blocked.\n";
18
20
  const CHIP_ALIASES = {
19
21
  "triage-ready": "design-critique:triage-ready",
20
22
  "mechanism-shaped": "design-critique:mechanism-shaped",
@@ -161,11 +163,25 @@ export function runDesignCritiqueChip(extra, seams = {}) {
161
163
  };
162
164
  }
163
165
  catch (err) {
164
- if (err instanceof ScmLabelError) {
165
- return { exitCode: 1, stdout: "", stderr: `error: ${err.message}\n` };
166
- }
167
166
  const message = err instanceof Error ? err.message : String(err);
168
- return { exitCode: 1, stdout: "", stderr: `error: ${message}\n` };
167
+ const payload = {
168
+ repo,
169
+ issue: args.issue,
170
+ chip: args.chip,
171
+ applied: false,
172
+ miss: true,
173
+ blocking: false,
174
+ error: message,
175
+ };
176
+ if (args.json) {
177
+ return { exitCode: 0, stdout: `${JSON.stringify(payload)}\n`, stderr: "" };
178
+ }
179
+ return {
180
+ exitCode: 0,
181
+ stdout: "",
182
+ stderr: `${CHIP_APPLY_MISS_TOKEN}: ${message}\n` +
183
+ "ingest is not blocked; remaining-set hygiene is optional for a write-capable identity\n",
184
+ };
169
185
  }
170
186
  }
171
187
  //# sourceMappingURL=design-critique-chip.js.map
@@ -1,4 +1,5 @@
1
- import { resolveBinary } from "./binary.js";
1
+ import { type SpawnSyncOptions } from "node:child_process";
2
+ import { type WhichFn } from "./binary.js";
2
3
  export declare const DEFAULT_TIMEOUT_S = 60;
3
4
  /** Raised when the `"owner/repo"` argument is malformed. */
4
5
  export declare class InvalidRepoError extends Error {
@@ -11,35 +12,47 @@ export declare class GhRestError extends Error {
11
12
  readonly endpoint: string;
12
13
  readonly payload: Record<string, unknown> | null;
13
14
  readonly hint: string;
15
+ readonly binary: string;
14
16
  constructor(options: {
15
17
  stderr: string;
16
18
  exitCode: number;
17
19
  endpoint: string;
18
20
  payload: Record<string, unknown> | null;
19
21
  hint?: string;
22
+ binary?: string;
20
23
  });
21
24
  }
22
- export type RunGhApiFn = (args: readonly string[], options?: {
23
- timeout?: number;
24
- whichFn?: Parameters<typeof resolveBinary>[0];
25
- }) => {
25
+ export type GhSpawnResult = {
26
+ readonly status: number | null;
27
+ readonly stdout?: string | Buffer | null;
28
+ readonly stderr?: string | Buffer | null;
29
+ readonly error?: {
30
+ readonly message?: string;
31
+ readonly code?: string;
32
+ };
33
+ };
34
+ export type GhSpawnFn = (command: string, args: readonly string[], options: SpawnSyncOptions) => GhSpawnResult;
35
+ export type GhApiProcess = {
26
36
  returncode: number;
27
37
  stdout: string;
28
38
  stderr: string;
39
+ binary?: string;
29
40
  };
41
+ export type RunGhApiFn = (args: readonly string[], options?: {
42
+ timeout?: number;
43
+ whichFn?: WhichFn;
44
+ spawnFn?: GhSpawnFn;
45
+ }) => GhApiProcess;
30
46
  /** Single subprocess seam invoked by every helper. */
31
47
  export declare function runGhApi(args: readonly string[], options?: {
32
48
  timeout?: number;
33
- whichFn?: Parameters<typeof resolveBinary>[0];
34
- }): {
35
- returncode: number;
36
- stdout: string;
37
- stderr: string;
38
- };
49
+ whichFn?: WhichFn;
50
+ spawnFn?: GhSpawnFn;
51
+ }): GhApiProcess;
39
52
  export declare function splitRepo(repo: string): [string, string];
40
53
  export interface GhRestSeams {
41
54
  readonly runGhApiFn?: RunGhApiFn;
42
- readonly whichFn?: Parameters<typeof resolveBinary>[0];
55
+ readonly whichFn?: WhichFn;
43
56
  }
44
57
  /** `GET /repos/{owner}/{repo}/issues/{n}` -- read a single issue. */
45
58
  export declare function restIssueView(repo: string, n: number, seams?: GhRestSeams): Record<string, unknown>;
@@ -53,7 +66,7 @@ export interface RestIssueListOptions {
53
66
  export declare function restIssueList(repo: string, options?: RestIssueListOptions, seams?: GhRestSeams): Record<string, unknown>[];
54
67
  export declare const REST_MAX_PER_PAGE = 100;
55
68
  export declare const REST_PAGINATION_MAX_PAGES = 100;
56
- export declare const PUBLIC_HELPERS: readonly ["restCreateIssue", "restPostComment", "restUpdateComment", "restDeleteComment", "restGetUser", "restUpdateIssue", "restCreateLabel", "restCloseIssue", "restOpenPr", "restMergePr", "restIssueView", "restPrView", "restIssueList", "restIssueListPaginated"];
69
+ export declare const PUBLIC_HELPERS: readonly ["restCreateIssue", "restPostComment", "restUpdateComment", "restDeleteComment", "restGetUser", "restUpdateIssue", "restCreateLabel", "restCloseIssue", "restOpenPr", "restMergePr", "restIssueView", "restPrView", "restIssueList", "restIssueListPaginated", "restIssueListOpenInventory"];
57
70
  export declare function restCreateIssue(repo: string, title: string, body: string, labels?: readonly string[], seams?: GhRestSeams): Record<string, unknown>;
58
71
  export declare function restPostComment(repo: string, n: number, body: string, seams?: GhRestSeams): Record<string, unknown>;
59
72
  /** `PATCH /repos/{owner}/{repo}/issues/comments/{id}` -- edit a PR/issue comment in place. */
@@ -78,5 +91,13 @@ export interface RestIssueListPaginatedOptions extends RestIssueListOptions {
78
91
  readonly limit?: number | null;
79
92
  readonly excludePulls?: boolean;
80
93
  }
94
+ /** Flatten `gh api --paginate --slurp` issue-list output (pages or single page). */
95
+ export declare function flattenOpenInventoryPayload(parsed: unknown[], endpoint: string): Record<string, unknown>[];
96
+ /**
97
+ * Complete open-issue inventory in one `gh api --paginate --slurp` subprocess (#3752).
98
+ * Fail-closed on command failure, non-array JSON, buffer exhaustion, or cap hit.
99
+ * Pull-request rows are excluded.
100
+ */
101
+ export declare function restIssueListOpenInventory(repo: string, seams?: GhRestSeams): Record<string, unknown>[];
81
102
  export declare function restIssueListPaginated(repo: string, options?: RestIssueListPaginatedOptions, seams?: GhRestSeams): Record<string, unknown>[];
82
103
  //# sourceMappingURL=gh-rest.d.ts.map