@deftai/directive-core 0.105.0 → 0.107.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 (173) hide show
  1. package/dist/authz/classify.js +591 -34
  2. package/dist/check/gate-lists.js +2 -0
  3. package/dist/check/named-cause.js +1 -1
  4. package/dist/design-critique/exclusive-chip.d.ts +33 -0
  5. package/dist/design-critique/exclusive-chip.js +68 -0
  6. package/dist/design-critique/parent-audit.d.ts +59 -0
  7. package/dist/design-critique/parent-audit.js +121 -0
  8. package/dist/doctor/checks.d.ts +6 -0
  9. package/dist/doctor/checks.js +35 -0
  10. package/dist/forward-coverage/diff-coverage.d.ts +39 -0
  11. package/dist/forward-coverage/diff-coverage.js +112 -0
  12. package/dist/forward-coverage/diff-lines.d.ts +19 -0
  13. package/dist/forward-coverage/diff-lines.js +173 -0
  14. package/dist/forward-coverage/evaluate.d.ts +24 -9
  15. package/dist/forward-coverage/evaluate.js +125 -19
  16. package/dist/hooks/dest-form.d.ts +46 -0
  17. package/dist/hooks/dest-form.js +575 -0
  18. package/dist/hooks/dispatcher.d.ts +17 -1
  19. package/dist/hooks/dispatcher.js +82 -11
  20. package/dist/hooks/index.d.ts +1 -0
  21. package/dist/hooks/index.js +1 -0
  22. package/dist/init-deposit/agent-hooks.d.ts +1 -1
  23. package/dist/init-deposit/agent-hooks.js +1 -1
  24. package/dist/intake/clause-derivation.d.ts +9 -0
  25. package/dist/intake/clause-derivation.js +30 -3
  26. package/dist/intake/github-auth-modes-cli.js +14 -2
  27. package/dist/intake/github-auth-modes.d.ts +54 -9
  28. package/dist/intake/github-auth-modes.js +326 -123
  29. package/dist/lifecycle/completed-tracked-on-delivery.d.ts +26 -0
  30. package/dist/lifecycle/completed-tracked-on-delivery.js +37 -14
  31. package/dist/lifecycle/completed-write-guard.d.ts +48 -0
  32. package/dist/lifecycle/completed-write-guard.js +373 -0
  33. package/dist/lifecycle/index.d.ts +1 -0
  34. package/dist/lifecycle/index.js +1 -0
  35. package/dist/lifecycle-visible/evaluate.d.ts +98 -0
  36. package/dist/lifecycle-visible/evaluate.js +710 -0
  37. package/dist/lifecycle-visible/index.d.ts +2 -0
  38. package/dist/lifecycle-visible/index.js +2 -0
  39. package/dist/literal-acceptance/capture.d.ts +16 -0
  40. package/dist/literal-acceptance/capture.js +78 -10
  41. package/dist/literal-acceptance/evaluate.js +25 -3
  42. package/dist/literal-acceptance/index.d.ts +2 -2
  43. package/dist/literal-acceptance/index.js +2 -2
  44. package/dist/literal-acceptance/run.js +9 -3
  45. package/dist/literal-acceptance/types.d.ts +6 -0
  46. package/dist/literal-acceptance/types.js +6 -0
  47. package/dist/orchestration/judgment-policy.d.ts +13 -0
  48. package/dist/orchestration/judgment-policy.js +15 -0
  49. package/dist/orchestration/probe-session.js +13 -8
  50. package/dist/orchestration/verify-judgment-gates.d.ts +4 -0
  51. package/dist/orchestration/verify-judgment-gates.js +43 -11
  52. package/dist/policy/ceremony-dial.js +7 -7
  53. package/dist/policy/host-hooks.d.ts +20 -0
  54. package/dist/policy/host-hooks.js +121 -2
  55. package/dist/policy/org-force-on-migration.js +2 -1
  56. package/dist/policy/plan-extensions.d.ts +1 -1
  57. package/dist/policy/plan-extensions.js +2 -0
  58. package/dist/policy/product-signal.js +6 -7
  59. package/dist/policy/require-human-merge.js +8 -6
  60. package/dist/policy/resolve.d.ts +7 -2
  61. package/dist/policy/resolve.js +25 -8
  62. package/dist/policy/runtime-authority.d.ts +18 -0
  63. package/dist/policy/runtime-authority.js +11 -0
  64. package/dist/policy/value-feedback.js +7 -8
  65. package/dist/policy/write-fence.js +3 -0
  66. package/dist/preflight-cache/evaluate.d.ts +12 -0
  67. package/dist/preflight-cache/evaluate.js +24 -4
  68. package/dist/preflight-cache/index.d.ts +1 -1
  69. package/dist/preflight-cache/index.js +1 -1
  70. package/dist/product-first-done-gate/evaluate.d.ts +4 -0
  71. package/dist/product-first-done-gate/evaluate.js +82 -10
  72. package/dist/release/closed-verb-gate.d.ts +12 -0
  73. package/dist/release/closed-verb-gate.js +51 -0
  74. package/dist/release/index.d.ts +1 -0
  75. package/dist/release/index.js +1 -0
  76. package/dist/release/pipeline.js +7 -0
  77. package/dist/release/types.d.ts +12 -0
  78. package/dist/render/constants.d.ts +2 -1
  79. package/dist/render/constants.js +2 -1
  80. package/dist/render/framework-commands.js +4 -0
  81. package/dist/render/spec-validate.js +23 -12
  82. package/dist/run-summary/types.d.ts +4 -0
  83. package/dist/scm/design-critique-chip.d.ts +37 -0
  84. package/dist/scm/design-critique-chip.js +171 -0
  85. package/dist/scm/index.d.ts +1 -0
  86. package/dist/scm/index.js +1 -0
  87. package/dist/scm/main.d.ts +3 -0
  88. package/dist/scm/main.js +15 -1
  89. package/dist/scm/readiness-cli.d.ts +2 -0
  90. package/dist/scm/readiness-cli.js +42 -1
  91. package/dist/scm/readiness.d.ts +2 -1
  92. package/dist/scm/readiness.js +7 -1
  93. package/dist/scope/decompose.js +2 -1
  94. package/dist/scope/delivery-evidence.d.ts +27 -1
  95. package/dist/scope/index.d.ts +1 -0
  96. package/dist/scope/index.js +1 -0
  97. package/dist/scope/lifecycle-write.d.ts +24 -0
  98. package/dist/scope/lifecycle-write.js +64 -0
  99. package/dist/scope/main.js +18 -6
  100. package/dist/scope/promote-from-issue.js +5 -5
  101. package/dist/scope/transition.js +90 -11
  102. package/dist/scope-provenance/evaluate.js +8 -4
  103. package/dist/session/ac-pass-banking.d.ts +5 -0
  104. package/dist/session/ac-pass-banking.js +3 -2
  105. package/dist/session/git.d.ts +18 -0
  106. package/dist/session/git.js +81 -1
  107. package/dist/session/occupancy.d.ts +2 -1
  108. package/dist/session/occupancy.js +11 -4
  109. package/dist/session/process-cost.d.ts +32 -10
  110. package/dist/session/process-cost.js +189 -1
  111. package/dist/session/product-state-hash.js +41 -24
  112. package/dist/session/ritual-entrypoint.js +16 -1
  113. package/dist/session/session-start.d.ts +18 -0
  114. package/dist/session/session-start.js +49 -2
  115. package/dist/session/verify-session-ritual.d.ts +46 -4
  116. package/dist/session/verify-session-ritual.js +118 -10
  117. package/dist/swarm/finalize-cohort-cli.d.ts +7 -2
  118. package/dist/swarm/finalize-cohort-cli.js +165 -35
  119. package/dist/swarm/finalize-cohort.d.ts +1 -0
  120. package/dist/swarm/finalize-cohort.js +14 -2
  121. package/dist/swarm/launch.d.ts +69 -0
  122. package/dist/swarm/launch.js +191 -1
  123. package/dist/swarm/routing-set-cli.js +2 -0
  124. package/dist/swarm/routing-verify.d.ts +1 -1
  125. package/dist/swarm/routing-verify.js +2 -2
  126. package/dist/triage/actions/index.d.ts +4 -0
  127. package/dist/triage/actions/index.js +6 -0
  128. package/dist/triage/bootstrap/index.js +6 -6
  129. package/dist/triage/evaluate/evaluate.d.ts +11 -0
  130. package/dist/triage/evaluate/evaluate.js +169 -0
  131. package/dist/triage/evaluate/github.d.ts +16 -0
  132. package/dist/triage/evaluate/github.js +153 -0
  133. package/dist/triage/evaluate/index.d.ts +9 -0
  134. package/dist/triage/evaluate/index.js +8 -0
  135. package/dist/triage/evaluate/paths.d.ts +7 -0
  136. package/dist/triage/evaluate/paths.js +25 -0
  137. package/dist/triage/evaluate/sink.d.ts +5 -0
  138. package/dist/triage/evaluate/sink.js +52 -0
  139. package/dist/triage/evaluate/types.d.ts +93 -0
  140. package/dist/triage/evaluate/types.js +13 -0
  141. package/dist/triage/evaluate/validity.d.ts +8 -0
  142. package/dist/triage/evaluate/validity.js +87 -0
  143. package/dist/triage/evaluate/value.d.ts +12 -0
  144. package/dist/triage/evaluate/value.js +33 -0
  145. package/dist/triage/evaluate/wip-census.d.ts +5 -0
  146. package/dist/triage/evaluate/wip-census.js +39 -0
  147. package/dist/triage/evaluate/worktrees.d.ts +8 -0
  148. package/dist/triage/evaluate/worktrees.js +68 -0
  149. package/dist/triage/evaluate/xbrief-refs.d.ts +8 -0
  150. package/dist/triage/evaluate/xbrief-refs.js +60 -0
  151. package/dist/triage/help/registry-data.d.ts +22 -8
  152. package/dist/triage/help/registry-data.js +54 -6
  153. package/dist/triage/index.d.ts +1 -0
  154. package/dist/triage/index.js +1 -0
  155. package/dist/triage/welcome/writers.d.ts +1 -1
  156. package/dist/triage/welcome/writers.js +10 -5
  157. package/dist/value/feedback-file.d.ts +17 -2
  158. package/dist/value/feedback-file.js +80 -6
  159. package/dist/value/readback.d.ts +4 -1
  160. package/dist/value/readback.js +51 -12
  161. package/dist/vbrief-reconcile/labels.d.ts +1 -0
  162. package/dist/vbrief-reconcile/labels.js +30 -0
  163. package/dist/vbrief-validate/schema.d.ts +4 -0
  164. package/dist/vbrief-validate/schema.js +1 -1
  165. package/dist/vbrief-validation/story-quality.d.ts +7 -0
  166. package/dist/vbrief-validation/story-quality.js +8 -1
  167. package/dist/verify-env/agent-hook-readiness.d.ts +2 -2
  168. package/dist/verify-env/agent-hook-readiness.js +12 -8
  169. package/dist/verify-env/agent-hooks-live-probe.d.ts +5 -1
  170. package/dist/verify-env/agent-hooks-live-probe.js +26 -5
  171. package/dist/verify-env/agent-hooks.js +3 -4
  172. package/dist/xbrief-migrate/agents-header.js +69 -7
  173. package/package.json +7 -3
@@ -1,5 +1,5 @@
1
1
  import { type RoutingFile } from "./routing.js";
2
- export declare const ROUTING_SET_CMD = "task swarm:routing-set -- --role <role> --model <slug> (or --harness-default)";
2
+ export declare const ROUTING_SET_CMD = "deft swarm:routing-set --role <role> --model <slug> (or --harness-default)";
3
3
  /** Roles the pre-dispatch gate checks by default: the actual model lever. */
4
4
  export declare const DEFAULT_GATED_ROLES: readonly ["leaf-implementation"];
5
5
  export interface VerifyRoutingOptions {
@@ -12,7 +12,7 @@
12
12
  */
13
13
  import { EXIT_CONFIG_ERROR, EXIT_GATE_FAILED, EXIT_OK } from "./constants.js";
14
14
  import { dispatchProviderFromRuntime, emptyHostDetectProbes, HARNESS_BOUND_PROVIDERS, loadRoutingFile, ROUTING_MODE_HARNESS_DEFAULT, resolveDispatchProvider, resolveModelRoute, resolveRoutingPath, } from "./routing.js";
15
- export const ROUTING_SET_CMD = "task swarm:routing-set -- --role <role> --model <slug> (or --harness-default)";
15
+ export const ROUTING_SET_CMD = "deft swarm:routing-set --role <role> --model <slug> (or --harness-default)";
16
16
  /** Roles the pre-dispatch gate checks by default: the actual model lever. */
17
17
  export const DEFAULT_GATED_ROLES = ["leaf-implementation"];
18
18
  function resolveProvider(options) {
@@ -105,7 +105,7 @@ export function verifyRouting(options) {
105
105
  };
106
106
  }
107
107
  if (options.advise) {
108
- const note = "[deft routing] NOTE: plan.policy.swarmSubagentBackend enum is deprecated (#1891); use 'task swarm:routing-set' / .deft/routing.local.json instead.";
108
+ const note = "[deft routing] NOTE: plan.policy.swarmSubagentBackend enum is deprecated (#1891); use 'deft swarm:routing-set' / .deft/routing.local.json instead.";
109
109
  let body;
110
110
  if (undecided.length === 0 && invalid.length === 0) {
111
111
  body = `[deft routing] provider '${provider}': all ${roles.length} gated role(s) decided.`;
@@ -38,6 +38,10 @@ export declare function reset(issueNumber: number, repo: string, deps: TriageAct
38
38
  actor?: string | null;
39
39
  projectRoot?: string;
40
40
  }): string;
41
+ /** Success line for ``triage:accept``, plus the optional design-critique affordance (#3708). */
42
+ export declare function formatAcceptOutput(issueNumber: number, repo: string, decisionId: string, options?: {
43
+ autoPromote?: boolean;
44
+ }): string;
41
45
  /** Format a decision entry for CLI ``status`` / ``history`` output. */
42
46
  export declare function formatDecision(entry: AuditEntry | null): string;
43
47
  //# sourceMappingURL=index.d.ts.map
@@ -407,6 +407,12 @@ export function reset(issueNumber, repo, deps, options = {}) {
407
407
  });
408
408
  return deps.candidatesLog.append(entry, { path: logPath });
409
409
  }
410
+ /** Success line for ``triage:accept``, plus the optional design-critique affordance (#3708). */
411
+ export function formatAcceptOutput(issueNumber, repo, decisionId, options = {}) {
412
+ const promoteNote = options.autoPromote === true ? " + auto-promote" : "";
413
+ return (`accept #${issueNumber} (${repo}) -> ${decisionId}${promoteNote}\n` +
414
+ "optional: deft-directive-design-critique (declining writes nothing)");
415
+ }
410
416
  /** Format a decision entry for CLI ``status`` / ``history`` output. */
411
417
  export function formatDecision(entry) {
412
418
  if (entry === null) {
@@ -370,12 +370,12 @@ export function formatSummary(result) {
370
370
  if (result.exitCode === 0) {
371
371
  lines.push("");
372
372
  lines.push("Next steps:");
373
- lines.push(" task cache:fetch-all -- --source=github-issue --repo OWNER/NAME # refresh the cache (#883 Story 2)");
374
- lines.push(" task cache:get -- github-issue OWNER/NAME/<N> # inspect cached issue N");
375
- lines.push(" task triage:accept -- --issue <N> --repo OWNER/NAME # accept issue N (#845 Story 3)");
376
- lines.push(" task triage:reject -- --issue <N> --repo OWNER/NAME --reason 'why' # reject issue N");
377
- lines.push(" task triage:bulk-accept -- --repo OWNER/NAME --label adoption-blocker # bulk accept");
378
- lines.push(" task triage:refresh-active # pre-swarm freshness gate");
373
+ lines.push(" deft cache:fetch-all --source=github-issue --repo OWNER/NAME # refresh the cache (#883 Story 2)");
374
+ lines.push(" deft cache:get github-issue OWNER/NAME/<N> # inspect cached issue N");
375
+ lines.push(" deft triage:accept --issue <N> --repo OWNER/NAME # accept issue N (#845 Story 3)");
376
+ lines.push(" deft triage:reject --issue <N> --repo OWNER/NAME --reason 'why' # reject issue N");
377
+ lines.push(" deft triage:bulk-accept --repo OWNER/NAME --label adoption-blocker # bulk accept");
378
+ lines.push(" deft triage:refresh-active # pre-swarm freshness gate");
379
379
  }
380
380
  return lines.join("\n");
381
381
  }
@@ -0,0 +1,11 @@
1
+ import { type EvaluateOptions, type EvaluateResult } from "./types.js";
2
+ export declare class EvaluateError extends Error {
3
+ name: string;
4
+ }
5
+ /**
6
+ * Stage A parent: WIP census + GitHub REST + worktree fan-out + sink write.
7
+ * Evaluators never receive the WIP census.
8
+ */
9
+ export declare function evaluateIssues(options: EvaluateOptions): Promise<EvaluateResult>;
10
+ export declare function renderEvaluateText(result: EvaluateResult): string;
11
+ //# sourceMappingURL=evaluate.d.ts.map
@@ -0,0 +1,169 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { runText } from "../../swarm/subprocess.js";
3
+ import { defaultGitRunner } from "../../swarm/worktrees.js";
4
+ import { defaultGithubReader, pullsMentioning } from "./github.js";
5
+ import { sha12Of, sinkDir } from "./paths.js";
6
+ import { gcStaleSha12Dirs, writeInvocationSink } from "./sink.js";
7
+ import { DEFAULT_CONCURRENCY, ORIGIN_MASTER, } from "./types.js";
8
+ import { evaluateValidity, joinValidityWithGithub } from "./validity.js";
9
+ import { buildValueAdvice } from "./value.js";
10
+ import { collectWipCensus, wipHitsForIssue } from "./wip-census.js";
11
+ import { addEvaluatorWorktree, removeEvaluatorWorktree } from "./worktrees.js";
12
+ export class EvaluateError extends Error {
13
+ name = "EvaluateError";
14
+ }
15
+ function resolveOriginSha(projectRoot, git) {
16
+ const proc = git(["rev-parse", ORIGIN_MASTER], projectRoot);
17
+ if (proc.returncode !== 0) {
18
+ throw new EvaluateError(`could not resolve ${ORIGIN_MASTER}: ${proc.stderr.trim() || "<no stderr>"}`);
19
+ }
20
+ return proc.stdout.trim();
21
+ }
22
+ function defaultSessionStart(worktreePath) {
23
+ const proc = runText(["deft", "session:start", "--read-only"], { cwd: worktreePath });
24
+ if (proc.returncode !== 0) {
25
+ throw new EvaluateError(`session:start --read-only failed in ${worktreePath}: ${proc.stderr.trim() || "<no stderr>"}`);
26
+ }
27
+ }
28
+ async function mapPool(items, limit, fn) {
29
+ const results = new Array(items.length);
30
+ let next = 0;
31
+ const worker = async () => {
32
+ while (true) {
33
+ const index = next;
34
+ next += 1;
35
+ if (index >= items.length) {
36
+ return;
37
+ }
38
+ results[index] = await fn(items[index], index);
39
+ }
40
+ };
41
+ const n = Math.max(1, Math.min(limit, Math.max(items.length, 1)));
42
+ await Promise.all(Array.from({ length: n }, () => worker()));
43
+ return results;
44
+ }
45
+ function collectGithubForRepo(repo, issues, reader) {
46
+ const views = issues.map((n) => reader.viewIssue(repo, n));
47
+ const openPulls = reader.listOpenPulls(repo);
48
+ reader.listOpenIssues(repo);
49
+ return { issues: views, openPulls };
50
+ }
51
+ /**
52
+ * Stage A parent: WIP census + GitHub REST + worktree fan-out + sink write.
53
+ * Evaluators never receive the WIP census.
54
+ */
55
+ export async function evaluateIssues(options) {
56
+ const issues = [...new Set(options.issues)].filter((n) => Number.isInteger(n) && n > 0);
57
+ if (issues.length === 0) {
58
+ throw new EvaluateError("triage:evaluate requires one or more issue numbers");
59
+ }
60
+ const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
61
+ if (!Number.isInteger(concurrency) || concurrency < 1) {
62
+ throw new EvaluateError("--concurrency must be a positive integer");
63
+ }
64
+ const git = options.git ?? defaultGitRunner;
65
+ const sessionStart = options.sessionStart ?? defaultSessionStart;
66
+ const github = options.github ?? defaultGithubReader();
67
+ const originSha = resolveOriginSha(options.projectRoot, git);
68
+ const sha12 = sha12Of(originSha);
69
+ const invocationId = options.invocationId ?? randomUUID();
70
+ const wip = collectWipCensus(options.projectRoot, issues);
71
+ const githubViews = collectGithubForRepo(options.repo, issues, github);
72
+ const viewByIssue = new Map(githubViews.issues.map((snap) => [snap.number, snap]));
73
+ const runOne = async (issue) => {
74
+ let worktreePath = null;
75
+ let teardownError = null;
76
+ const snap = viewByIssue.get(issue) ?? null;
77
+ const pulls = pullsMentioning(githubViews.openPulls, issue);
78
+ let verdict;
79
+ try {
80
+ worktreePath = addEvaluatorWorktree(options.projectRoot, issue, invocationId, originSha, git);
81
+ sessionStart(worktreePath);
82
+ const validity = evaluateValidity(worktreePath, issue);
83
+ const joined = joinValidityWithGithub(validity, snap?.state ?? null);
84
+ const duplicates = snap?.duplicateOf !== null && snap?.duplicateOf !== undefined ? [snap.duplicateOf] : [];
85
+ verdict = {
86
+ issue,
87
+ sha12,
88
+ invocationId,
89
+ validity: joined,
90
+ wip: wipHitsForIssue(wip, issue),
91
+ github: snap,
92
+ openPulls: pulls,
93
+ duplicates,
94
+ value: buildValueAdvice(snap),
95
+ error: null,
96
+ };
97
+ }
98
+ catch (err) {
99
+ const message = err instanceof Error ? err.message : String(err);
100
+ verdict = {
101
+ issue,
102
+ sha12,
103
+ invocationId,
104
+ validity: null,
105
+ wip: wipHitsForIssue(wip, issue),
106
+ github: snap,
107
+ openPulls: pulls,
108
+ duplicates: [],
109
+ value: buildValueAdvice(snap),
110
+ error: message,
111
+ };
112
+ }
113
+ finally {
114
+ if (worktreePath !== null) {
115
+ try {
116
+ removeEvaluatorWorktree(options.projectRoot, worktreePath, git);
117
+ }
118
+ catch (teardownErr) {
119
+ teardownError = teardownErr instanceof Error ? teardownErr.message : String(teardownErr);
120
+ }
121
+ }
122
+ }
123
+ if (teardownError !== null) {
124
+ verdict = {
125
+ ...verdict,
126
+ error: verdict.error !== null ? `${verdict.error}; teardown: ${teardownError}` : teardownError,
127
+ };
128
+ }
129
+ return verdict;
130
+ };
131
+ try {
132
+ const verdicts = await mapPool(issues, concurrency, (issue) => runOne(issue));
133
+ const result = {
134
+ sha12,
135
+ invocationId,
136
+ originSha,
137
+ sinkDir: sinkDir(options.projectRoot, sha12, invocationId),
138
+ concurrency,
139
+ verdicts,
140
+ };
141
+ writeInvocationSink(options.projectRoot, result);
142
+ gcStaleSha12Dirs(options.projectRoot, sha12);
143
+ return result;
144
+ }
145
+ catch (err) {
146
+ gcStaleSha12Dirs(options.projectRoot, sha12);
147
+ throw err;
148
+ }
149
+ }
150
+ export function renderEvaluateText(result) {
151
+ const lines = [
152
+ `triage:evaluate sha12=${result.sha12} invocation=${result.invocationId} concurrency=${result.concurrency}`,
153
+ `sink: ${result.sinkDir}`,
154
+ ];
155
+ for (const verdict of result.verdicts) {
156
+ const state = verdict.validity?.state ?? "error";
157
+ const evidence = verdict.validity?.evidence ?? verdict.error ?? "";
158
+ const wip = verdict.wip.length > 0 ? ` wip=${verdict.wip.length}` : " wip=0";
159
+ lines.push(`#${verdict.issue} validity=${state}${wip} critique-recommend: ${verdict.value["critique-recommend"]}`);
160
+ if (evidence.length > 0) {
161
+ lines.push(` ${evidence}`);
162
+ }
163
+ if (verdict.error !== null) {
164
+ lines.push(` error: ${verdict.error}`);
165
+ }
166
+ }
167
+ return `${lines.join("\n")}\n`;
168
+ }
169
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1,16 @@
1
+ import { type RunGhApiFn } from "../../scm/gh-rest.js";
2
+ import type { GithubCensus, GithubIssueSnapshot, GithubPullSnapshot } from "./types.js";
3
+ export declare function snapshotFromIssuePayload(raw: Record<string, unknown>): GithubIssueSnapshot;
4
+ export declare function snapshotFromPullPayload(raw: Record<string, unknown>): GithubPullSnapshot;
5
+ export interface GithubSeams {
6
+ readonly runGhApiFn?: RunGhApiFn;
7
+ }
8
+ /** GET-only GitHub census. Parent-owned. Never POST/PATCH/PUT/DELETE. */
9
+ export declare function collectGithubCensus(repo: string, issues: readonly number[], seams?: GithubSeams): GithubCensus;
10
+ export declare function defaultGithubReader(seams?: GithubSeams): {
11
+ viewIssue: (repo: string, n: number) => GithubIssueSnapshot;
12
+ listOpenIssues: (repo: string) => GithubIssueSnapshot[];
13
+ listOpenPulls: (repo: string) => GithubPullSnapshot[];
14
+ };
15
+ export declare function pullsMentioning(pulls: readonly GithubPullSnapshot[], issue: number): GithubPullSnapshot[];
16
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1,153 @@
1
+ import { GhRestError, restIssueListPaginated, restIssueView, runGhApi, splitRepo, } from "../../scm/gh-rest.js";
2
+ const DUPLICATE_RE = /duplicate of\s+#(\d+)/iu;
3
+ const MENTION_RE = /(?:#|issues\/)(\d+)\b/gu;
4
+ function labelsOf(raw) {
5
+ const labels = raw.labels;
6
+ if (!Array.isArray(labels)) {
7
+ return [];
8
+ }
9
+ const out = [];
10
+ for (const item of labels) {
11
+ if (typeof item === "string") {
12
+ out.push(item);
13
+ continue;
14
+ }
15
+ if (item !== null && typeof item === "object" && "name" in item) {
16
+ const name = item.name;
17
+ if (typeof name === "string") {
18
+ out.push(name);
19
+ }
20
+ }
21
+ }
22
+ return out;
23
+ }
24
+ function duplicateOf(title, body, labels) {
25
+ const match = DUPLICATE_RE.exec(`${title}\n${body}`);
26
+ if (match?.[1] !== undefined) {
27
+ return Number.parseInt(match[1], 10);
28
+ }
29
+ if (labels.some((label) => label.toLowerCase().includes("duplicate"))) {
30
+ const hash = /#(\d+)/u.exec(title);
31
+ if (hash?.[1] !== undefined) {
32
+ return Number.parseInt(hash[1], 10);
33
+ }
34
+ }
35
+ return null;
36
+ }
37
+ export function snapshotFromIssuePayload(raw) {
38
+ const number = typeof raw.number === "number" ? raw.number : Number.parseInt(String(raw.number), 10);
39
+ const state = raw.state === "closed" ? "closed" : "open";
40
+ const title = typeof raw.title === "string" ? raw.title : "";
41
+ const body = typeof raw.body === "string" ? raw.body : "";
42
+ const labels = labelsOf(raw);
43
+ const htmlUrl = typeof raw.html_url === "string" ? raw.html_url : null;
44
+ return {
45
+ number,
46
+ state,
47
+ title,
48
+ body,
49
+ labels,
50
+ htmlUrl,
51
+ pullRequest: raw.pull_request !== undefined && raw.pull_request !== null,
52
+ duplicateOf: duplicateOf(title, body, labels),
53
+ };
54
+ }
55
+ function mentionsFrom(text) {
56
+ const found = new Set();
57
+ for (const match of text.matchAll(MENTION_RE)) {
58
+ if (match[1] !== undefined) {
59
+ found.add(Number.parseInt(match[1], 10));
60
+ }
61
+ }
62
+ return [...found];
63
+ }
64
+ export function snapshotFromPullPayload(raw) {
65
+ const number = typeof raw.number === "number" ? raw.number : Number.parseInt(String(raw.number), 10);
66
+ const title = typeof raw.title === "string" ? raw.title : "";
67
+ const body = typeof raw.body === "string" ? raw.body : "";
68
+ return {
69
+ number,
70
+ title,
71
+ body,
72
+ htmlUrl: typeof raw.html_url === "string" ? raw.html_url : null,
73
+ mentions: mentionsFrom(`${title}\n${body}`),
74
+ };
75
+ }
76
+ const OPEN_PULLS_PER_PAGE = 100;
77
+ const OPEN_PULLS_MAX_PAGES = 50;
78
+ function parsePullPage(stdout) {
79
+ const parsed = JSON.parse(stdout.trim() || "[]");
80
+ if (!Array.isArray(parsed)) {
81
+ return [];
82
+ }
83
+ return parsed
84
+ .filter((item) => item !== null && typeof item === "object")
85
+ .map((item) => snapshotFromPullPayload(item));
86
+ }
87
+ function fetchPullPage(runner, endpoint, page) {
88
+ const result = runner([
89
+ endpoint,
90
+ "--method",
91
+ "GET",
92
+ "--raw-field",
93
+ "state=open",
94
+ "--raw-field",
95
+ `per_page=${OPEN_PULLS_PER_PAGE}`,
96
+ "--raw-field",
97
+ `page=${page}`,
98
+ ]);
99
+ if (result.returncode !== 0) {
100
+ throw new Error(`GET ${endpoint} failed: ${result.stderr.trim() || "no stderr"}`);
101
+ }
102
+ return parsePullPage(result.stdout);
103
+ }
104
+ function listOpenPulls(repo, seams) {
105
+ const [owner, name] = splitRepo(repo);
106
+ const endpoint = `repos/${owner}/${name}/pulls`;
107
+ const runner = seams.runGhApiFn ?? runGhApi;
108
+ const out = [];
109
+ for (let page = 1; page <= OPEN_PULLS_MAX_PAGES; page += 1) {
110
+ const pageItems = fetchPullPage(runner, endpoint, page);
111
+ out.push(...pageItems);
112
+ if (pageItems.length < OPEN_PULLS_PER_PAGE) {
113
+ return out;
114
+ }
115
+ }
116
+ // Full last collected page: probe one more page to distinguish exactly-at-cap
117
+ // from a truncated census. Do not collect past the cap.
118
+ const overflow = fetchPullPage(runner, endpoint, OPEN_PULLS_MAX_PAGES + 1);
119
+ if (overflow.length === 0) {
120
+ return out;
121
+ }
122
+ throw new GhRestError({
123
+ stderr: `open-pull census truncated: last page ${OPEN_PULLS_MAX_PAGES} was full (${OPEN_PULLS_PER_PAGE} items) and page ${OPEN_PULLS_MAX_PAGES + 1} has more; later PRs are omitted`,
124
+ exitCode: 0,
125
+ endpoint,
126
+ payload: null,
127
+ hint: "census is incomplete; fail the evaluation rather than treating a truncated list as complete",
128
+ });
129
+ }
130
+ /** GET-only GitHub census. Parent-owned. Never POST/PATCH/PUT/DELETE. */
131
+ export function collectGithubCensus(repo, issues, seams = {}) {
132
+ const views = {};
133
+ for (const n of issues) {
134
+ const raw = restIssueView(repo, n, { runGhApiFn: seams.runGhApiFn });
135
+ views[n] = snapshotFromIssuePayload(raw);
136
+ }
137
+ const openIssues = restIssueListPaginated(repo, { state: "open", excludePulls: true, perPage: 100 }, { runGhApiFn: seams.runGhApiFn }).map((raw) => snapshotFromIssuePayload(raw));
138
+ const openPulls = listOpenPulls(repo, seams);
139
+ return { issues: views, openIssues, openPulls };
140
+ }
141
+ export function defaultGithubReader(seams = {}) {
142
+ return {
143
+ viewIssue: (repo, n) => snapshotFromIssuePayload(restIssueView(repo, n, { runGhApiFn: seams.runGhApiFn })),
144
+ listOpenIssues: (repo) => restIssueListPaginated(repo, { state: "open", excludePulls: true, perPage: 100 }, {
145
+ runGhApiFn: seams.runGhApiFn,
146
+ }).map((raw) => snapshotFromIssuePayload(raw)),
147
+ listOpenPulls: (repo) => listOpenPulls(repo, seams),
148
+ };
149
+ }
150
+ export function pullsMentioning(pulls, issue) {
151
+ return pulls.filter((pull) => pull.mentions.includes(issue) || pull.number === issue);
152
+ }
153
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1,9 @@
1
+ export { EvaluateError, evaluateIssues, renderEvaluateText } from "./evaluate.js";
2
+ export { evaluatorWorktreePath, sha12Of, sinkDir } from "./paths.js";
3
+ export type { EvaluateOptions, EvaluateResult, GithubReader, IssueEvalVerdict, ValidityState, ValueAdvice, WipCensus, } from "./types.js";
4
+ export { CRITIQUE_RECOMMEND_FIELD, DEFAULT_CONCURRENCY, RESERVED_CLEARANCE_RE } from "./types.js";
5
+ export { evaluateValidity } from "./validity.js";
6
+ export { buildValueAdvice, formatValueField, ReservedClearanceError } from "./value.js";
7
+ export { collectWipCensus } from "./wip-census.js";
8
+ export { addEvaluatorWorktree, removeEvaluatorWorktree } from "./worktrees.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,8 @@
1
+ export { EvaluateError, evaluateIssues, renderEvaluateText } from "./evaluate.js";
2
+ export { evaluatorWorktreePath, sha12Of, sinkDir } from "./paths.js";
3
+ export { CRITIQUE_RECOMMEND_FIELD, DEFAULT_CONCURRENCY, RESERVED_CLEARANCE_RE } from "./types.js";
4
+ export { evaluateValidity } from "./validity.js";
5
+ export { buildValueAdvice, formatValueField, ReservedClearanceError } from "./value.js";
6
+ export { collectWipCensus } from "./wip-census.js";
7
+ export { addEvaluatorWorktree, removeEvaluatorWorktree } from "./worktrees.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ export declare function sha12Of(fullSha: string): string;
2
+ export declare function issueEvalScratchRoot(projectRoot: string): string;
3
+ export declare function sinkDir(projectRoot: string, sha12: string, invocationId: string): string;
4
+ export declare function sinkVerdictPath(projectRoot: string, sha12: string, invocationId: string, issue: number): string;
5
+ export declare function sinkManifestPath(projectRoot: string, sha12: string, invocationId: string): string;
6
+ export declare function evaluatorWorktreePath(projectRoot: string, issue: number, invocationId: string): string;
7
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { join, resolve } from "node:path";
2
+ import { SHA12_LENGTH } from "./types.js";
3
+ export function sha12Of(fullSha) {
4
+ const trimmed = fullSha.trim().toLowerCase();
5
+ if (!/^[0-9a-f]{12,40}$/u.test(trimmed)) {
6
+ throw new Error(`origin/master SHA is not a git object id: ${fullSha.trim()}`);
7
+ }
8
+ return trimmed.slice(0, SHA12_LENGTH);
9
+ }
10
+ export function issueEvalScratchRoot(projectRoot) {
11
+ return join(resolve(projectRoot), ".deft-scratch", "issue-eval");
12
+ }
13
+ export function sinkDir(projectRoot, sha12, invocationId) {
14
+ return join(issueEvalScratchRoot(projectRoot), sha12, invocationId);
15
+ }
16
+ export function sinkVerdictPath(projectRoot, sha12, invocationId, issue) {
17
+ return join(sinkDir(projectRoot, sha12, invocationId), `issue-${issue}.json`);
18
+ }
19
+ export function sinkManifestPath(projectRoot, sha12, invocationId) {
20
+ return join(sinkDir(projectRoot, sha12, invocationId), "manifest.json");
21
+ }
22
+ export function evaluatorWorktreePath(projectRoot, issue, invocationId) {
23
+ return join(resolve(projectRoot), ".deft-scratch", "worktrees", `issue-eval-${issue}-${invocationId}`).replace(/\\/g, "/");
24
+ }
25
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1,5 @@
1
+ import type { EvaluateResult } from "./types.js";
2
+ export declare function writeInvocationSink(projectRoot: string, result: EvaluateResult): string;
3
+ export declare function gcStaleSha12Dirs(projectRoot: string, currentSha12: string): string[];
4
+ export declare function teardownSink(projectRoot: string, sha12: string, invocationId: string): void;
5
+ //# sourceMappingURL=sink.d.ts.map
@@ -0,0 +1,52 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { containedRemove, containedWrite } from "../../fs/contained-write.js";
4
+ import { issueEvalScratchRoot, sinkDir, sinkManifestPath, sinkVerdictPath } from "./paths.js";
5
+ import { assertNoReservedClearance } from "./value.js";
6
+ export function writeInvocationSink(projectRoot, result) {
7
+ const root = resolve(projectRoot);
8
+ const serialized = JSON.stringify(result, null, 2);
9
+ assertNoReservedClearance(serialized, "verdict sink");
10
+ containedWrite({
11
+ root,
12
+ target: sinkManifestPath(projectRoot, result.sha12, result.invocationId),
13
+ data: `${serialized}\n`,
14
+ mode: "replace",
15
+ });
16
+ for (const verdict of result.verdicts) {
17
+ const body = JSON.stringify(verdict, null, 2);
18
+ assertNoReservedClearance(body, `verdict issue-${verdict.issue}`);
19
+ containedWrite({
20
+ root,
21
+ target: sinkVerdictPath(projectRoot, result.sha12, result.invocationId, verdict.issue),
22
+ data: `${body}\n`,
23
+ mode: "replace",
24
+ });
25
+ }
26
+ return sinkDir(projectRoot, result.sha12, result.invocationId);
27
+ }
28
+ export function gcStaleSha12Dirs(projectRoot, currentSha12) {
29
+ const root = resolve(projectRoot);
30
+ const scratch = issueEvalScratchRoot(projectRoot);
31
+ if (!existsSync(scratch)) {
32
+ return [];
33
+ }
34
+ const removed = [];
35
+ for (const name of readdirSync(scratch)) {
36
+ if (name === currentSha12) {
37
+ continue;
38
+ }
39
+ const target = `${scratch.replace(/\\/g, "/")}/${name}`;
40
+ containedRemove({ root, target, recursive: true });
41
+ removed.push(name);
42
+ }
43
+ return removed;
44
+ }
45
+ export function teardownSink(projectRoot, sha12, invocationId) {
46
+ const dir = sinkDir(projectRoot, sha12, invocationId);
47
+ if (!existsSync(dir)) {
48
+ return;
49
+ }
50
+ containedRemove({ root: resolve(projectRoot), target: dir, recursive: true });
51
+ }
52
+ //# sourceMappingURL=sink.js.map
@@ -0,0 +1,93 @@
1
+ /** Stage A issue-eval types (#3648). */
2
+ export declare const DEFAULT_CONCURRENCY = 4;
3
+ export declare const ORIGIN_MASTER = "origin/master";
4
+ export declare const SHA12_LENGTH = 12;
5
+ export declare const CRITIQUE_RECOMMEND_FIELD = "critique-recommend";
6
+ export declare const RESERVED_CLEARANCE_RE: RegExp;
7
+ export declare const VALIDITY_STATES: readonly ["still-open", "partial", "likely-shipped", "needs-re-scope"];
8
+ export type ValidityState = (typeof VALIDITY_STATES)[number];
9
+ export interface ValidityVerdict {
10
+ readonly state: ValidityState;
11
+ readonly evidence: string;
12
+ readonly worktreePath: string;
13
+ readonly sessionStartReadOnly: true;
14
+ }
15
+ export interface WipHit {
16
+ readonly kind: "active-xbrief" | "pending-xbrief" | "plan-sequence";
17
+ readonly path: string;
18
+ readonly issue: number;
19
+ }
20
+ export interface WipCensus {
21
+ readonly active: readonly WipHit[];
22
+ readonly pending: readonly WipHit[];
23
+ readonly planSequence: readonly WipHit[];
24
+ }
25
+ export interface GithubIssueSnapshot {
26
+ readonly number: number;
27
+ readonly state: "open" | "closed";
28
+ readonly title: string;
29
+ readonly body: string;
30
+ readonly labels: readonly string[];
31
+ readonly htmlUrl: string | null;
32
+ readonly pullRequest: boolean;
33
+ readonly duplicateOf: number | null;
34
+ }
35
+ export interface GithubPullSnapshot {
36
+ readonly number: number;
37
+ readonly title: string;
38
+ readonly body: string;
39
+ readonly htmlUrl: string | null;
40
+ readonly mentions: readonly number[];
41
+ }
42
+ export interface GithubCensus {
43
+ readonly issues: Readonly<Record<number, GithubIssueSnapshot>>;
44
+ readonly openIssues: readonly GithubIssueSnapshot[];
45
+ readonly openPulls: readonly GithubPullSnapshot[];
46
+ }
47
+ export interface ValueAdvice {
48
+ readonly [CRITIQUE_RECOMMEND_FIELD]: boolean;
49
+ readonly reason: string;
50
+ }
51
+ export interface IssueEvalVerdict {
52
+ readonly issue: number;
53
+ readonly sha12: string;
54
+ readonly invocationId: string;
55
+ readonly validity: ValidityVerdict | null;
56
+ readonly wip: readonly WipHit[];
57
+ readonly github: GithubIssueSnapshot | null;
58
+ readonly openPulls: readonly GithubPullSnapshot[];
59
+ readonly duplicates: readonly number[];
60
+ readonly value: ValueAdvice;
61
+ readonly error: string | null;
62
+ }
63
+ export interface EvaluateResult {
64
+ readonly sha12: string;
65
+ readonly invocationId: string;
66
+ readonly originSha: string;
67
+ readonly sinkDir: string;
68
+ readonly concurrency: number;
69
+ readonly verdicts: readonly IssueEvalVerdict[];
70
+ }
71
+ export interface GitRunnerResult {
72
+ readonly returncode: number;
73
+ readonly stdout: string;
74
+ readonly stderr: string;
75
+ }
76
+ export type GitRunner = (args: readonly string[], cwd: string) => GitRunnerResult;
77
+ export type SessionStartFn = (worktreePath: string) => void;
78
+ export type GithubReader = {
79
+ readonly viewIssue: (repo: string, n: number) => GithubIssueSnapshot;
80
+ readonly listOpenIssues: (repo: string) => readonly GithubIssueSnapshot[];
81
+ readonly listOpenPulls: (repo: string) => readonly GithubPullSnapshot[];
82
+ };
83
+ export interface EvaluateOptions {
84
+ readonly projectRoot: string;
85
+ readonly repo: string;
86
+ readonly issues: readonly number[];
87
+ readonly concurrency?: number;
88
+ readonly invocationId?: string;
89
+ readonly git?: GitRunner;
90
+ readonly sessionStart?: SessionStartFn;
91
+ readonly github?: GithubReader;
92
+ }
93
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,13 @@
1
+ /** Stage A issue-eval types (#3648). */
2
+ export const DEFAULT_CONCURRENCY = 4;
3
+ export const ORIGIN_MASTER = "origin/master";
4
+ export const SHA12_LENGTH = 12;
5
+ export const CRITIQUE_RECOMMEND_FIELD = "critique-recommend";
6
+ export const RESERVED_CLEARANCE_RE = /design-critique:\s*(?:warranted|not warranted)(?:\s*\|\s*(?:warranted|not warranted))?\s*,\s*because/iu;
7
+ export const VALIDITY_STATES = [
8
+ "still-open",
9
+ "partial",
10
+ "likely-shipped",
11
+ "needs-re-scope",
12
+ ];
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,8 @@
1
+ import type { ValidityVerdict } from "./types.js";
2
+ /**
3
+ * Evaluator-owned. Reads only `worktreeRoot` (detached origin/master).
4
+ * Must not accept a WIP census argument.
5
+ */
6
+ export declare function evaluateValidity(worktreeRoot: string, issue: number): ValidityVerdict;
7
+ export declare function joinValidityWithGithub(validity: ValidityVerdict, githubState: "open" | "closed" | null): ValidityVerdict;
8
+ //# sourceMappingURL=validity.d.ts.map