@deftai/directive-core 0.106.0 → 0.108.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 (249) hide show
  1. package/dist/architecture/sor-preflight.js +2 -0
  2. package/dist/check/cached-orchestrator.js +28 -3
  3. package/dist/check/gate-lists.js +2 -0
  4. package/dist/check/named-cause.js +18 -3
  5. package/dist/codebase/provider.js +7 -1
  6. package/dist/delivery-attempt/handoff.js +1 -1
  7. package/dist/design-critique/citation-grammar.d.ts +53 -0
  8. package/dist/design-critique/citation-grammar.js +303 -0
  9. package/dist/design-critique/completed-arc-record.d.ts +62 -0
  10. package/dist/design-critique/completed-arc-record.js +232 -0
  11. package/dist/design-critique/exclusive-chip.d.ts +33 -0
  12. package/dist/design-critique/exclusive-chip.js +68 -0
  13. package/dist/design-critique/parent-audit.d.ts +59 -0
  14. package/dist/design-critique/parent-audit.js +121 -0
  15. package/dist/doctor/checks.d.ts +6 -0
  16. package/dist/doctor/checks.js +35 -0
  17. package/dist/freshness/bind.d.ts +32 -3
  18. package/dist/freshness/bind.js +58 -12
  19. package/dist/hooks/classify/host-session-identity.d.ts +76 -0
  20. package/dist/hooks/classify/host-session-identity.js +470 -0
  21. package/dist/hooks/classify/index.d.ts +3 -2
  22. package/dist/hooks/classify/index.js +3 -2
  23. package/dist/hooks/classify/paths.d.ts +7 -0
  24. package/dist/hooks/classify/paths.js +35 -0
  25. package/dist/hooks/classify/stdin.d.ts +5 -0
  26. package/dist/hooks/classify/stdin.js +27 -0
  27. package/dist/hooks/dest-form.d.ts +46 -0
  28. package/dist/hooks/dest-form.js +575 -0
  29. package/dist/hooks/dispatcher.d.ts +69 -2
  30. package/dist/hooks/dispatcher.js +609 -45
  31. package/dist/hooks/fixtures/cases.d.ts +16 -2
  32. package/dist/hooks/fixtures/cases.js +175 -0
  33. package/dist/hooks/index.d.ts +1 -0
  34. package/dist/hooks/index.js +1 -0
  35. package/dist/hooks/scope.js +3 -1
  36. package/dist/init-deposit/agent-hooks.d.ts +2 -1
  37. package/dist/init-deposit/agent-hooks.js +8 -1
  38. package/dist/init-deposit/hook-runtime-travel.d.ts +77 -0
  39. package/dist/init-deposit/hook-runtime-travel.js +269 -0
  40. package/dist/intake/clause-derivation.js +9 -3
  41. package/dist/intake/github-auth-modes-cli.js +14 -2
  42. package/dist/intake/github-auth-modes.d.ts +54 -9
  43. package/dist/intake/github-auth-modes.js +326 -123
  44. package/dist/intake/issue-ingest.js +62 -2
  45. package/dist/intake/platform-capabilities.d.ts +9 -2
  46. package/dist/intake/platform-capabilities.js +38 -9
  47. package/dist/lifecycle/completed-tracked-on-delivery.d.ts +26 -0
  48. package/dist/lifecycle/completed-tracked-on-delivery.js +37 -14
  49. package/dist/lifecycle/completed-write-guard.d.ts +48 -0
  50. package/dist/lifecycle/completed-write-guard.js +373 -0
  51. package/dist/lifecycle/index.d.ts +1 -0
  52. package/dist/lifecycle/index.js +1 -0
  53. package/dist/literal-acceptance/capture.d.ts +16 -0
  54. package/dist/literal-acceptance/capture.js +75 -7
  55. package/dist/literal-acceptance/evaluate.js +25 -3
  56. package/dist/literal-acceptance/index.d.ts +2 -2
  57. package/dist/literal-acceptance/index.js +2 -2
  58. package/dist/literal-acceptance/run.js +9 -3
  59. package/dist/literal-acceptance/types.d.ts +6 -0
  60. package/dist/literal-acceptance/types.js +6 -0
  61. package/dist/orphan-active/evaluate.d.ts +25 -0
  62. package/dist/orphan-active/evaluate.js +193 -128
  63. package/dist/orphan-active/index.d.ts +1 -0
  64. package/dist/orphan-active/index.js +1 -0
  65. package/dist/orphan-active/issue-state.d.ts +121 -0
  66. package/dist/orphan-active/issue-state.js +278 -0
  67. package/dist/platform/cursor-managed-runtime.d.ts +95 -0
  68. package/dist/platform/cursor-managed-runtime.js +241 -0
  69. package/dist/platform/index.d.ts +1 -0
  70. package/dist/platform/index.js +1 -0
  71. package/dist/platform/platform-capabilities.d.ts +23 -1
  72. package/dist/platform/platform-capabilities.js +55 -15
  73. package/dist/policy/ceremony-dial.js +5 -9
  74. package/dist/policy/host-hooks.js +4 -9
  75. package/dist/policy/org-force-on-migration.js +5 -10
  76. package/dist/policy/plan-extensions.d.ts +14 -2
  77. package/dist/policy/plan-extensions.js +24 -3
  78. package/dist/policy/product-signal.js +5 -10
  79. package/dist/policy/require-human-merge.js +5 -9
  80. package/dist/policy/resolve.js +77 -16
  81. package/dist/policy/runtime-authority.d.ts +18 -0
  82. package/dist/policy/runtime-authority.js +11 -0
  83. package/dist/policy/value-feedback.js +9 -18
  84. package/dist/policy/write-fence.js +3 -0
  85. package/dist/pr-closeout-attestable/evaluate.d.ts +80 -0
  86. package/dist/pr-closeout-attestable/evaluate.js +306 -0
  87. package/dist/pr-closeout-attestable/index.d.ts +2 -0
  88. package/dist/pr-closeout-attestable/index.js +2 -0
  89. package/dist/pr-protected-issues/gh.d.ts +6 -2
  90. package/dist/pr-protected-issues/gh.js +7 -3
  91. package/dist/pr-wait-mergeable/cascade.d.ts +9 -1
  92. package/dist/pr-wait-mergeable/cascade.js +21 -1
  93. package/dist/pr-wait-mergeable/types.d.ts +2 -0
  94. package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
  95. package/dist/pr-wait-mergeable/wrappers.js +11 -0
  96. package/dist/preflight/evaluate.d.ts +15 -0
  97. package/dist/preflight/evaluate.js +29 -3
  98. package/dist/preflight-cache/evaluate.js +9 -3
  99. package/dist/product-first-done-gate/acceptance-resolver.d.ts +14 -2
  100. package/dist/product-first-done-gate/acceptance-resolver.js +13 -2
  101. package/dist/product-first-done-gate/evaluate.js +53 -12
  102. package/dist/release/cli-drift-report.d.ts +68 -0
  103. package/dist/release/cli-drift-report.js +189 -0
  104. package/dist/release/index.d.ts +1 -0
  105. package/dist/release/index.js +1 -0
  106. package/dist/release/issue-state-fetch.d.ts +5 -3
  107. package/dist/release/issue-state-fetch.js +86 -21
  108. package/dist/release/native-steps.js +1 -0
  109. package/dist/release/pipeline.js +19 -0
  110. package/dist/release/types.d.ts +14 -0
  111. package/dist/render/constants.d.ts +4 -0
  112. package/dist/render/constants.js +11 -8
  113. package/dist/render/export-spec.js +31 -5
  114. package/dist/render/index.d.ts +1 -1
  115. package/dist/render/index.js +1 -1
  116. package/dist/render/prd-render.d.ts +4 -1
  117. package/dist/render/prd-render.js +82 -23
  118. package/dist/render/project-render.js +14 -7
  119. package/dist/render/scope-outlook.d.ts +2 -0
  120. package/dist/render/scope-outlook.js +3 -0
  121. package/dist/render/spec-render.js +2 -2
  122. package/dist/resolution/package-manager.d.ts +33 -4
  123. package/dist/resolution/package-manager.js +210 -9
  124. package/dist/review-monitor/constants.d.ts +7 -0
  125. package/dist/review-monitor/constants.js +7 -0
  126. package/dist/review-monitor/github-lease.d.ts +98 -1
  127. package/dist/review-monitor/github-lease.js +182 -1
  128. package/dist/review-monitor/lease-comment.d.ts +36 -0
  129. package/dist/review-monitor/lease-comment.js +103 -2
  130. package/dist/scm/call.js +12 -3
  131. package/dist/scm/design-critique-chip.d.ts +38 -0
  132. package/dist/scm/design-critique-chip.js +187 -0
  133. package/dist/scm/gh-rest.d.ts +9 -1
  134. package/dist/scm/gh-rest.js +103 -0
  135. package/dist/scm/index.d.ts +1 -0
  136. package/dist/scm/index.js +1 -0
  137. package/dist/scm/main.d.ts +3 -0
  138. package/dist/scm/main.js +15 -1
  139. package/dist/scm/readiness-cli.d.ts +2 -0
  140. package/dist/scm/readiness-cli.js +42 -1
  141. package/dist/scm/readiness.d.ts +4 -1
  142. package/dist/scm/readiness.js +35 -5
  143. package/dist/scope/decompose.js +2 -1
  144. package/dist/scope/delivery-evidence.d.ts +27 -1
  145. package/dist/scope/index.d.ts +1 -0
  146. package/dist/scope/index.js +1 -0
  147. package/dist/scope/lifecycle-write.d.ts +24 -0
  148. package/dist/scope/lifecycle-write.js +64 -0
  149. package/dist/scope/main.js +3 -1
  150. package/dist/scope/project-definition-sync.js +14 -7
  151. package/dist/scope/transition.js +90 -11
  152. package/dist/scope-provenance/evaluate.js +5 -1
  153. package/dist/session/git.d.ts +42 -0
  154. package/dist/session/git.js +219 -3
  155. package/dist/session/occupancy.d.ts +239 -12
  156. package/dist/session/occupancy.js +820 -27
  157. package/dist/session/ritual-sentinel.d.ts +5 -0
  158. package/dist/session/ritual-sentinel.js +5 -0
  159. package/dist/session/session-ready.d.ts +5 -1
  160. package/dist/session/session-ready.js +98 -8
  161. package/dist/session/session-start.d.ts +17 -1
  162. package/dist/session/session-start.js +49 -17
  163. package/dist/session/toolchain-preflight.d.ts +13 -3
  164. package/dist/session/toolchain-preflight.js +93 -18
  165. package/dist/session/verify-session-ritual.d.ts +33 -3
  166. package/dist/session/verify-session-ritual.js +89 -19
  167. package/dist/slice/constants.d.ts +1 -1
  168. package/dist/spec-authority/constants.d.ts +10 -2
  169. package/dist/spec-authority/constants.js +53 -8
  170. package/dist/spec-authority/resolver.d.ts +3 -0
  171. package/dist/spec-authority/resolver.js +55 -7
  172. package/dist/subprocess/max-buffer.d.ts +14 -0
  173. package/dist/subprocess/max-buffer.js +15 -0
  174. package/dist/swarm/complete-cohort.d.ts +2 -0
  175. package/dist/swarm/complete-cohort.js +21 -13
  176. package/dist/swarm/index.d.ts +1 -0
  177. package/dist/swarm/index.js +1 -0
  178. package/dist/swarm/launch-cli.js +53 -33
  179. package/dist/swarm/launch.d.ts +77 -0
  180. package/dist/swarm/launch.js +248 -38
  181. package/dist/swarm/pre-dispatch-cli.js +2 -1
  182. package/dist/swarm/pre-dispatch.js +10 -1
  183. package/dist/swarm/subagent-status-dir.d.ts +27 -0
  184. package/dist/swarm/subagent-status-dir.js +42 -0
  185. package/dist/swarm/worktrees.d.ts +17 -3
  186. package/dist/swarm/worktrees.js +48 -4
  187. package/dist/triage/actions/index.d.ts +4 -0
  188. package/dist/triage/actions/index.js +6 -0
  189. package/dist/triage/evaluate/evaluate.d.ts +11 -0
  190. package/dist/triage/evaluate/evaluate.js +169 -0
  191. package/dist/triage/evaluate/github.d.ts +16 -0
  192. package/dist/triage/evaluate/github.js +153 -0
  193. package/dist/triage/evaluate/index.d.ts +9 -0
  194. package/dist/triage/evaluate/index.js +8 -0
  195. package/dist/triage/evaluate/paths.d.ts +7 -0
  196. package/dist/triage/evaluate/paths.js +25 -0
  197. package/dist/triage/evaluate/sink.d.ts +5 -0
  198. package/dist/triage/evaluate/sink.js +52 -0
  199. package/dist/triage/evaluate/types.d.ts +93 -0
  200. package/dist/triage/evaluate/types.js +13 -0
  201. package/dist/triage/evaluate/validity.d.ts +8 -0
  202. package/dist/triage/evaluate/validity.js +87 -0
  203. package/dist/triage/evaluate/value.d.ts +12 -0
  204. package/dist/triage/evaluate/value.js +33 -0
  205. package/dist/triage/evaluate/wip-census.d.ts +5 -0
  206. package/dist/triage/evaluate/wip-census.js +39 -0
  207. package/dist/triage/evaluate/worktrees.d.ts +8 -0
  208. package/dist/triage/evaluate/worktrees.js +68 -0
  209. package/dist/triage/evaluate/xbrief-refs.d.ts +8 -0
  210. package/dist/triage/evaluate/xbrief-refs.js +60 -0
  211. package/dist/triage/help/registry-data.d.ts +21 -7
  212. package/dist/triage/help/registry-data.js +53 -5
  213. package/dist/triage/index.d.ts +1 -0
  214. package/dist/triage/index.js +1 -0
  215. package/dist/triage/scope/mutations-core.d.ts +2 -6
  216. package/dist/triage/scope/mutations-core.js +23 -55
  217. package/dist/triage/scope-drift/add-ignore.js +5 -39
  218. package/dist/triage/subscribe/index.d.ts +2 -6
  219. package/dist/triage/subscribe/index.js +15 -65
  220. package/dist/triage/welcome/writers.js +15 -74
  221. package/dist/value/feedback-file.d.ts +17 -2
  222. package/dist/value/feedback-file.js +80 -6
  223. package/dist/vbrief-build/index.d.ts +2 -1
  224. package/dist/vbrief-build/index.js +2 -1
  225. package/dist/vbrief-build/parity-scenarios.js +5 -5
  226. package/dist/vbrief-build/project-definition-io.d.ts +63 -3
  227. package/dist/vbrief-build/project-definition-io.js +357 -31
  228. package/dist/vbrief-build/project-definition-mutation.d.ts +32 -0
  229. package/dist/vbrief-build/project-definition-mutation.js +43 -0
  230. package/dist/vbrief-reconcile/labels.d.ts +1 -0
  231. package/dist/vbrief-reconcile/labels.js +30 -0
  232. package/dist/vbrief-validate/conformance.d.ts +1 -0
  233. package/dist/vbrief-validate/conformance.js +85 -8
  234. package/dist/vbrief-validate/main.js +2 -0
  235. package/dist/vbrief-validate/precutover.js +5 -10
  236. package/dist/vbrief-validation/story-quality.d.ts +7 -0
  237. package/dist/vbrief-validation/story-quality.js +8 -1
  238. package/dist/verify-ac/clauses.d.ts +62 -2
  239. package/dist/verify-ac/clauses.js +183 -111
  240. package/dist/verify-ac/index.d.ts +1 -1
  241. package/dist/verify-ac/index.js +1 -1
  242. package/dist/verify-env/node-runtime.d.ts +8 -4
  243. package/dist/verify-env/node-runtime.js +9 -6
  244. package/dist/verify-env/toolchain-check.d.ts +35 -3
  245. package/dist/verify-env/toolchain-check.js +155 -36
  246. package/dist/verify-source/project-definition-mutation-boundary.d.ts +104 -0
  247. package/dist/verify-source/project-definition-mutation-boundary.js +326 -0
  248. package/dist/xbrief-migrate/agents-header.js +69 -7
  249. package/package.json +7 -3
@@ -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
@@ -0,0 +1,87 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { listXbriefHits } from "./xbrief-refs.js";
4
+ const ADR_DIR = join("docs", "decisions");
5
+ const CONTRACT_DIR = join("content", "contracts");
6
+ function filesMentionIssue(dir, issue) {
7
+ if (!existsSync(dir)) {
8
+ return [];
9
+ }
10
+ const needle = `#${issue}`;
11
+ const hits = [];
12
+ for (const name of readdirSync(dir)) {
13
+ if (!name.endsWith(".md")) {
14
+ continue;
15
+ }
16
+ const path = join(dir, name);
17
+ try {
18
+ const text = readFileSync(path, "utf8");
19
+ if (text.includes(needle) || text.includes(`/issues/${issue}`)) {
20
+ hits.push(path);
21
+ }
22
+ }
23
+ catch { }
24
+ }
25
+ return hits;
26
+ }
27
+ /**
28
+ * Evaluator-owned. Reads only `worktreeRoot` (detached origin/master).
29
+ * Must not accept a WIP census argument.
30
+ */
31
+ export function evaluateValidity(worktreeRoot, issue) {
32
+ const completed = listXbriefHits(worktreeRoot, "completed").filter((hit) => hit.issue === issue);
33
+ const pending = listXbriefHits(worktreeRoot, "pending").filter((hit) => hit.issue === issue);
34
+ const active = listXbriefHits(worktreeRoot, "active").filter((hit) => hit.issue === issue);
35
+ const proposed = listXbriefHits(worktreeRoot, "proposed").filter((hit) => hit.issue === issue);
36
+ const adrHits = filesMentionIssue(join(worktreeRoot, ADR_DIR), issue);
37
+ const contractHits = filesMentionIssue(join(worktreeRoot, CONTRACT_DIR), issue);
38
+ if (completed.length > 0) {
39
+ return {
40
+ state: "likely-shipped",
41
+ evidence: `completed xbrief on origin/master: ${completed[0]?.path}`,
42
+ worktreePath: worktreeRoot,
43
+ sessionStartReadOnly: true,
44
+ };
45
+ }
46
+ if (pending.length > 0 || active.length > 0 || proposed.length > 0) {
47
+ const hit = pending[0] ?? active[0] ?? proposed[0];
48
+ return {
49
+ state: "partial",
50
+ evidence: `committed lifecycle xbrief on origin/master: ${hit?.path}`,
51
+ worktreePath: worktreeRoot,
52
+ sessionStartReadOnly: true,
53
+ };
54
+ }
55
+ if (adrHits.length > 0 || contractHits.length > 0) {
56
+ return {
57
+ state: "partial",
58
+ evidence: `ADR/contract mention on origin/master: ${adrHits[0] ?? contractHits[0] ?? ""}`,
59
+ worktreePath: worktreeRoot,
60
+ sessionStartReadOnly: true,
61
+ };
62
+ }
63
+ return {
64
+ state: "still-open",
65
+ evidence: "no origin/master lifecycle coverage, ADR, or contract mention",
66
+ worktreePath: worktreeRoot,
67
+ sessionStartReadOnly: true,
68
+ };
69
+ }
70
+ export function joinValidityWithGithub(validity, githubState) {
71
+ if (githubState === "closed" && validity.state === "still-open") {
72
+ return {
73
+ ...validity,
74
+ state: "likely-shipped",
75
+ evidence: `${validity.evidence}; GitHub issue state=closed`,
76
+ };
77
+ }
78
+ if (githubState === "open" && validity.state === "likely-shipped") {
79
+ return {
80
+ ...validity,
81
+ state: "needs-re-scope",
82
+ evidence: `${validity.evidence}; GitHub issue still open`,
83
+ };
84
+ }
85
+ return validity;
86
+ }
87
+ //# sourceMappingURL=validity.js.map