@deftai/directive-core 0.108.0 → 0.109.1

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 (157) hide show
  1. package/dist/cache/operations.js +1 -1
  2. package/dist/check/gate-lists.d.ts +14 -0
  3. package/dist/check/gate-lists.js +26 -3
  4. package/dist/check/session-completed-ac.d.ts +1 -1
  5. package/dist/check/session-completed-ac.js +1 -1
  6. package/dist/consumer-check-contract/evaluate.d.ts +47 -0
  7. package/dist/consumer-check-contract/evaluate.js +181 -15
  8. package/dist/delivery-attempt/evaluate.d.ts +9 -1
  9. package/dist/delivery-attempt/evaluate.js +69 -0
  10. package/dist/delivery-attempt/index.d.ts +1 -1
  11. package/dist/delivery-attempt/index.js +1 -1
  12. package/dist/deposit/live-procedure-exclusions.d.ts +18 -0
  13. package/dist/deposit/live-procedure-exclusions.js +110 -0
  14. package/dist/deposit/live-procedure-targets.d.ts +45 -0
  15. package/dist/deposit/live-procedure-targets.js +274 -0
  16. package/dist/deposit/python-free.d.ts +6 -0
  17. package/dist/deposit/python-free.js +15 -0
  18. package/dist/deposit/rewrite-deposit-links.d.ts +42 -0
  19. package/dist/deposit/rewrite-deposit-links.js +148 -0
  20. package/dist/deposit/run-stage-content-pack.d.ts +2 -0
  21. package/dist/deposit/run-stage-content-pack.js +3 -0
  22. package/dist/deposit/stage-content-pack.d.ts +17 -0
  23. package/dist/deposit/stage-content-pack.js +91 -0
  24. package/dist/design-critique/citation-grammar.d.ts +7 -0
  25. package/dist/design-critique/citation-grammar.js +1 -1
  26. package/dist/design-critique/completed-arc-record.d.ts +8 -1
  27. package/dist/design-critique/completed-arc-record.js +98 -14
  28. package/dist/hooks/classify/host-session-identity.d.ts +25 -12
  29. package/dist/hooks/classify/host-session-identity.js +64 -49
  30. package/dist/hooks/classify/index.d.ts +2 -2
  31. package/dist/hooks/classify/index.js +2 -2
  32. package/dist/hooks/classify/paths.d.ts +2 -0
  33. package/dist/hooks/classify/paths.js +8 -4
  34. package/dist/hooks/classify/stdin.js +69 -3
  35. package/dist/hooks/dest-form.d.ts +20 -1
  36. package/dist/hooks/dest-form.js +158 -21
  37. package/dist/hooks/dispatcher.d.ts +22 -2
  38. package/dist/hooks/dispatcher.js +338 -66
  39. package/dist/hooks/fixtures/cases.d.ts +6 -0
  40. package/dist/hooks/fixtures/cases.js +53 -0
  41. package/dist/hooks/git-destructive-log.d.ts +32 -0
  42. package/dist/hooks/git-destructive-log.js +46 -0
  43. package/dist/hooks/index.d.ts +3 -0
  44. package/dist/hooks/index.js +3 -0
  45. package/dist/hooks/owner-liveness.d.ts +92 -0
  46. package/dist/hooks/owner-liveness.js +103 -0
  47. package/dist/hooks/shell-write-targets.d.ts +10 -0
  48. package/dist/hooks/shell-write-targets.js +274 -0
  49. package/dist/hooks/tools.d.ts +49 -2
  50. package/dist/hooks/tools.js +96 -1
  51. package/dist/init-deposit/agent-hooks.d.ts +10 -0
  52. package/dist/init-deposit/agent-hooks.js +39 -0
  53. package/dist/init-deposit/gitignore.d.ts +7 -0
  54. package/dist/init-deposit/gitignore.js +24 -0
  55. package/dist/init-deposit/host-tool-coverage.d.ts +53 -0
  56. package/dist/init-deposit/host-tool-coverage.js +150 -0
  57. package/dist/init-deposit/index.d.ts +1 -0
  58. package/dist/init-deposit/index.js +1 -0
  59. package/dist/init-deposit/init-deposit.js +3 -0
  60. package/dist/init-deposit/refresh.js +7 -0
  61. package/dist/init-deposit/runtime-writers.d.ts +14 -0
  62. package/dist/init-deposit/runtime-writers.js +33 -0
  63. package/dist/intake/issue-ingest.d.ts +6 -1
  64. package/dist/intake/issue-ingest.js +21 -2
  65. package/dist/intake/reconcile-issues.js +13 -13
  66. package/dist/lifecycle/brief-envelope.d.ts +25 -0
  67. package/dist/lifecycle/brief-envelope.js +42 -0
  68. package/dist/lifecycle/index.d.ts +1 -0
  69. package/dist/lifecycle/index.js +1 -0
  70. package/dist/literal-acceptance/evaluate.js +14 -5
  71. package/dist/literal-acceptance/index.d.ts +1 -1
  72. package/dist/literal-acceptance/index.js +1 -1
  73. package/dist/literal-acceptance/run.d.ts +2 -0
  74. package/dist/literal-acceptance/run.js +19 -1
  75. package/dist/orchestration/subagent-monitor.d.ts +6 -0
  76. package/dist/orchestration/subagent-monitor.js +23 -1
  77. package/dist/orphan-active/candidate-scope.d.ts +53 -0
  78. package/dist/orphan-active/candidate-scope.js +157 -0
  79. package/dist/orphan-active/evaluate.d.ts +27 -0
  80. package/dist/orphan-active/evaluate.js +63 -6
  81. package/dist/orphan-active/index.d.ts +1 -0
  82. package/dist/orphan-active/index.js +1 -0
  83. package/dist/policy/merge-approval-head.js +7 -6
  84. package/dist/pr-closing-keywords/gh.js +32 -9
  85. package/dist/pr-closing-keywords/main.d.ts +1 -0
  86. package/dist/pr-closing-keywords/main.js +68 -3
  87. package/dist/pr-closing-keywords/types.d.ts +2 -0
  88. package/dist/pr-merge-readiness/gh.js +32 -9
  89. package/dist/pr-protected-issues/gh.js +36 -9
  90. package/dist/pr-wait-mergeable/wrappers.js +3 -3
  91. package/dist/product-first-done-gate/evaluate.js +6 -2
  92. package/dist/product-first-done-gate/types.js +2 -0
  93. package/dist/release/consumer-hard-stops.d.ts +48 -0
  94. package/dist/release/consumer-hard-stops.js +140 -0
  95. package/dist/release/consumer-readiness-disclosure.d.ts +18 -0
  96. package/dist/release/consumer-readiness-disclosure.js +51 -0
  97. package/dist/release/index.d.ts +2 -0
  98. package/dist/release/index.js +2 -0
  99. package/dist/release/pipeline.js +55 -0
  100. package/dist/release/run-consumer-readiness.d.ts +15 -0
  101. package/dist/release/run-consumer-readiness.js +24 -0
  102. package/dist/release/types.d.ts +6 -0
  103. package/dist/review-monitor/github-lease.js +4 -3
  104. package/dist/run-summary/types.d.ts +2 -2
  105. package/dist/scm/build-command.d.ts +2 -2
  106. package/dist/scm/build-command.js +2 -2
  107. package/dist/scm/call-shape.d.ts +25 -0
  108. package/dist/scm/call-shape.js +59 -0
  109. package/dist/scm/call.d.ts +7 -4
  110. package/dist/scm/call.js +41 -11
  111. package/dist/scm/gh-rest.d.ts +25 -12
  112. package/dist/scm/gh-rest.js +65 -15
  113. package/dist/scm/index.d.ts +2 -0
  114. package/dist/scm/index.js +2 -0
  115. package/dist/scm/spawn-status.d.ts +33 -0
  116. package/dist/scm/spawn-status.js +53 -0
  117. package/dist/scope/acceptance-evidence.d.ts +1 -1
  118. package/dist/scope/transition.js +4 -14
  119. package/dist/session/ac-pass-banking.d.ts +2 -2
  120. package/dist/session/ac-pass-banking.js +2 -2
  121. package/dist/session/child-occupancy.d.ts +72 -0
  122. package/dist/session/child-occupancy.js +209 -0
  123. package/dist/session/host-session-owner.d.ts +93 -0
  124. package/dist/session/host-session-owner.js +148 -0
  125. package/dist/session/index.d.ts +2 -0
  126. package/dist/session/index.js +2 -0
  127. package/dist/session/occupancy.d.ts +107 -2
  128. package/dist/session/occupancy.js +272 -34
  129. package/dist/session/verify-ac-session-cache.d.ts +2 -2
  130. package/dist/session/verify-ac-session-cache.js +2 -2
  131. package/dist/swarm/complete-cohort.js +2 -0
  132. package/dist/swarm/pre-dispatch.js +2 -0
  133. package/dist/swarm/subagent-status-dir.d.ts +2 -1
  134. package/dist/swarm/subagent-status-dir.js +11 -2
  135. package/dist/swarm/worktrees.js +2 -0
  136. package/dist/triage/evaluate/worktrees.js +153 -6
  137. package/dist/umbrella-current-shape/index.d.ts +51 -3
  138. package/dist/umbrella-current-shape/index.js +106 -18
  139. package/dist/validate-content/deposit-required.d.ts +39 -0
  140. package/dist/validate-content/deposit-required.js +147 -0
  141. package/dist/validate-content/index.d.ts +1 -0
  142. package/dist/validate-content/index.js +1 -0
  143. package/dist/validate-content/validate-links.d.ts +2 -3
  144. package/dist/validate-content/validate-links.js +29 -2
  145. package/dist/vbrief-activate/activate.d.ts +7 -2
  146. package/dist/vbrief-activate/activate.js +26 -13
  147. package/dist/verify-ac/evaluate.d.ts +9 -0
  148. package/dist/verify-ac/evaluate.js +32 -9
  149. package/dist/verify-env/agent-hooks.d.ts +6 -1
  150. package/dist/verify-env/agent-hooks.js +28 -2
  151. package/dist/verify-source/deposit-closure.d.ts +23 -0
  152. package/dist/verify-source/deposit-closure.js +162 -0
  153. package/dist/verify-source/index.d.ts +2 -0
  154. package/dist/verify-source/index.js +2 -0
  155. package/dist/verify-source/semantic-single-source.d.ts +36 -0
  156. package/dist/verify-source/semantic-single-source.js +349 -0
  157. package/package.json +3 -3
package/dist/scm/call.js CHANGED
@@ -1,27 +1,35 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { resolveCaptureFailureStderr, SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
3
- import { resolveBinary } from "./binary.js";
3
+ import { defaultWhich } from "./binary.js";
4
+ import { classifyScmArgv, resolveBinaryForRole } from "./call-shape.js";
4
5
  import { SUPPORTED_CALL_SOURCES } from "./constants.js";
5
6
  import { pyRepr } from "./py-format.js";
7
+ import { formatScmSpawnDiagnostic, isAvailabilitySpawnFailure } from "./spawn-status.js";
6
8
  /**
7
9
  * Source-aware SCM invocation -- partial down-payment on #445 / #935 Workstream 6.
8
10
  * Mirrors `scripts/scm.py::call`.
9
11
  *
10
- * Binary absence still throws ScmStubError via resolveBinary with the #2275
11
- * 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
12
14
  * (`scm/main`, issue-ingest, reconcile-issues) via requireScmReady — not on
13
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.
14
19
  */
15
20
  export function call(source, verb, args = null, options = {}) {
16
21
  if (!SUPPORTED_CALL_SOURCES.includes(source)) {
17
22
  throw new Error(`source=${pyRepr(source)} not yet supported; ` +
18
23
  "see #445 / #935 Workstream 6 for the abstraction.");
19
24
  }
20
- const resolved = options.binary ?? resolveBinary(options.whichFn);
21
- 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 ?? [];
22
30
  const captureOutput = options.captureOutput ?? true;
23
31
  const timeoutMs = options.timeout !== undefined ? Math.round(options.timeout * 1000) : undefined;
24
- const result = spawnSync(resolved, [verb, ...(args ?? [])], {
32
+ const spawnOnce = (bin) => spawnSync(bin, [verb, ...extra], {
25
33
  cwd: options.cwd,
26
34
  env: options.env ?? process.env,
27
35
  input: options.input,
@@ -30,14 +38,36 @@ export function call(source, verb, args = null, options = {}) {
30
38
  maxBuffer: SUBPROCESS_MAX_BUFFER,
31
39
  stdio: captureOutput ? ["pipe", "pipe", "pipe"] : "inherit",
32
40
  });
33
- // A spawn-level failure (ENOBUFS past maxBuffer, timeout kill) yields a null
34
- // status and empty stderr; surface error.message so callers never report a
35
- // bare exit 1 with no reason (#1867 / #3903).
36
- const stderr = resolveCaptureFailureStderr({
37
- captured: typeof result.stderr === "string" ? result.stderr : "",
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,
38
59
  status: result.status,
39
60
  message: result.error?.message,
40
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
+ }
41
71
  if (options.check && result.status !== 0) {
42
72
  const error = new Error(stderr || `Process exited with code ${result.status}`);
43
73
  throw error;
@@ -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>;
@@ -3,8 +3,10 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
6
- import { resolveBinary } from "./binary.js";
6
+ import { defaultWhich } from "./binary.js";
7
+ import { classifyScmArgv, resolveBinaryForRole } from "./call-shape.js";
7
8
  import { pyRepr } from "./py-format.js";
9
+ import { classifySpawnStatus, formatScmSpawnDiagnostic, isAvailabilitySpawnFailure, } from "./spawn-status.js";
8
10
  export const DEFAULT_TIMEOUT_S = 60;
9
11
  /** Raised when the `"owner/repo"` argument is malformed. */
10
12
  export class InvalidRepoError extends Error {
@@ -23,10 +25,16 @@ export class GhRestError extends Error {
23
25
  endpoint;
24
26
  payload;
25
27
  hint;
28
+ binary;
26
29
  constructor(options) {
27
30
  const hint = options.hint ?? "";
28
- let msg = `gh api failed: endpoint=${pyRepr(options.endpoint)} ` +
29
- `exit=${options.exitCode} stderr=${pyRepr(options.stderr)}`;
31
+ const binary = options.binary ?? "gh";
32
+ const statusClass = classifySpawnStatus(options.exitCode);
33
+ let msg = `${binary} api failed: endpoint=${pyRepr(options.endpoint)} ` + `exit=${options.exitCode}`;
34
+ if (statusClass !== `exit ${options.exitCode}`) {
35
+ msg += ` (${statusClass})`;
36
+ }
37
+ msg += ` stderr=${pyRepr(options.stderr)}`;
30
38
  if (hint.length > 0) {
31
39
  msg += `; hint: ${hint}`;
32
40
  }
@@ -37,30 +45,70 @@ export class GhRestError extends Error {
37
45
  this.endpoint = options.endpoint;
38
46
  this.payload = options.payload;
39
47
  this.hint = hint;
48
+ this.binary = binary;
49
+ }
50
+ }
51
+ function defaultGhSpawn(command, args, options) {
52
+ return spawnSync(command, [...args], options);
53
+ }
54
+ function finalizeGhApiResult(binary, result) {
55
+ const rawStderr = typeof result.stderr === "string" ? result.stderr : "";
56
+ let stderr = rawStderr;
57
+ if (result.status === null && result.error && stderr.trim().length === 0) {
58
+ stderr = result.error.message ?? "";
40
59
  }
60
+ if (isAvailabilitySpawnFailure({
61
+ status: result.status,
62
+ error: result.error,
63
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
64
+ stderr: rawStderr,
65
+ }) &&
66
+ stderr.trim().length === 0) {
67
+ stderr = formatScmSpawnDiagnostic(binary, result.status, stderr, result.error);
68
+ }
69
+ return {
70
+ returncode: result.status ?? 1,
71
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
72
+ stderr,
73
+ binary,
74
+ rawStderr,
75
+ rawStatus: result.status,
76
+ error: result.error,
77
+ };
41
78
  }
42
79
  /** Single subprocess seam invoked by every helper. */
43
80
  export function runGhApi(args, options = {}) {
44
- const binary = resolveBinary(options.whichFn);
81
+ const whichFn = options.whichFn ?? defaultWhich;
82
+ const spawnFn = options.spawnFn ?? defaultGhSpawn;
83
+ const role = classifyScmArgv("api", args);
84
+ let binary = resolveBinaryForRole(role, whichFn);
45
85
  const timeoutMs = options.timeout !== undefined ? Math.round(options.timeout * 1000) : DEFAULT_TIMEOUT_S * 1000;
46
- const result = spawnSync(binary, ["api", ...args], {
86
+ const spawnOpts = {
47
87
  encoding: "utf8",
48
88
  timeout: timeoutMs,
49
89
  env: process.env,
50
90
  maxBuffer: SUBPROCESS_MAX_BUFFER,
51
91
  stdio: ["ignore", "pipe", "pipe"],
52
- });
53
- let stderr = typeof result.stderr === "string" ? result.stderr : "";
54
- // A spawn-level failure (e.g. ENOBUFS when stdout exceeds maxBuffer) yields a
55
- // null status and empty stderr; surface error.message so the GhRestError that
56
- // wraps this never reports a blank reason (#1867).
57
- if (result.status === null && result.error && stderr.trim().length === 0) {
58
- stderr = result.error.message;
92
+ };
93
+ const spawnOnce = (bin) => finalizeGhApiResult(bin, spawnFn(bin, ["api", ...args], spawnOpts));
94
+ let first = spawnOnce(binary);
95
+ if (role === "cached-get" &&
96
+ binary === "ghx" &&
97
+ isAvailabilitySpawnFailure({
98
+ status: first.rawStatus,
99
+ error: first.error,
100
+ stdout: first.stdout,
101
+ stderr: first.rawStderr,
102
+ }) &&
103
+ whichFn("gh") !== null) {
104
+ binary = "gh";
105
+ first = spawnOnce(binary);
59
106
  }
60
107
  return {
61
- returncode: result.status ?? 1,
62
- stdout: typeof result.stdout === "string" ? result.stdout : "",
63
- stderr,
108
+ returncode: first.returncode,
109
+ stdout: first.stdout,
110
+ stderr: first.stderr,
111
+ binary,
64
112
  };
65
113
  }
66
114
  export function splitRepo(repo) {
@@ -83,6 +131,7 @@ function execApi(args, options) {
83
131
  endpoint: options.endpoint,
84
132
  payload: options.payload,
85
133
  hint: options.hint ?? "",
134
+ binary: result.binary ?? "gh",
86
135
  });
87
136
  }
88
137
  const stdout = result.stdout.trim();
@@ -393,6 +442,7 @@ export function restIssueListOpenInventory(repo, seams = {}) {
393
442
  endpoint,
394
443
  payload: null,
395
444
  hint: "verify gh auth and core REST quota; open-issue inventory must be complete",
445
+ binary: result.binary ?? "gh",
396
446
  });
397
447
  }
398
448
  const stdout = result.stdout.trim();
@@ -2,6 +2,7 @@ export * from "./argv.js";
2
2
  export * from "./binary.js";
3
3
  export * from "./build-command.js";
4
4
  export * from "./call.js";
5
+ export * from "./call-shape.js";
5
6
  export * from "./constants.js";
6
7
  export * from "./design-critique-chip.js";
7
8
  export * from "./errors.js";
@@ -10,4 +11,5 @@ export * from "./main.js";
10
11
  export * from "./py-format.js";
11
12
  export * from "./readiness.js";
12
13
  export * from "./rest-dispatch.js";
14
+ export * from "./spawn-status.js";
13
15
  //# sourceMappingURL=index.d.ts.map
package/dist/scm/index.js CHANGED
@@ -2,6 +2,7 @@ export * from "./argv.js";
2
2
  export * from "./binary.js";
3
3
  export * from "./build-command.js";
4
4
  export * from "./call.js";
5
+ export * from "./call-shape.js";
5
6
  export * from "./constants.js";
6
7
  export * from "./design-critique-chip.js";
7
8
  export * from "./errors.js";
@@ -10,4 +11,5 @@ export * from "./main.js";
10
11
  export * from "./py-format.js";
11
12
  export * from "./readiness.js";
12
13
  export * from "./rest-dispatch.js";
14
+ export * from "./spawn-status.js";
13
15
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,33 @@
1
+ import type { WhichFn } from "./binary.js";
2
+ import type { ScmBinaryRole } from "./call-shape.js";
3
+ /** Windows NTSTATUS STATUS_DLL_INIT_FAILED (#3737). Unsigned and signed 32-bit. */
4
+ export declare const STATUS_DLL_INIT_FAILED = 3221225794;
5
+ export declare function unsignedExitStatus(status: number): number;
6
+ export declare function classifySpawnStatus(status: number | null | undefined, error?: {
7
+ readonly message?: string;
8
+ readonly code?: string;
9
+ }): string;
10
+ export declare function isAvailabilitySpawnFailure(result: {
11
+ readonly status: number | null | undefined;
12
+ readonly error?: {
13
+ readonly message?: string;
14
+ readonly code?: string;
15
+ };
16
+ readonly stdout?: string;
17
+ readonly stderr?: string;
18
+ }): boolean;
19
+ export declare function formatScmSpawnDiagnostic(binary: string, status: number | null | undefined, stderr: string, error?: {
20
+ readonly message?: string;
21
+ readonly code?: string;
22
+ }): string;
23
+ /** Return `gh` when a cached-get ghx spawn failed and live gh is on PATH. */
24
+ export declare function ghxSpawnFallbackBinary(role: ScmBinaryRole, invoked: string, whichFn: WhichFn, failure: {
25
+ readonly status: number | null | undefined;
26
+ readonly error?: {
27
+ readonly message?: string;
28
+ readonly code?: string;
29
+ };
30
+ readonly stdout?: string;
31
+ readonly stderr?: string;
32
+ }): string | null;
33
+ //# sourceMappingURL=spawn-status.d.ts.map
@@ -0,0 +1,53 @@
1
+ /** Windows NTSTATUS STATUS_DLL_INIT_FAILED (#3737). Unsigned and signed 32-bit. */
2
+ export const STATUS_DLL_INIT_FAILED = 0xc0000142;
3
+ const STATUS_DLL_INIT_FAILED_SIGNED = -1073741502;
4
+ export function unsignedExitStatus(status) {
5
+ return status >>> 0;
6
+ }
7
+ export function classifySpawnStatus(status, error) {
8
+ if (typeof status !== "number") {
9
+ if (typeof error?.code === "string" && error.code.length > 0) {
10
+ return error.code;
11
+ }
12
+ if (typeof error?.message === "string" && error.message.trim().length > 0) {
13
+ return error.message.trim();
14
+ }
15
+ return "spawn-failed";
16
+ }
17
+ const unsigned = unsignedExitStatus(status);
18
+ if (unsigned === STATUS_DLL_INIT_FAILED || status === STATUS_DLL_INIT_FAILED_SIGNED) {
19
+ return "0xC0000142 STATUS_DLL_INIT_FAILED";
20
+ }
21
+ if (unsigned >= 0xc0000000) {
22
+ return `0x${unsigned.toString(16).toUpperCase()}`;
23
+ }
24
+ return `exit ${status}`;
25
+ }
26
+ export function isAvailabilitySpawnFailure(result) {
27
+ if (result.error !== undefined) {
28
+ return true;
29
+ }
30
+ if (typeof result.status !== "number") {
31
+ return true;
32
+ }
33
+ const unsigned = unsignedExitStatus(result.status);
34
+ const emptyOut = (result.stdout ?? "").trim().length === 0 && (result.stderr ?? "").trim().length === 0;
35
+ return (emptyOut &&
36
+ (unsigned === STATUS_DLL_INIT_FAILED || result.status === STATUS_DLL_INIT_FAILED_SIGNED));
37
+ }
38
+ export function formatScmSpawnDiagnostic(binary, status, stderr, error) {
39
+ const cls = classifySpawnStatus(status, error);
40
+ const err = stderr.trim();
41
+ if (err.length > 0) {
42
+ return `${binary} failed (${cls}): ${err}`;
43
+ }
44
+ return `${binary} failed (${cls}); stderr empty`;
45
+ }
46
+ /** Return `gh` when a cached-get ghx spawn failed and live gh is on PATH. */
47
+ export function ghxSpawnFallbackBinary(role, invoked, whichFn, failure) {
48
+ if (role !== "cached-get" || invoked !== "ghx" || whichFn("gh") === null) {
49
+ return null;
50
+ }
51
+ return isAvailabilitySpawnFailure(failure) ? "gh" : null;
52
+ }
53
+ //# sourceMappingURL=spawn-status.js.map
@@ -58,7 +58,7 @@ export interface AcceptanceEvidenceGateResult {
58
58
  readonly message: string;
59
59
  readonly reports: readonly CriterionAcceptanceReport[];
60
60
  /** How the #3357 walk obtained AC (#3387). */
61
- readonly servedFrom?: "bank" | "cache" | "executed";
61
+ readonly servedFrom?: "bank" | "cache" | "executed" | "refused";
62
62
  /** Which acceptance check decided the walk (#3497). Undefined when no walk ran. */
63
63
  readonly predicate?: AcceptancePredicate;
64
64
  }
@@ -5,6 +5,7 @@ import { InstrumentedVbriefCrud, persistCrudMetrics } from "../eval/crud-telemet
5
5
  import { assertProjectionContained, ProjectionContainmentError, } from "../fs/projection-containment.js";
6
6
  import { applyClauseDerivationToPlan, maybeEmitAcceptanceStampFromChange, } from "../intake/clause-derivation.js";
7
7
  import { hasArtifactSuffix } from "../layout/resolve.js";
8
+ import { stampExistingEnvelopes } from "../lifecycle/brief-envelope.js";
8
9
  import { evaluateCompletedPlanConsistency } from "../lifecycle/completed-consistency.js";
9
10
  import { evaluateLiteralAcceptanceFromPlan } from "../literal-acceptance/index.js";
10
11
  import { evaluateAcceptanceActivateGate } from "./acceptance-activate-gate.js";
@@ -45,18 +46,6 @@ function advanceNonTerminalOwnItems(items, targetStatus) {
45
46
  advanceNonTerminalOwnItems(obj.items, targetStatus);
46
47
  }
47
48
  }
48
- /**
49
- * Refresh the document envelope `updated` stamp to match `plan.updated`.
50
- * Stamps whichever of xBRIEFInfo (v0.8) / vBRIEFInfo (v0.6) is present — never creates one (#2862 / #2346).
51
- */
52
- function stampEnvelopeUpdated(data, nowIso) {
53
- for (const key of ["xBRIEFInfo", "vBRIEFInfo"]) {
54
- const env = data[key];
55
- if (typeof env === "object" && env !== null && !Array.isArray(env)) {
56
- env.updated = nowIso;
57
- }
58
- }
59
- }
60
49
  export function runTransition(action, filePath, now = new Date(), options = {}) {
61
50
  if (!(action in TRANSITIONS)) {
62
51
  const valid = Object.keys(TRANSITIONS).sort().join(", ");
@@ -255,7 +244,8 @@ export function runTransition(action, filePath, now = new Date(), options = {})
255
244
  planObj.status = targetStatus;
256
245
  planObj.updated = nowIso;
257
246
  // Keep the envelope clock aligned with plan.updated on every mutating transition (#2862).
258
- stampEnvelopeUpdated(data, nowIso);
247
+ // Stamps whichever envelope is present, never creates one (#3933 / #2346).
248
+ stampExistingEnvelopes(data, nowIso);
259
249
  // Reconcile the completing brief's own plan.items (mirrors #1527 / #2566 registry sync) (#2862).
260
250
  // On complete, items only reach here when #3240 evidence/disposition gate passed.
261
251
  if (OWN_ITEMS_RECONCILE_ACTIONS.has(act)) {
@@ -412,7 +402,7 @@ function restampCompletedBrief(args) {
412
402
  }
413
403
  planObj.status = "completed";
414
404
  planObj.updated = nowIso;
415
- stampEnvelopeUpdated(data, nowIso);
405
+ stampExistingEnvelopes(data, nowIso);
416
406
  stampCompletionMetadata(planObj, projectRoot, nowIso, {
417
407
  completedSessionId: resolveCompletionSessionId(projectRoot),
418
408
  });
@@ -12,8 +12,8 @@
12
12
  */
13
13
  import { type AcPassBankingConfig } from "../policy/ac-pass-banking.js";
14
14
  import type { HardEffortBudget, VerificationDepthPolicy } from "./effort-budget.js";
15
- /** Durable bank ledger directory (gitignored under `.deft/`). */
16
- export declare const AC_PASS_BANK_DIR = ".deft/ac-pass-banks";
15
+ /** Durable bank ledger directory (under already-ignored `.deft/cache/`). */
16
+ export declare const AC_PASS_BANK_DIR = ".deft/cache/ac-pass-banks";
17
17
  export declare const AC_PASS_BANK_SCHEMA_VERSION: 1;
18
18
  /** Env path for optional JSONL run summary (#3282). Unset = silent. */
19
19
  export declare const ENV_RUN_SUMMARY_PATH = "DEFT_RUN_SUMMARY_PATH";
@@ -18,8 +18,8 @@ import { DEFAULT_SURPLUS_THRESHOLD, resolveAcPassBanking, } from "../policy/ac-p
18
18
  import { RunSummaryEmitter } from "../run-summary/emit.js";
19
19
  import { detectHardEffortBudget, formatDeepeningSkippedNote, recommendVerificationDepth, } from "./effort-budget.js";
20
20
  import { gitHead } from "./git.js";
21
- /** Durable bank ledger directory (gitignored under `.deft/`). */
22
- export const AC_PASS_BANK_DIR = ".deft/ac-pass-banks";
21
+ /** Durable bank ledger directory (under already-ignored `.deft/cache/`). */
22
+ export const AC_PASS_BANK_DIR = ".deft/cache/ac-pass-banks";
23
23
  export const AC_PASS_BANK_SCHEMA_VERSION = 1;
24
24
  /** Env path for optional JSONL run summary (#3282). Unset = silent. */
25
25
  export const ENV_RUN_SUMMARY_PATH = "DEFT_RUN_SUMMARY_PATH";
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Dispatch-recorded child occupancy leases (#3999).
3
+ *
4
+ * A parent records the child's occupancy owner and the exact worktree root at
5
+ * dispatch in `.deft/child-occupancy/` — lease-gated, not `.deft-scratch/**`.
6
+ * The orchestration terminal transition already carries agent_id / parent_id /
7
+ * phase; this store is the missing occupancy-owner datum. Release reuses
8
+ * `releaseOccupancy` under the occupancy lock and only fires when the recorded
9
+ * child is still the current owner of the recorded tree.
10
+ *
11
+ * Per identity-source kind: `host-env` children are strangers and strand —
12
+ * that is the defect. `payload` parents share one id with their children, so
13
+ * the same transition is a no-op; auto-release would drop a live parent lease
14
+ * mid-flight. Swarm close-out of the launcher's occupancy_session_id is not
15
+ * the precedent and is not copied here.
16
+ */
17
+ import type { LockDeps } from "../slice/lock.js";
18
+ import { type OccupancyDecision } from "./occupancy.js";
19
+ export declare const CHILD_OCCUPANCY_SCHEMA_VERSION = 1;
20
+ export declare const CHILD_OCCUPANCY_DIR: readonly [".deft", "child-occupancy"];
21
+ export declare const CHILD_OCCUPANCY_IDENTITY_SOURCE_KINDS: readonly ["host-env", "payload"];
22
+ export type ChildOccupancyIdentitySourceKind = (typeof CHILD_OCCUPANCY_IDENTITY_SOURCE_KINDS)[number];
23
+ export interface ChildOccupancyRecord {
24
+ readonly schemaVersion: number;
25
+ readonly agentId: string;
26
+ readonly parentId: string;
27
+ readonly occupancyOwner: string;
28
+ readonly worktreePath: string;
29
+ readonly identitySourceKind: ChildOccupancyIdentitySourceKind;
30
+ }
31
+ export interface ChildOccupancyDispatchInput {
32
+ readonly agentId: string;
33
+ readonly parentId: string;
34
+ readonly occupancyOwner: string;
35
+ readonly worktreePath: string;
36
+ readonly identitySourceKind: ChildOccupancyIdentitySourceKind;
37
+ }
38
+ export type ChildOccupancyReleaseReason = "released" | "already-free" | "owner-changed" | "payload-skip" | "missing-record" | "denied";
39
+ export interface ChildOccupancyReleaseResult {
40
+ readonly reason: ChildOccupancyReleaseReason;
41
+ readonly record: ChildOccupancyRecord | null;
42
+ readonly occupancy: OccupancyDecision | null;
43
+ }
44
+ /** Filename-safe agent id; the payload keeps the original. */
45
+ export declare function childOccupancyFileSegment(agentId: string): string;
46
+ export declare function childOccupancyRelpath(agentId: string): string[];
47
+ export declare function childOccupancyPath(storeRoot: string, agentId: string): string;
48
+ export declare function childOccupancyIdentitySourceKind(host: string): ChildOccupancyIdentitySourceKind | null;
49
+ export declare function readChildOccupancyLease(storeRoot: string, agentId: string): ChildOccupancyRecord | null;
50
+ /**
51
+ * Parent-only write at dispatch. Workers cannot author this store: `.deft/` is
52
+ * not assist-scratch, so a mutation write is occupancy-gated and an assist
53
+ * writer does not get the scratch carve-out.
54
+ */
55
+ export declare function recordChildOccupancyLease(storeRoot: string, input: ChildOccupancyDispatchInput): ChildOccupancyRecord;
56
+ /**
57
+ * Compare-and-release under the occupancy lock. Caller identity is the id the
58
+ * parent recorded at dispatch — not the occupant currently named in the lease
59
+ * file, and not a field on a worker-authored heartbeat.
60
+ */
61
+ export declare function releaseChildOccupancyOnTerminal(storeRoot: string, input: {
62
+ readonly agentId: string;
63
+ readonly now?: Date;
64
+ readonly lockDeps?: LockDeps;
65
+ }): ChildOccupancyReleaseResult;
66
+ /**
67
+ * Worktree guesses for a heartbeat file. Canonical layout is
68
+ * `<worktree>/.deft-scratch/subagent-status/<agent>.json`; cwd is the fallback
69
+ * when the scratch dir was passed as a custom path.
70
+ */
71
+ export declare function worktreeCandidatesForHeartbeat(heartbeatPath: string, cwd: string): readonly string[];
72
+ //# sourceMappingURL=child-occupancy.d.ts.map