vouchington-tooling 0.20.0 → 0.22.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 (257) hide show
  1. package/README.md +73 -10
  2. package/dist/agent-blackboard/snapshot-partition-format.mjs +2 -72
  3. package/dist/agent-blackboard/snapshot-partition-guards.d.mts +3 -0
  4. package/dist/agent-blackboard/snapshot-partition-guards.mjs +9 -0
  5. package/dist/agent-blackboard/snapshot-partition-predicates.d.mts +3 -0
  6. package/dist/agent-blackboard/snapshot-partition-predicates.mjs +65 -0
  7. package/dist/api-contract-discovery/header-contract-parser.d.mts +3 -0
  8. package/dist/api-contract-discovery/header-contract-parser.mjs +93 -0
  9. package/dist/api-contract-discovery/header-contract-registry.d.mts +3 -0
  10. package/dist/api-contract-discovery/header-contract-registry.mjs +38 -0
  11. package/dist/api-contract-discovery/header-contract-types.d.mts +19 -0
  12. package/dist/api-contract-discovery/header-contract-types.mjs +1 -0
  13. package/dist/api-contract-discovery/index.d.mts +31 -0
  14. package/dist/api-contract-discovery/index.mjs +26 -0
  15. package/dist/api-contract-discovery/query-contract-extraction.d.mts +3 -0
  16. package/dist/api-contract-discovery/query-contract-extraction.mjs +65 -0
  17. package/dist/api-contract-discovery/query-contract-literals.d.mts +8 -0
  18. package/dist/api-contract-discovery/query-contract-literals.mjs +54 -0
  19. package/dist/api-contract-discovery/query-contract-registry.d.mts +3 -0
  20. package/dist/api-contract-discovery/query-contract-registry.mjs +68 -0
  21. package/dist/api-contract-discovery/query-contract-types.d.mts +8 -0
  22. package/dist/api-contract-discovery/query-contract-types.mjs +1 -0
  23. package/dist/api-contract-discovery/registered-route-catalog.d.mts +10 -0
  24. package/dist/api-contract-discovery/registered-route-catalog.mjs +81 -0
  25. package/dist/api-contract-discovery/registered-route-handler-analysis.d.mts +2 -0
  26. package/dist/api-contract-discovery/registered-route-handler-analysis.mjs +74 -0
  27. package/dist/api-contract-discovery/request-contract-implicit.d.mts +17 -0
  28. package/dist/api-contract-discovery/request-contract-implicit.mjs +73 -0
  29. package/dist/api-contract-discovery/request-contract-lenient.d.mts +28 -0
  30. package/dist/api-contract-discovery/request-contract-lenient.mjs +59 -0
  31. package/dist/api-contract-discovery/request-contract-registry.d.mts +14 -0
  32. package/dist/api-contract-discovery/request-contract-registry.mjs +61 -0
  33. package/dist/api-contract-discovery/request-contract-route-analysis.d.mts +25 -0
  34. package/dist/api-contract-discovery/request-contract-route-analysis.mjs +68 -0
  35. package/dist/api-contract-discovery/request-contract-types.d.mts +12 -0
  36. package/dist/api-contract-discovery/request-contract-types.mjs +1 -0
  37. package/dist/api-contract-discovery/response-contract-ctx-analysis.d.mts +7 -0
  38. package/dist/api-contract-discovery/response-contract-ctx-analysis.mjs +28 -0
  39. package/dist/api-contract-discovery/response-contract-error-branch.d.mts +15 -0
  40. package/dist/api-contract-discovery/response-contract-error-branch.mjs +47 -0
  41. package/dist/api-contract-discovery/response-contract-implicit-sources.test-helpers.d.mts +145 -0
  42. package/dist/api-contract-discovery/response-contract-implicit-sources.test-helpers.mjs +145 -0
  43. package/dist/api-contract-discovery/response-contract-implicit.d.mts +5 -0
  44. package/dist/api-contract-discovery/response-contract-implicit.mjs +110 -0
  45. package/dist/api-contract-discovery/response-contract-lenient.d.mts +23 -0
  46. package/dist/api-contract-discovery/response-contract-lenient.mjs +52 -0
  47. package/dist/api-contract-discovery/response-contract-media.d.mts +3 -0
  48. package/dist/api-contract-discovery/response-contract-media.mjs +46 -0
  49. package/dist/api-contract-discovery/response-contract-registration.d.mts +11 -0
  50. package/dist/api-contract-discovery/response-contract-registration.mjs +61 -0
  51. package/dist/api-contract-discovery/response-contract-registry.d.mts +7 -0
  52. package/dist/api-contract-discovery/response-contract-registry.mjs +76 -0
  53. package/dist/api-contract-discovery/response-contract-route-analysis.d.mts +13 -0
  54. package/dist/api-contract-discovery/response-contract-route-analysis.mjs +144 -0
  55. package/dist/api-contract-discovery/response-contract-status.d.mts +27 -0
  56. package/dist/api-contract-discovery/response-contract-status.mjs +80 -0
  57. package/dist/api-contract-discovery/response-contract-types.d.mts +16 -0
  58. package/dist/api-contract-discovery/response-contract-types.mjs +1 -0
  59. package/dist/api-contract-discovery/test-setup.test-helpers.d.mts +3 -0
  60. package/dist/api-contract-discovery/test-setup.test-helpers.mjs +2 -0
  61. package/dist/ast-grep-examples/companion-parity-compare.d.mts +3 -0
  62. package/dist/ast-grep-examples/companion-parity-compare.mjs +58 -0
  63. package/dist/ast-grep-examples/companion-parity.d.mts +2 -1
  64. package/dist/ast-grep-examples/companion-parity.mjs +2 -58
  65. package/dist/browser-session-runner/attempt.mjs +29 -48
  66. package/dist/browser-session-runner/termination.d.mts +11 -0
  67. package/dist/browser-session-runner/termination.mjs +34 -0
  68. package/dist/cli/parse-direct-options.d.mts +26 -0
  69. package/dist/cli/parse-direct-options.mjs +72 -0
  70. package/dist/cli/parse-forwarded-commands.d.mts +42 -0
  71. package/dist/cli/parse-forwarded-commands.mjs +29 -0
  72. package/dist/cli/parse-options.d.mts +1 -2
  73. package/dist/cli/parse-options.mjs +2 -72
  74. package/dist/cli/parse.mjs +4 -26
  75. package/dist/compiler-build/freshness.d.mts +27 -0
  76. package/dist/compiler-build/freshness.mjs +116 -0
  77. package/dist/compiler-build/index.d.mts +2 -0
  78. package/dist/compiler-build/index.mjs +2 -0
  79. package/dist/compiler-build/settlement.d.mts +10 -0
  80. package/dist/compiler-build/settlement.mjs +14 -0
  81. package/dist/config-inventory/call-arguments.d.mts +1 -0
  82. package/dist/config-inventory/call-arguments.mjs +30 -0
  83. package/dist/config-inventory/collect.d.mts +3 -0
  84. package/dist/config-inventory/collect.mjs +43 -0
  85. package/dist/config-inventory/dynamic-configs.d.mts +2 -0
  86. package/dist/config-inventory/dynamic-configs.mjs +9 -0
  87. package/dist/config-inventory/env-expression-readers.d.mts +3 -0
  88. package/dist/config-inventory/env-expression-readers.mjs +117 -0
  89. package/dist/config-inventory/env-prefix-readers.d.mts +2 -0
  90. package/dist/config-inventory/env-prefix-readers.mjs +14 -0
  91. package/dist/config-inventory/env-reference-matchers.d.mts +3 -0
  92. package/dist/config-inventory/env-reference-matchers.mjs +9 -0
  93. package/dist/config-inventory/env-var-accumulator.d.mts +6 -0
  94. package/dist/config-inventory/env-var-accumulator.mjs +82 -0
  95. package/dist/config-inventory/env-vars.d.mts +4 -0
  96. package/dist/config-inventory/env-vars.mjs +108 -0
  97. package/dist/config-inventory/format.d.mts +2 -0
  98. package/dist/config-inventory/format.mjs +69 -0
  99. package/dist/config-inventory/index.d.mts +3 -0
  100. package/dist/config-inventory/index.mjs +2 -0
  101. package/dist/config-inventory/package-gates.d.mts +2 -0
  102. package/dist/config-inventory/package-gates.mjs +54 -0
  103. package/dist/config-inventory/shared.d.mts +3 -0
  104. package/dist/config-inventory/shared.mjs +7 -0
  105. package/dist/config-inventory/types.d.mts +96 -0
  106. package/dist/config-inventory/types.mjs +1 -0
  107. package/dist/contract-schema/contract-schema-bounded-array.d.mts +15 -0
  108. package/dist/contract-schema/contract-schema-bounded-array.mjs +32 -0
  109. package/dist/contract-schema/contract-schema-type-extractor.d.mts +2 -8
  110. package/dist/contract-schema/contract-schema-type-extractor.mjs +2 -31
  111. package/dist/coverage-transport/control-v2-parse-object.d.mts +12 -0
  112. package/dist/coverage-transport/control-v2-parse-object.mjs +28 -0
  113. package/dist/coverage-transport/control-v2.mjs +2 -27
  114. package/dist/dependency-license-policy/audit-directory.d.mts +28 -0
  115. package/dist/dependency-license-policy/audit-directory.mjs +115 -0
  116. package/dist/dependency-license-policy/audit-signals.d.mts +12 -0
  117. package/dist/dependency-license-policy/audit-signals.mjs +44 -0
  118. package/dist/dependency-license-policy/audit-store.d.mts +12 -0
  119. package/dist/dependency-license-policy/audit-store.mjs +32 -0
  120. package/dist/dependency-license-policy/collect.d.mts +3 -1
  121. package/dist/dependency-license-policy/collect.mjs +16 -21
  122. package/dist/dependency-license-policy/execute-pnpm.d.mts +10 -0
  123. package/dist/dependency-license-policy/execute-pnpm.mjs +83 -0
  124. package/dist/dependency-license-policy/types.d.mts +11 -9
  125. package/dist/dependency-license-policy/workspace.d.mts +3 -1
  126. package/dist/dependency-license-policy/workspace.mjs +12 -5
  127. package/dist/gh-api-shell-quoting/argument-scan.d.mts +7 -0
  128. package/dist/gh-api-shell-quoting/argument-scan.mjs +35 -0
  129. package/dist/gh-api-shell-quoting/logical-line.d.mts +3 -0
  130. package/dist/gh-api-shell-quoting/logical-line.mjs +90 -0
  131. package/dist/gh-api-shell-quoting/scan.d.mts +2 -4
  132. package/dist/gh-api-shell-quoting/scan.mjs +1 -119
  133. package/dist/gh-cli/diff.d.mts +24 -4
  134. package/dist/gh-cli/diff.mjs +111 -4
  135. package/dist/gh-cli/index.d.mts +2 -1
  136. package/dist/gh-cli/index.mjs +1 -1
  137. package/dist/gha-workspace-policy/docker-options.d.mts +4 -0
  138. package/dist/gha-workspace-policy/docker-options.mjs +93 -0
  139. package/dist/gha-workspace-policy/docker-workspace-user.mjs +1 -168
  140. package/dist/gha-workspace-policy/shell-token-modes.d.mts +10 -0
  141. package/dist/gha-workspace-policy/shell-token-modes.mjs +31 -0
  142. package/dist/gha-workspace-policy/shell-tokens.d.mts +1 -0
  143. package/dist/gha-workspace-policy/shell-tokens.mjs +65 -0
  144. package/dist/index.d.mts +2 -1
  145. package/dist/index.mjs +1 -1
  146. package/dist/openapi-document/contract-schema-types.d.mts +1 -1
  147. package/dist/openapi-document/openapi-types.d.mts +1 -1
  148. package/dist/pnpm-install/native-family.d.mts +3 -0
  149. package/dist/pnpm-install/native-family.mjs +31 -0
  150. package/dist/pnpm-install/native-health.d.mts +1 -4
  151. package/dist/pnpm-install/native-health.mjs +2 -31
  152. package/dist/retrospective-facts/local-merge.d.mts +2 -0
  153. package/dist/retrospective-facts/local-merge.mjs +21 -0
  154. package/dist/retrospective-facts/local.mjs +1 -21
  155. package/dist/retrospective-transcript/claude-blocks.d.mts +3 -0
  156. package/dist/retrospective-transcript/claude-blocks.mjs +38 -0
  157. package/dist/retrospective-transcript/claude.mjs +4 -34
  158. package/dist/retrospective-transcript/codex-payload.d.mts +2 -0
  159. package/dist/retrospective-transcript/codex-payload.mjs +51 -0
  160. package/dist/retrospective-transcript/codex-records.d.mts +2 -0
  161. package/dist/retrospective-transcript/codex-records.mjs +95 -0
  162. package/dist/retrospective-transcript/codex.mjs +2 -144
  163. package/dist/retrospective-transcript/index.d.mts +3 -16
  164. package/dist/retrospective-transcript/index.mjs +4 -48
  165. package/dist/retrospective-transcript/resolve-transcript-file.d.mts +19 -0
  166. package/dist/retrospective-transcript/resolve-transcript-file.mjs +50 -0
  167. package/dist/session-friction/command-segments.d.mts +3 -0
  168. package/dist/session-friction/command-segments.mjs +94 -0
  169. package/dist/session-friction/log-file.d.mts +7 -0
  170. package/dist/session-friction/log-file.mjs +83 -0
  171. package/dist/session-friction/log.mjs +3 -83
  172. package/dist/session-friction/normalize.mjs +1 -94
  173. package/dist/sql-scanner/comments.d.mts +1 -0
  174. package/dist/sql-scanner/comments.mjs +101 -0
  175. package/dist/sql-scanner/index.d.mts +1 -1
  176. package/dist/sql-scanner/index.mjs +1 -100
  177. package/dist/sql-scanner/literals.mjs +1 -35
  178. package/dist/sql-scanner/quote-decode.d.mts +2 -0
  179. package/dist/sql-scanner/quote-decode.mjs +35 -0
  180. package/dist/vitest-diagnostics/index.d.mts +7 -0
  181. package/dist/vitest-diagnostics/index.mjs +4 -0
  182. package/dist/vitest-diagnostics/vitest-fork-exit-error-detail.d.mts +7 -0
  183. package/dist/vitest-diagnostics/vitest-fork-exit-error-detail.mjs +26 -0
  184. package/dist/vitest-diagnostics/vitest-fork-exit-records.d.mts +26 -0
  185. package/dist/vitest-diagnostics/vitest-fork-exit-records.mjs +161 -0
  186. package/dist/vitest-diagnostics/vitest-fork-exit-sentinel.d.mts +14 -0
  187. package/dist/vitest-diagnostics/vitest-fork-exit-sentinel.mjs +131 -0
  188. package/dist/vitest-diagnostics/vitest-fork-leak-detection.d.mts +26 -0
  189. package/dist/vitest-diagnostics/vitest-fork-leak-detection.mjs +96 -0
  190. package/dist/vitest-diagnostics/vitest-fork-leak-diagnostics.d.mts +3 -0
  191. package/dist/vitest-diagnostics/vitest-fork-leak-diagnostics.mjs +14 -0
  192. package/dist/vitest-diagnostics/vitest-fork-leak-episode.d.mts +37 -0
  193. package/dist/vitest-diagnostics/vitest-fork-leak-episode.mjs +118 -0
  194. package/dist/vitest-diagnostics/vitest-worker-exit-diagnostics-errors.d.mts +2 -0
  195. package/dist/vitest-diagnostics/vitest-worker-exit-diagnostics-errors.mjs +42 -0
  196. package/dist/vitest-diagnostics/vitest-worker-exit-diagnostics-reporter.d.mts +12 -0
  197. package/dist/vitest-diagnostics/vitest-worker-exit-diagnostics-reporter.mjs +142 -0
  198. package/dist/workflow-policy/concurrency-scope.d.mts +4 -0
  199. package/dist/workflow-policy/concurrency-scope.mjs +66 -0
  200. package/dist/workflow-policy/concurrency.d.mts +10 -0
  201. package/dist/workflow-policy/concurrency.mjs +77 -0
  202. package/dist/workflow-policy/condition-entailment.d.mts +20 -0
  203. package/dist/workflow-policy/condition-entailment.mjs +132 -0
  204. package/dist/workflow-policy/condition-split.d.mts +15 -0
  205. package/dist/workflow-policy/condition-split.mjs +71 -0
  206. package/dist/workflow-policy/graph.d.mts +2 -0
  207. package/dist/workflow-policy/graph.mjs +138 -0
  208. package/dist/workflow-policy/index.d.mts +7 -0
  209. package/dist/workflow-policy/index.mjs +6 -0
  210. package/dist/workflow-policy/permission-mismatches.d.mts +3 -0
  211. package/dist/workflow-policy/permission-mismatches.mjs +52 -0
  212. package/dist/workflow-policy/permissions.d.mts +16 -0
  213. package/dist/workflow-policy/permissions.mjs +83 -0
  214. package/dist/workflow-policy/secret-readiness.d.mts +18 -0
  215. package/dist/workflow-policy/secret-readiness.mjs +163 -0
  216. package/dist/workflow-policy/shell-comments.d.mts +7 -0
  217. package/dist/workflow-policy/shell-comments.mjs +28 -0
  218. package/dist/workflow-policy/types.d.mts +90 -0
  219. package/dist/workflow-policy/types.mjs +1 -0
  220. package/dist/workspace-gates/manifest-version-parser.mjs +1 -52
  221. package/dist/workspace-gates/manifest-version-skips.d.mts +1 -0
  222. package/dist/workspace-gates/manifest-version-skips.mjs +52 -0
  223. package/docs/workflow-and-compiler.md +74 -0
  224. package/package.json +26 -2
  225. package/skills/agent-workflow/SKILL.md +4 -4
  226. package/skills/agent-workflow/references/review-response.md +47 -44
  227. package/skills/backend-vitest-test-authoring/SKILL.md +9 -6
  228. package/skills/blackboard/SKILL.md +8 -8
  229. package/skills/dependabot/SKILL.md +68 -67
  230. package/skills/dotnet-test-authoring/SKILL.md +11 -12
  231. package/skills/git-commit-checklist/SKILL.md +3 -3
  232. package/skills/github-actions-authoring/SKILL.md +40 -38
  233. package/skills/github-actions-checklist/SKILL.md +93 -85
  234. package/skills/github-issue/SKILL.md +82 -78
  235. package/skills/nextjs-vitest-test-authoring/SKILL.md +8 -6
  236. package/skills/npm-publishing/SKILL.md +42 -38
  237. package/skills/organize-github-issues/SKILL.md +18 -22
  238. package/skills/package-json-checklist/SKILL.md +5 -5
  239. package/skills/planning/SKILL.md +3 -3
  240. package/skills/playwright-authoring/SKILL.md +10 -9
  241. package/skills/postgres-node-performance-tuning/SKILL.md +10 -6
  242. package/skills/postgres-partitioning-uuid-v7/SKILL.md +10 -7
  243. package/skills/pr-description/SKILL.md +4 -3
  244. package/skills/retrospective/SKILL.md +14 -15
  245. package/skills/retrospective-distill/SKILL.md +14 -14
  246. package/skills/review-ci-logs/SKILL.md +18 -19
  247. package/skills/review-github-issue-taxonomy/SKILL.md +21 -18
  248. package/skills/revisit-followups/SKILL.md +3 -3
  249. package/skills/stacked-prs/SKILL.md +30 -61
  250. package/skills/static-analysis-checklist/SKILL.md +6 -4
  251. package/skills/storybook-authoring/SKILL.md +9 -9
  252. package/skills/swift-test-authoring/SKILL.md +10 -9
  253. package/skills/test-authoring/SKILL.md +14 -14
  254. package/skills/test-authoring/references/core-practice.md +12 -11
  255. package/skills/test-authoring/references/dependency-boundaries.md +36 -35
  256. package/skills/test-authoring/references/tautological-tests.md +45 -44
  257. package/skills/vitest-test-authoring/SKILL.md +9 -6
@@ -1,6 +1,7 @@
1
1
  import { createOutputConsumer } from './output.mjs';
2
2
  import { tailText } from './tail.mjs';
3
3
  import { TailQueue } from './tail-queue.mjs';
4
+ import { terminateAttempt } from './termination.mjs';
4
5
  import { createWatchdogLifecycle, registerWatchdogSetup } from './watchdog.mjs';
5
6
  export function runAttempt(options, deps, deadline, attempt) {
6
7
  return new Promise((resolve, reject) => {
@@ -18,8 +19,17 @@ export function runAttempt(options, deps, deadline, attempt) {
18
19
  const drainTimeoutMs = options.graceMs + (options.processGroupDrainMs ?? options.graceMs);
19
20
  const tail = new TailQueue(maxTail);
20
21
  let startupProgress = false, semanticProgress = false;
21
- let failure, hasFailure = false, childExited = false, closeExit, draining = false, drainDone = false, reason = 'exit', complete = false, terminating = false;
22
- let killTimer, stallTimer;
22
+ let failure, hasFailure = false, closeExit, draining = false, drainDone = false, complete = false;
23
+ let stallTimer;
24
+ const control = {
25
+ childExited: false,
26
+ deps,
27
+ graceMs: options.graceMs,
28
+ killTimer: undefined,
29
+ process,
30
+ reason: 'exit',
31
+ terminating: false,
32
+ };
23
33
  const watchdog = createWatchdogLifecycle();
24
34
  const listeners = [];
25
35
  const captureFailure = (error) => !hasFailure && ((failure = error), (hasFailure = true));
@@ -28,18 +38,18 @@ export function runAttempt(options, deps, deadline, attempt) {
28
38
  return;
29
39
  complete = true;
30
40
  deps.clearTimeout(deadlineTimer);
31
- if (killTimer)
32
- deps.clearTimeout(killTimer);
41
+ if (control.killTimer)
42
+ deps.clearTimeout(control.killTimer);
33
43
  deps.clearTimeout(stallTimer);
34
44
  watchdog.complete(captureFailure);
35
45
  for (const remove of listeners)
36
46
  remove();
37
47
  const result = {
38
48
  attempts: 0,
39
- deadlineExceeded: reason === 'deadline',
49
+ deadlineExceeded: control.reason === 'deadline',
40
50
  diagnosticTail: tailText(tail.toBuffer(), maxTail),
41
51
  exit,
42
- reason,
52
+ reason: control.reason,
43
53
  semanticProgress,
44
54
  startupProgress,
45
55
  };
@@ -48,37 +58,8 @@ export function runAttempt(options, deps, deadline, attempt) {
48
58
  else
49
59
  resolve(result);
50
60
  };
51
- const signal = (value) => {
52
- try {
53
- deps.killProcessGroup(process.processGroupId, value);
54
- }
55
- catch { }
56
- try {
57
- process.kill(value);
58
- }
59
- catch { }
60
- };
61
61
  const terminate = (nextReason) => {
62
- if (nextReason === 'deadline' && childExited && reason === 'exit')
63
- return;
64
- if (nextReason === 'parent-signal' || nextReason === 'deadline') {
65
- if (reason === 'exit' || reason === 'startup-stall' || reason === 'semantic-stall')
66
- reason = nextReason;
67
- else if (reason !== nextReason)
68
- return;
69
- }
70
- else if (reason !== 'exit')
71
- return;
72
- else if (nextReason !== 'exit')
73
- reason = nextReason;
74
- if (terminating)
75
- return;
76
- terminating = true;
77
- killTimer = deps.setTimeout(() => {
78
- if (deps.isProcessGroupAlive(process.processGroupId))
79
- signal('SIGKILL');
80
- }, options.graceMs);
81
- signal('SIGTERM');
62
+ terminateAttempt(control, nextReason);
82
63
  };
83
64
  const fail = (error) => {
84
65
  if (hasFailure || complete)
@@ -108,12 +89,12 @@ export function runAttempt(options, deps, deadline, attempt) {
108
89
  const event = options.onLine(value);
109
90
  if (event === 'startup' && !startupProgress) {
110
91
  startupProgress = true;
111
- if (!childExited)
92
+ if (!control.childExited)
112
93
  armStall(options.semanticStallMs, 'semantic-stall');
113
94
  }
114
95
  if (event === 'semantic') {
115
96
  semanticProgress = true;
116
- if (startupProgress && !childExited)
97
+ if (startupProgress && !control.childExited)
117
98
  armStall(options.semanticStallMs, 'semantic-stall');
118
99
  }
119
100
  };
@@ -146,13 +127,13 @@ export function runAttempt(options, deps, deadline, attempt) {
146
127
  }
147
128
  process.on('error', () => terminate('exit'));
148
129
  process.on('exit', () => {
149
- if (reason === 'exit' && deps.now() >= deadline)
150
- reason = 'deadline';
151
- childExited = true;
130
+ if (control.reason === 'exit' && deps.now() >= deadline)
131
+ control.reason = 'deadline';
132
+ control.childExited = true;
152
133
  deps.clearTimeout(stallTimer);
153
134
  if (!deps.isProcessGroupAlive(process.processGroupId))
154
135
  return;
155
- terminate(reason);
136
+ terminate(control.reason);
156
137
  drain();
157
138
  });
158
139
  process.on('close', (code, value) => {
@@ -163,15 +144,15 @@ export function runAttempt(options, deps, deadline, attempt) {
163
144
  catch (error) {
164
145
  fail(error);
165
146
  }
166
- if (!childExited &&
147
+ if (!control.childExited &&
167
148
  deps.now() >= deadline &&
168
- reason !== 'parent-signal' &&
169
- reason !== 'deadline')
170
- reason = 'deadline';
149
+ control.reason !== 'parent-signal' &&
150
+ control.reason !== 'deadline')
151
+ control.reason = 'deadline';
171
152
  closeExit = { code, signal: value };
172
153
  if (!deps.isProcessGroupAlive(process.processGroupId))
173
154
  return finish(closeExit);
174
- terminate(reason);
155
+ terminate(control.reason);
175
156
  drain();
176
157
  if (drainDone || hasFailure)
177
158
  finish(closeExit);
@@ -183,7 +164,7 @@ export function runAttempt(options, deps, deadline, attempt) {
183
164
  now: () => deps.now(),
184
165
  process,
185
166
  terminate: () => {
186
- if (!childExited)
167
+ if (!control.childExited)
187
168
  terminate('provider-watchdog');
188
169
  },
189
170
  });
@@ -0,0 +1,11 @@
1
+ import type { BrowserSessionDeps, BrowserSessionProcess, BrowserSessionResult } from './types.mts';
2
+ export type AttemptControl = {
3
+ childExited: boolean;
4
+ deps: BrowserSessionDeps;
5
+ graceMs: number;
6
+ killTimer: unknown;
7
+ process: BrowserSessionProcess;
8
+ reason: BrowserSessionResult['reason'];
9
+ terminating: boolean;
10
+ };
11
+ export declare function terminateAttempt(control: AttemptControl, nextReason: BrowserSessionResult['reason']): void;
@@ -0,0 +1,34 @@
1
+ function signalProcess(control, value) {
2
+ try {
3
+ control.deps.killProcessGroup(control.process.processGroupId, value);
4
+ }
5
+ catch { }
6
+ try {
7
+ control.process.kill(value);
8
+ }
9
+ catch { }
10
+ }
11
+ export function terminateAttempt(control, nextReason) {
12
+ if (nextReason === 'deadline' && control.childExited && control.reason === 'exit')
13
+ return;
14
+ if (nextReason === 'parent-signal' || nextReason === 'deadline') {
15
+ if (control.reason === 'exit' ||
16
+ control.reason === 'startup-stall' ||
17
+ control.reason === 'semantic-stall')
18
+ control.reason = nextReason;
19
+ else if (control.reason !== nextReason)
20
+ return;
21
+ }
22
+ else if (control.reason !== 'exit')
23
+ return;
24
+ else if (nextReason !== 'exit')
25
+ control.reason = nextReason;
26
+ if (control.terminating)
27
+ return;
28
+ control.terminating = true;
29
+ control.killTimer = control.deps.setTimeout(() => {
30
+ if (control.deps.isProcessGroupAlive(control.process.processGroupId))
31
+ signalProcess(control, 'SIGKILL');
32
+ }, control.graceMs);
33
+ signalProcess(control, 'SIGTERM');
34
+ }
@@ -0,0 +1,26 @@
1
+ type ParsedFlagOptions = {
2
+ values: Record<string, string | undefined>;
3
+ workflowDirectories: string[];
4
+ actionDirectories: string[];
5
+ };
6
+ type FlagOptionFailure = {
7
+ kind: 'error';
8
+ message: string;
9
+ } | {
10
+ kind: 'help';
11
+ };
12
+ type LinkSkillCli = FlagOptionFailure | {
13
+ kind: 'link-skill';
14
+ name: string;
15
+ sourceRoot: string;
16
+ targetRoot: string;
17
+ };
18
+ type HttpOriginCli = FlagOptionFailure | {
19
+ kind: 'http-origin';
20
+ field: string;
21
+ value: string;
22
+ };
23
+ export declare function parseLinkSkill(args: readonly string[]): LinkSkillCli;
24
+ export declare function parseHttpOrigin(args: readonly string[]): HttpOriginCli;
25
+ export declare function parseOptions(args: readonly string[], allowed: readonly string[], command: string, missingValue: string | ((flag: string) => string)): ParsedFlagOptions | FlagOptionFailure;
26
+ export {};
@@ -0,0 +1,72 @@
1
+ export function parseLinkSkill(args) {
2
+ const [name, ...flags] = args;
3
+ if (name === undefined || name.startsWith('-'))
4
+ return { kind: 'error', message: 'link-skill requires a skill name' };
5
+ const options = parseOptions(flags, ['source-root', 'target-root'], 'link-skill', 'path');
6
+ if ('kind' in options)
7
+ return options;
8
+ const sourceRoot = options.values['source-root'];
9
+ const targetRoot = options.values['target-root'];
10
+ if (sourceRoot === undefined || targetRoot === undefined) {
11
+ return { kind: 'error', message: 'link-skill requires --source-root and --target-root' };
12
+ }
13
+ return {
14
+ kind: 'link-skill',
15
+ name,
16
+ sourceRoot,
17
+ targetRoot,
18
+ };
19
+ }
20
+ export function parseHttpOrigin(args) {
21
+ let field = 'origin';
22
+ const values = [];
23
+ let index = 0;
24
+ while (index < args.length) {
25
+ const flag = args[index];
26
+ index += 1;
27
+ if (flag === '--help' || flag === '-h')
28
+ return { kind: 'help' };
29
+ if (flag === '--field') {
30
+ const value = args[index];
31
+ if (value === undefined)
32
+ return { kind: 'error', message: '--field requires a name' };
33
+ field = value;
34
+ index += 1;
35
+ continue;
36
+ }
37
+ if (flag === '--') {
38
+ values.push(...args.slice(index));
39
+ break;
40
+ }
41
+ if (flag.startsWith('-'))
42
+ return { kind: 'error', message: `unknown http-origin option: ${flag}` };
43
+ values.push(flag);
44
+ }
45
+ if (values.length > 1)
46
+ return { kind: 'error', message: 'http-origin accepts at most one value' };
47
+ return { kind: 'http-origin', field, value: values[0] ?? '' };
48
+ }
49
+ export function parseOptions(args, allowed, command, missingValue) {
50
+ const result = { values: {}, workflowDirectories: [], actionDirectories: [] };
51
+ for (let index = 0; index < args.length; index += 1) {
52
+ const flag = args[index];
53
+ if (flag === '--help' || flag === '-h')
54
+ return { kind: 'help' };
55
+ if (!flag.startsWith('--') || !allowed.includes(flag.slice(2))) {
56
+ return { kind: 'error', message: `unknown ${command} option: ${flag}` };
57
+ }
58
+ const value = args[index + 1];
59
+ if (value === undefined) {
60
+ const noun = typeof missingValue === 'string' ? missingValue : missingValue(flag);
61
+ return { kind: 'error', message: `${flag} requires a ${noun}` };
62
+ }
63
+ if (flag === '--workflow-directory')
64
+ result.workflowDirectories.push(value);
65
+ else if (flag === '--action-directory')
66
+ result.actionDirectories.push(value);
67
+ else
68
+ result.values[flag.slice(2)] = value;
69
+ index += 1;
70
+ }
71
+ return result;
72
+ }
@@ -0,0 +1,42 @@
1
+ type ForwardedCommand = {
2
+ kind: 'with-host-lock';
3
+ args: string[];
4
+ } | {
5
+ kind: 'agent-harness-config';
6
+ args: string[];
7
+ } | {
8
+ kind: 'pnpm-install';
9
+ args: string[];
10
+ } | {
11
+ kind: 'vitest-blob-manifest';
12
+ args: string[];
13
+ } | {
14
+ kind: 'vitest-report-attempt';
15
+ args: string[];
16
+ } | {
17
+ kind: 'prepare-vitest-reports';
18
+ args: string[];
19
+ } | {
20
+ kind: 'nuget-central-version';
21
+ args: string[];
22
+ } | {
23
+ kind: 'swift-semantic-equal';
24
+ args: string[];
25
+ } | {
26
+ kind: 'post-review';
27
+ args: string[];
28
+ } | {
29
+ kind: 'stage-review-payload';
30
+ args: string[];
31
+ } | {
32
+ kind: 'retrospective-transcript';
33
+ args: string[];
34
+ } | {
35
+ kind: 'retrospective-facts';
36
+ args: string[];
37
+ } | {
38
+ kind: 'agent-blackboard';
39
+ args: string[];
40
+ };
41
+ export declare function parseForwardedCommand(command: string | undefined, args: string[]): ForwardedCommand | undefined;
42
+ export {};
@@ -0,0 +1,29 @@
1
+ export function parseForwardedCommand(command, args) {
2
+ if (command === 'with-host-lock')
3
+ return { kind: 'with-host-lock', args };
4
+ if (command === 'agent-harness-config')
5
+ return { kind: 'agent-harness-config', args };
6
+ if (command === 'pnpm-install')
7
+ return { kind: 'pnpm-install', args };
8
+ if (command === 'vitest-blob-manifest')
9
+ return { kind: 'vitest-blob-manifest', args };
10
+ if (command === 'vitest-report-attempt')
11
+ return { kind: 'vitest-report-attempt', args };
12
+ if (command === 'prepare-vitest-reports')
13
+ return { kind: 'prepare-vitest-reports', args };
14
+ if (command === 'nuget-central-version')
15
+ return { kind: 'nuget-central-version', args };
16
+ if (command === 'swift-semantic-equal')
17
+ return { kind: 'swift-semantic-equal', args };
18
+ if (command === 'post-review')
19
+ return { kind: 'post-review', args };
20
+ if (command === 'stage-review-payload')
21
+ return { kind: 'stage-review-payload', args };
22
+ if (command === 'retrospective-transcript')
23
+ return { kind: 'retrospective-transcript', args };
24
+ if (command === 'retrospective-facts')
25
+ return { kind: 'retrospective-facts', args };
26
+ if (command === 'agent-blackboard')
27
+ return { kind: 'agent-blackboard', args };
28
+ return undefined;
29
+ }
@@ -1,7 +1,6 @@
1
1
  import type { ParsedCli } from './parse.mts';
2
- export declare function parseLinkSkill(args: readonly string[]): ParsedCli;
2
+ export { parseHttpOrigin, parseLinkSkill } from './parse-direct-options.mts';
3
3
  export declare function parseRunnerPortPolicy(args: readonly string[]): ParsedCli;
4
- export declare function parseHttpOrigin(args: readonly string[]): ParsedCli;
5
4
  export declare function parseRequireUpToDate(args: readonly string[]): ParsedCli;
6
5
  export declare function parseGitleaksDirectoryScan(args: readonly string[]): ParsedCli;
7
6
  export declare function parseAstGrepPack(args: readonly string[]): ParsedCli;
@@ -1,22 +1,5 @@
1
- export function parseLinkSkill(args) {
2
- const [name, ...flags] = args;
3
- if (name === undefined || name.startsWith('-'))
4
- return { kind: 'error', message: 'link-skill requires a skill name' };
5
- const options = parseOptions(flags, ['source-root', 'target-root'], 'link-skill', 'path');
6
- if ('kind' in options)
7
- return options;
8
- const sourceRoot = options.values['source-root'];
9
- const targetRoot = options.values['target-root'];
10
- if (sourceRoot === undefined || targetRoot === undefined) {
11
- return { kind: 'error', message: 'link-skill requires --source-root and --target-root' };
12
- }
13
- return {
14
- kind: 'link-skill',
15
- name,
16
- sourceRoot,
17
- targetRoot,
18
- };
19
- }
1
+ import { parseOptions } from './parse-direct-options.mjs';
2
+ export { parseHttpOrigin, parseLinkSkill } from './parse-direct-options.mjs';
20
3
  export function parseRunnerPortPolicy(args) {
21
4
  const options = parseOptions(args, ['file', 'reserved'], 'runner-port-policy', (flag) => flag === '--reserved' ? 'port' : 'path');
22
5
  if ('kind' in options)
@@ -33,35 +16,6 @@ export function parseRunnerPortPolicy(args) {
33
16
  ...(reserved === undefined ? {} : { reserved }),
34
17
  };
35
18
  }
36
- export function parseHttpOrigin(args) {
37
- let field = 'origin';
38
- const values = [];
39
- let index = 0;
40
- while (index < args.length) {
41
- const flag = args[index];
42
- index += 1;
43
- if (flag === '--help' || flag === '-h')
44
- return { kind: 'help' };
45
- if (flag === '--field') {
46
- const value = args[index];
47
- if (value === undefined)
48
- return { kind: 'error', message: '--field requires a name' };
49
- field = value;
50
- index += 1;
51
- continue;
52
- }
53
- if (flag === '--') {
54
- values.push(...args.slice(index));
55
- break;
56
- }
57
- if (flag.startsWith('-'))
58
- return { kind: 'error', message: `unknown http-origin option: ${flag}` };
59
- values.push(flag);
60
- }
61
- if (values.length > 1)
62
- return { kind: 'error', message: 'http-origin accepts at most one value' };
63
- return { kind: 'http-origin', field, value: values[0] ?? '' };
64
- }
65
19
  export function parseRequireUpToDate(args) {
66
20
  const options = parseOptions(args, ['remote', 'branch'], 'require-up-to-date', 'name');
67
21
  if ('kind' in options)
@@ -122,27 +76,3 @@ export function parseGhaWorkspacePolicy(args) {
122
76
  : { actionDirectories: options.actionDirectories }),
123
77
  };
124
78
  }
125
- function parseOptions(args, allowed, command, missingValue) {
126
- const result = { values: {}, workflowDirectories: [], actionDirectories: [] };
127
- for (let index = 0; index < args.length; index += 1) {
128
- const flag = args[index];
129
- if (flag === '--help' || flag === '-h')
130
- return { kind: 'help' };
131
- if (!flag.startsWith('--') || !allowed.includes(flag.slice(2))) {
132
- return { kind: 'error', message: `unknown ${command} option: ${flag}` };
133
- }
134
- const value = args[index + 1];
135
- if (value === undefined) {
136
- const noun = typeof missingValue === 'string' ? missingValue : missingValue(flag);
137
- return { kind: 'error', message: `${flag} requires a ${noun}` };
138
- }
139
- if (flag === '--workflow-directory')
140
- result.workflowDirectories.push(value);
141
- else if (flag === '--action-directory')
142
- result.actionDirectories.push(value);
143
- else
144
- result.values[flag.slice(2)] = value;
145
- index += 1;
146
- }
147
- return result;
148
- }
@@ -1,5 +1,6 @@
1
1
  import { parseGhaArtifactsCleanup, } from './parse-gha-artifacts-cleanup.mjs';
2
2
  import { parseGhaRuntimeAudit } from './parse-gha-runtime-audit.mjs';
3
+ import { parseForwardedCommand } from './parse-forwarded-commands.mjs';
3
4
  import { commandNames } from './usage.mjs';
4
5
  import { parseAstGrepExamples, parseAstGrepPack, parseGhaWorkspacePolicy, parseGitleaksDirectoryScan, parseHttpOrigin, parseLinkSkill, parseRequireUpToDate, parseRunnerPortPolicy, } from './parse-options.mjs';
5
6
  const SCRIPT_COMMANDS = new Set([
@@ -35,40 +36,17 @@ export function parseCli(argv) {
35
36
  const help = parseCommandHelp(command, rest);
36
37
  if (help)
37
38
  return help;
39
+ const forwarded = parseForwardedCommand(command, rest);
40
+ if (forwarded)
41
+ return forwarded;
38
42
  if (command === 'runner-port-policy')
39
43
  return parseRunnerPortPolicy(rest);
40
- if (command === 'with-host-lock')
41
- return { kind: 'with-host-lock', args: rest };
42
- if (command === 'agent-harness-config')
43
- return { kind: 'agent-harness-config', args: rest };
44
44
  if (command === 'gha-runtime-audit')
45
45
  return parseGhaRuntimeAudit(rest);
46
- if (command === 'pnpm-install')
47
- return { kind: 'pnpm-install', args: rest };
48
- if (command === 'vitest-blob-manifest')
49
- return { kind: 'vitest-blob-manifest', args: rest };
50
- if (command === 'vitest-report-attempt')
51
- return { kind: 'vitest-report-attempt', args: rest };
52
- if (command === 'prepare-vitest-reports')
53
- return { kind: 'prepare-vitest-reports', args: rest };
54
- if (command === 'nuget-central-version')
55
- return { kind: 'nuget-central-version', args: rest };
56
- if (command === 'swift-semantic-equal')
57
- return { kind: 'swift-semantic-equal', args: rest };
58
- if (command === 'post-review')
59
- return { kind: 'post-review', args: rest };
60
- if (command === 'stage-review-payload')
61
- return { kind: 'stage-review-payload', args: rest };
62
46
  if (command === 'http-origin')
63
47
  return parseHttpOrigin(rest);
64
- if (command === 'retrospective-transcript')
65
- return { kind: 'retrospective-transcript', args: rest };
66
48
  if (command === 'link-skill')
67
49
  return parseLinkSkill(rest);
68
- if (command === 'retrospective-facts')
69
- return { kind: 'retrospective-facts', args: rest };
70
- if (command === 'agent-blackboard')
71
- return { kind: 'agent-blackboard', args: rest };
72
50
  if (command === 'require-up-to-date')
73
51
  return parseRequireUpToDate(rest);
74
52
  if (command === 'gitleaks-directory-scan')
@@ -0,0 +1,27 @@
1
+ type Probe = {
2
+ key: string;
3
+ replay(): unknown;
4
+ value: unknown;
5
+ };
6
+ export type CompilerHostProbeSnapshot = {
7
+ probes: readonly Probe[];
8
+ stableDuringCapture: boolean;
9
+ };
10
+ export interface CompilerFilesystemHost {
11
+ directoryExists?(path: string): boolean;
12
+ fileExists(path: string): boolean;
13
+ getDirectories?(path: string): string[];
14
+ readDirectory?(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[];
15
+ readFile(path: string): string | undefined;
16
+ realpath?(path: string): string;
17
+ }
18
+ export interface CompilerHostTrackingOptions {
19
+ afterRead?(path: string): boolean | void;
20
+ readFileReplay?: 'always' | 'when-filesystem-metadata-stable';
21
+ }
22
+ export declare function trackCompilerHost<Host extends CompilerFilesystemHost>(host: Host, options?: CompilerHostTrackingOptions): {
23
+ host: Host;
24
+ snapshot(): CompilerHostProbeSnapshot;
25
+ };
26
+ export declare function compilerHostProbesAreFresh(snapshot: CompilerHostProbeSnapshot): boolean;
27
+ export {};
@@ -0,0 +1,116 @@
1
+ import { statSync } from 'node:fs';
2
+ import { normalize } from 'node:path';
3
+ export function trackCompilerHost(host, options) {
4
+ const probes = new Map();
5
+ let stableDuringCapture = true;
6
+ const record = (key, value, replay) => {
7
+ const existing = probes.get(key);
8
+ if (existing && !sameValue(existing.value, value))
9
+ stableDuringCapture = false;
10
+ else if (!existing)
11
+ probes.set(key, { key, replay, value });
12
+ return value;
13
+ };
14
+ const wrapBoolean = (operation, original) => (path) => {
15
+ const key = `${operation}:${normalizedPath(path)}`;
16
+ return record(key, original(path), () => original(path));
17
+ };
18
+ const capturePaths = (key, originalPaths, replay) => {
19
+ record(key, normalizedPaths(originalPaths), () => normalizedPaths(replay()));
20
+ return originalPaths;
21
+ };
22
+ const wrapPaths = (operation, original) => (path) => capturePaths(`${operation}:${normalizedPath(path)}`, original(path), () => original(path));
23
+ const readFile = host.readFile.bind(host);
24
+ host.readFile = (path) => {
25
+ const key = `readFile:${normalizedPath(path)}`;
26
+ const usesMetadataReplay = options?.readFileReplay === 'when-filesystem-metadata-stable';
27
+ const before = usesMetadataReplay ? fileMetadata(path) : undefined;
28
+ const value = readFile(path);
29
+ if (options?.afterRead?.(path))
30
+ stableDuringCapture = false;
31
+ const after = usesMetadataReplay ? fileMetadata(path) : undefined;
32
+ if (usesMetadataReplay && !sameMetadata(before, after))
33
+ stableDuringCapture = false;
34
+ return record(key, value, () => {
35
+ const current = usesMetadataReplay ? fileMetadata(path) : undefined;
36
+ return usesMetadataReplay &&
37
+ value !== undefined &&
38
+ after !== undefined &&
39
+ sameMetadata(after, current)
40
+ ? value
41
+ : readFile(path);
42
+ });
43
+ };
44
+ host.fileExists = wrapBoolean('fileExists', host.fileExists.bind(host));
45
+ if (host.directoryExists)
46
+ host.directoryExists = wrapBoolean('directoryExists', host.directoryExists.bind(host));
47
+ if (host.getDirectories)
48
+ host.getDirectories = wrapPaths('getDirectories', host.getDirectories.bind(host));
49
+ if (host.readDirectory) {
50
+ const readDirectory = host.readDirectory.bind(host);
51
+ host.readDirectory = (path, extensions, exclude, include, depth) => {
52
+ const argumentsKey = JSON.stringify([extensions, exclude, include, depth]);
53
+ const key = `readDirectory:${normalizedPath(path)}:${argumentsKey}`;
54
+ return capturePaths(key, readDirectory(path, extensions, exclude, include, depth), () => readDirectory(path, extensions, exclude, include, depth));
55
+ };
56
+ }
57
+ if (host.realpath) {
58
+ const realpath = host.realpath.bind(host);
59
+ host.realpath = (path) => {
60
+ const key = `realpath:${normalizedPath(path)}`;
61
+ const read = () => normalizedPath(realpath(path));
62
+ return record(key, read(), read);
63
+ };
64
+ }
65
+ return {
66
+ host,
67
+ snapshot: () => ({
68
+ probes: [...probes.values()].toSorted((left, right) => left.key.localeCompare(right.key)),
69
+ stableDuringCapture,
70
+ }),
71
+ };
72
+ }
73
+ export function compilerHostProbesAreFresh(snapshot) {
74
+ return (snapshot.stableDuringCapture &&
75
+ snapshot.probes.every((probe) => {
76
+ try {
77
+ return sameValue(probe.value, probe.replay());
78
+ }
79
+ catch (error) {
80
+ if (isMissingFilesystemInput(error))
81
+ return false;
82
+ throw error;
83
+ }
84
+ }));
85
+ }
86
+ function fileMetadata(path) {
87
+ try {
88
+ const stat = statSync(path, { bigint: true });
89
+ return { ctimeNs: stat.ctimeNs, mtimeNs: stat.mtimeNs, size: stat.size };
90
+ }
91
+ catch (error) {
92
+ if (isMissingFilesystemInput(error))
93
+ return undefined;
94
+ throw error;
95
+ }
96
+ }
97
+ function normalizedPath(path) {
98
+ return normalize(path).replaceAll('\\', '/');
99
+ }
100
+ function normalizedPaths(paths) {
101
+ return [...new Set(paths.map(normalizedPath))].toSorted();
102
+ }
103
+ function sameMetadata(left, right) {
104
+ return (left?.ctimeNs === right?.ctimeNs &&
105
+ left?.mtimeNs === right?.mtimeNs &&
106
+ left?.size === right?.size);
107
+ }
108
+ function sameValue(left, right) {
109
+ return Array.isArray(left) && Array.isArray(right)
110
+ ? left.length === right.length && left.every((value, index) => value === right[index])
111
+ : left === right;
112
+ }
113
+ function isMissingFilesystemInput(error) {
114
+ const code = error.code;
115
+ return code === 'ENOENT' || code === 'ENOTDIR';
116
+ }
@@ -0,0 +1,2 @@
1
+ export { compilerHostProbesAreFresh, trackCompilerHost, type CompilerFilesystemHost, type CompilerHostProbeSnapshot, type CompilerHostTrackingOptions, } from './freshness.mts';
2
+ export { settleBuild, type SettlementCallbacks, type SettlementConfirmation, } from './settlement.mts';
@@ -0,0 +1,2 @@
1
+ export { compilerHostProbesAreFresh, trackCompilerHost, } from './freshness.mjs';
2
+ export { settleBuild, } from './settlement.mjs';