@deftai/directive 0.55.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 (261) hide show
  1. package/dist/bin.d.ts +3 -0
  2. package/dist/bin.js +5 -0
  3. package/dist/branch-parity.d.ts +47 -0
  4. package/dist/branch-parity.js +263 -0
  5. package/dist/cache-parity.d.ts +36 -0
  6. package/dist/cache-parity.js +165 -0
  7. package/dist/cache.d.ts +4 -0
  8. package/dist/cache.js +10 -0
  9. package/dist/capacity-backfill.d.ts +3 -0
  10. package/dist/capacity-backfill.js +9 -0
  11. package/dist/capacity-show.d.ts +3 -0
  12. package/dist/capacity-show.js +9 -0
  13. package/dist/check.d.ts +10 -0
  14. package/dist/check.js +60 -0
  15. package/dist/cli-router/index.d.ts +8 -0
  16. package/dist/cli-router/index.js +37 -0
  17. package/dist/cli-router/route-argv.d.ts +32 -0
  18. package/dist/cli-router/route-argv.js +226 -0
  19. package/dist/codebase-default-extractor.d.ts +3 -0
  20. package/dist/codebase-default-extractor.js +9 -0
  21. package/dist/codebase-map-fresh.d.ts +3 -0
  22. package/dist/codebase-map-fresh.js +9 -0
  23. package/dist/codebase-map.d.ts +3 -0
  24. package/dist/codebase-map.js +9 -0
  25. package/dist/codebase-parity.d.ts +30 -0
  26. package/dist/codebase-parity.js +294 -0
  27. package/dist/codebase-projection-registry.d.ts +3 -0
  28. package/dist/codebase-projection-registry.js +9 -0
  29. package/dist/codebase-provider.d.ts +3 -0
  30. package/dist/codebase-provider.js +9 -0
  31. package/dist/content-validate-cli/validate-links.d.ts +11 -0
  32. package/dist/content-validate-cli/validate-links.js +37 -0
  33. package/dist/content-validate-cli/validate-strategy-output.d.ts +13 -0
  34. package/dist/content-validate-cli/validate-strategy-output.js +56 -0
  35. package/dist/content-validate-cli/verify-capacity.d.ts +12 -0
  36. package/dist/content-validate-cli/verify-capacity.js +54 -0
  37. package/dist/dispatch.d.ts +26 -0
  38. package/dist/dispatch.js +569 -0
  39. package/dist/doctor-parity.d.ts +38 -0
  40. package/dist/doctor-parity.js +151 -0
  41. package/dist/doctor.d.ts +3 -0
  42. package/dist/doctor.js +10 -0
  43. package/dist/gates-cli/_helpers.d.ts +18 -0
  44. package/dist/gates-cli/_helpers.js +71 -0
  45. package/dist/index.d.ts +11 -0
  46. package/dist/index.js +15 -0
  47. package/dist/init-cli/constants.d.ts +5 -0
  48. package/dist/init-cli/constants.js +5 -0
  49. package/dist/init-cli/init.d.ts +3 -0
  50. package/dist/init-cli/init.js +11 -0
  51. package/dist/init-cli/resolve-binary.d.ts +34 -0
  52. package/dist/init-cli/resolve-binary.js +78 -0
  53. package/dist/init-cli/run-deft-install.d.ts +23 -0
  54. package/dist/init-cli/run-deft-install.js +71 -0
  55. package/dist/init-cli/update.d.ts +3 -0
  56. package/dist/init-cli/update.js +11 -0
  57. package/dist/install-cli/coverage-map.d.ts +14 -0
  58. package/dist/install-cli/coverage-map.js +111 -0
  59. package/dist/intake-parity.d.ts +30 -0
  60. package/dist/intake-parity.js +203 -0
  61. package/dist/lifecycle-cli/coverage-map.d.ts +16 -0
  62. package/dist/lifecycle-cli/coverage-map.js +254 -0
  63. package/dist/lifecycle-cli/helpers.d.ts +8 -0
  64. package/dist/lifecycle-cli/helpers.js +20 -0
  65. package/dist/lifecycle-packs-parity.d.ts +30 -0
  66. package/dist/lifecycle-packs-parity.js +377 -0
  67. package/dist/orchestration-cli/coverage-map.d.ts +21 -0
  68. package/dist/orchestration-cli/coverage-map.js +225 -0
  69. package/dist/orchestration-cli/helpers.d.ts +11 -0
  70. package/dist/orchestration-cli/helpers.js +35 -0
  71. package/dist/orchestration-parity.d.ts +38 -0
  72. package/dist/orchestration-parity.js +364 -0
  73. package/dist/parity.d.ts +36 -0
  74. package/dist/parity.js +176 -0
  75. package/dist/platform-parity.d.ts +26 -0
  76. package/dist/platform-parity.js +309 -0
  77. package/dist/policy-parity.d.ts +35 -0
  78. package/dist/policy-parity.js +214 -0
  79. package/dist/policy.d.ts +27 -0
  80. package/dist/policy.js +316 -0
  81. package/dist/pr-closing-keywords-parity.d.ts +45 -0
  82. package/dist/pr-closing-keywords-parity.js +259 -0
  83. package/dist/pr-closing-keywords.d.ts +3 -0
  84. package/dist/pr-closing-keywords.js +10 -0
  85. package/dist/pr-merge-readiness-parity.d.ts +44 -0
  86. package/dist/pr-merge-readiness-parity.js +296 -0
  87. package/dist/pr-merge-readiness.d.ts +3 -0
  88. package/dist/pr-merge-readiness.js +10 -0
  89. package/dist/pr-monitor-parity.d.ts +44 -0
  90. package/dist/pr-monitor-parity.js +283 -0
  91. package/dist/pr-monitor.d.ts +3 -0
  92. package/dist/pr-monitor.js +10 -0
  93. package/dist/pr-protected-issues-parity.d.ts +41 -0
  94. package/dist/pr-protected-issues-parity.js +220 -0
  95. package/dist/pr-protected-issues.d.ts +3 -0
  96. package/dist/pr-protected-issues.js +10 -0
  97. package/dist/pr-wait-mergeable-parity.d.ts +45 -0
  98. package/dist/pr-wait-mergeable-parity.js +340 -0
  99. package/dist/pr-wait-mergeable.d.ts +3 -0
  100. package/dist/pr-wait-mergeable.js +10 -0
  101. package/dist/preflight-cache.d.ts +16 -0
  102. package/dist/preflight-cache.js +106 -0
  103. package/dist/preflight-gh.d.ts +12 -0
  104. package/dist/preflight-gh.js +188 -0
  105. package/dist/probe-session.d.ts +3 -0
  106. package/dist/probe-session.js +10 -0
  107. package/dist/release-e2e-parity.d.ts +31 -0
  108. package/dist/release-e2e-parity.js +114 -0
  109. package/dist/release-e2e.d.ts +3 -0
  110. package/dist/release-e2e.js +10 -0
  111. package/dist/release-parity.d.ts +40 -0
  112. package/dist/release-parity.js +223 -0
  113. package/dist/release-publish-parity.d.ts +36 -0
  114. package/dist/release-publish-parity.js +138 -0
  115. package/dist/release-publish.d.ts +3 -0
  116. package/dist/release-publish.js +10 -0
  117. package/dist/release-rollback-parity.d.ts +37 -0
  118. package/dist/release-rollback-parity.js +161 -0
  119. package/dist/release-rollback.d.ts +3 -0
  120. package/dist/release-rollback.js +10 -0
  121. package/dist/release.d.ts +3 -0
  122. package/dist/release.js +10 -0
  123. package/dist/render-cli/deft-ts-runner.d.ts +20 -0
  124. package/dist/render-cli/deft-ts-runner.js +35 -0
  125. package/dist/render-cli/prd-render-cli.d.ts +2 -0
  126. package/dist/render-cli/prd-render-cli.js +7 -0
  127. package/dist/render-cli/project-render-cli.d.ts +2 -0
  128. package/dist/render-cli/project-render-cli.js +6 -0
  129. package/dist/render-cli/spec-render-cli.d.ts +2 -0
  130. package/dist/render-cli/spec-render-cli.js +6 -0
  131. package/dist/render-cli/spec-validate-cli.d.ts +2 -0
  132. package/dist/render-cli/spec-validate-cli.js +6 -0
  133. package/dist/render-parity.d.ts +35 -0
  134. package/dist/render-parity.js +383 -0
  135. package/dist/scm-parity.d.ts +39 -0
  136. package/dist/scm-parity.js +181 -0
  137. package/dist/scope-lifecycle-parity.d.ts +35 -0
  138. package/dist/scope-lifecycle-parity.js +177 -0
  139. package/dist/scope-lifecycle.d.ts +4 -0
  140. package/dist/scope-lifecycle.js +29 -0
  141. package/dist/session-parity.d.ts +39 -0
  142. package/dist/session-parity.js +260 -0
  143. package/dist/slice-parity.d.ts +36 -0
  144. package/dist/slice-parity.js +304 -0
  145. package/dist/slice.d.ts +4 -0
  146. package/dist/slice.js +29 -0
  147. package/dist/story-ready-parity.d.ts +49 -0
  148. package/dist/story-ready-parity.js +254 -0
  149. package/dist/subagent-monitor.d.ts +3 -0
  150. package/dist/subagent-monitor.js +10 -0
  151. package/dist/swarm-parity.d.ts +28 -0
  152. package/dist/swarm-parity.js +326 -0
  153. package/dist/toolchain-check.d.ts +3 -0
  154. package/dist/toolchain-check.js +14 -0
  155. package/dist/triage-actions-parity.d.ts +33 -0
  156. package/dist/triage-actions-parity.js +201 -0
  157. package/dist/triage-actions.d.ts +17 -0
  158. package/dist/triage-actions.js +144 -0
  159. package/dist/triage-aux-a-parity.d.ts +35 -0
  160. package/dist/triage-aux-a-parity.js +172 -0
  161. package/dist/triage-aux-b-parity.d.ts +32 -0
  162. package/dist/triage-aux-b-parity.js +308 -0
  163. package/dist/triage-bootstrap-parity.d.ts +44 -0
  164. package/dist/triage-bootstrap-parity.js +226 -0
  165. package/dist/triage-bootstrap.d.ts +24 -0
  166. package/dist/triage-bootstrap.js +172 -0
  167. package/dist/triage-bulk.d.ts +17 -0
  168. package/dist/triage-bulk.js +154 -0
  169. package/dist/triage-classify-parity.d.ts +40 -0
  170. package/dist/triage-classify-parity.js +227 -0
  171. package/dist/triage-classify.d.ts +13 -0
  172. package/dist/triage-classify.js +76 -0
  173. package/dist/triage-help.d.ts +3 -0
  174. package/dist/triage-help.js +10 -0
  175. package/dist/triage-queue-parity.d.ts +59 -0
  176. package/dist/triage-queue-parity.js +363 -0
  177. package/dist/triage-queue.d.ts +17 -0
  178. package/dist/triage-queue.js +174 -0
  179. package/dist/triage-reconcile.d.ts +11 -0
  180. package/dist/triage-reconcile.js +76 -0
  181. package/dist/triage-refresh.d.ts +7 -0
  182. package/dist/triage-refresh.js +37 -0
  183. package/dist/triage-scope-drift.d.ts +12 -0
  184. package/dist/triage-scope-drift.js +117 -0
  185. package/dist/triage-scope-parity.d.ts +34 -0
  186. package/dist/triage-scope-parity.js +218 -0
  187. package/dist/triage-scope.d.ts +4 -0
  188. package/dist/triage-scope.js +29 -0
  189. package/dist/triage-smoketest.d.ts +3 -0
  190. package/dist/triage-smoketest.js +43 -0
  191. package/dist/triage-subscribe.d.ts +15 -0
  192. package/dist/triage-subscribe.js +157 -0
  193. package/dist/triage-summary-parity.d.ts +50 -0
  194. package/dist/triage-summary-parity.js +306 -0
  195. package/dist/triage-summary.d.ts +14 -0
  196. package/dist/triage-summary.js +77 -0
  197. package/dist/triage-welcome.d.ts +12 -0
  198. package/dist/triage-welcome.js +78 -0
  199. package/dist/ts-check-lane.d.ts +9 -0
  200. package/dist/ts-check-lane.js +49 -0
  201. package/dist/validate-content-parity.d.ts +33 -0
  202. package/dist/validate-content-parity.js +356 -0
  203. package/dist/vbrief-activate-parity.d.ts +39 -0
  204. package/dist/vbrief-activate-parity.js +216 -0
  205. package/dist/vbrief-activate.d.ts +3 -0
  206. package/dist/vbrief-activate.js +7 -0
  207. package/dist/vbrief-build-parity.d.ts +28 -0
  208. package/dist/vbrief-build-parity.js +399 -0
  209. package/dist/vbrief-build.d.ts +3 -0
  210. package/dist/vbrief-build.js +8 -0
  211. package/dist/vbrief-preflight-parity.d.ts +34 -0
  212. package/dist/vbrief-preflight-parity.js +163 -0
  213. package/dist/vbrief-preflight.d.ts +12 -0
  214. package/dist/vbrief-preflight.js +58 -0
  215. package/dist/vbrief-reconcile-parity.d.ts +22 -0
  216. package/dist/vbrief-reconcile-parity.js +600 -0
  217. package/dist/vbrief-reconcile.d.ts +3 -0
  218. package/dist/vbrief-reconcile.js +7 -0
  219. package/dist/vbrief-validate-parity.d.ts +27 -0
  220. package/dist/vbrief-validate-parity.js +122 -0
  221. package/dist/vbrief-validate.d.ts +3 -0
  222. package/dist/vbrief-validate.js +8 -0
  223. package/dist/vbrief-validation-parity.d.ts +28 -0
  224. package/dist/vbrief-validation-parity.js +645 -0
  225. package/dist/vbrief-validation.d.ts +3 -0
  226. package/dist/vbrief-validation.js +8 -0
  227. package/dist/verify-branch.d.ts +14 -0
  228. package/dist/verify-branch.js +81 -0
  229. package/dist/verify-encoding.d.ts +15 -0
  230. package/dist/verify-encoding.js +86 -0
  231. package/dist/verify-env-parity.d.ts +28 -0
  232. package/dist/verify-env-parity.js +272 -0
  233. package/dist/verify-hooks-installed.d.ts +10 -0
  234. package/dist/verify-hooks-installed.js +48 -0
  235. package/dist/verify-investigation.d.ts +3 -0
  236. package/dist/verify-investigation.js +10 -0
  237. package/dist/verify-judgment-gates.d.ts +3 -0
  238. package/dist/verify-judgment-gates.js +10 -0
  239. package/dist/verify-no-task-runtime.d.ts +3 -0
  240. package/dist/verify-no-task-runtime.js +16 -0
  241. package/dist/verify-session-ritual.d.ts +13 -0
  242. package/dist/verify-session-ritual.js +78 -0
  243. package/dist/verify-source-cli/rule-ownership-lint.d.ts +12 -0
  244. package/dist/verify-source-cli/rule-ownership-lint.js +61 -0
  245. package/dist/verify-source-cli/verify-content-manifest.d.ts +12 -0
  246. package/dist/verify-source-cli/verify-content-manifest.js +61 -0
  247. package/dist/verify-source-cli/verify-scm-boundary.d.ts +13 -0
  248. package/dist/verify-source-cli/verify-scm-boundary.js +66 -0
  249. package/dist/verify-source-cli/verify-stubs.d.ts +11 -0
  250. package/dist/verify-source-cli/verify-stubs.js +27 -0
  251. package/dist/verify-source-parity.d.ts +26 -0
  252. package/dist/verify-source-parity.js +178 -0
  253. package/dist/verify-story-ready.d.ts +16 -0
  254. package/dist/verify-story-ready.js +138 -0
  255. package/dist/verify-tools.d.ts +13 -0
  256. package/dist/verify-tools.js +67 -0
  257. package/dist/verify-wip-cap.d.ts +13 -0
  258. package/dist/verify-wip-cap.js +62 -0
  259. package/dist/wip-cap-parity.d.ts +39 -0
  260. package/dist/wip-cap-parity.js +209 -0
  261. package/package.json +50 -0
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Golden-output parity harness (#1725): runs BOTH Python oracles and ported TS
4
+ * CLIs for triage aux verbs B (bulk / subscribe / help / smoketest).
5
+ *
6
+ * Exit codes: 0 parity / 1 divergence / 2 harness setup error.
7
+ */
8
+ import { spawnSync } from "node:child_process";
9
+ import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync, } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { dirname, join, resolve } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ export function normalizeOutput(text) {
14
+ return text
15
+ .replace(/project_root=[^\s)]+/g, "project_root=<ROOT>")
16
+ .replace(/\/tmp\/deft-[^\s/]+/g, "<TMPROOT>")
17
+ .replace(/change_id": "[^"]+"/g, 'change_id": "<UUID>"')
18
+ .replace(/Using CPython[^\n]*\n/g, "")
19
+ .replace(/Creating virtual environment[^\n]*\n/g, "")
20
+ .replace(/Installed \d+ packages[^\n]*\n/g, "");
21
+ }
22
+ function runCapture(cmd, args, cwd, env = {}) {
23
+ const merged = { ...process.env, ...env };
24
+ for (const key of Object.keys(merged)) {
25
+ if (merged[key] === undefined)
26
+ delete merged[key];
27
+ }
28
+ const result = spawnSync(cmd, args, {
29
+ cwd,
30
+ encoding: "utf8",
31
+ env: merged,
32
+ stdio: ["ignore", "pipe", "pipe"],
33
+ });
34
+ return {
35
+ status: result.status ?? 2,
36
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
37
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
38
+ };
39
+ }
40
+ function writeProjectDefinition(root, policy = {}) {
41
+ mkdirSync(join(root, "vbrief"), { recursive: true });
42
+ writeFileSync(join(root, "vbrief", "PROJECT-DEFINITION.vbrief.json"), `${JSON.stringify({
43
+ vBRIEFInfo: { version: "0.6" },
44
+ plan: { title: "T", status: "running", items: [], policy },
45
+ }, null, 2)}\n`, { encoding: "utf8" });
46
+ }
47
+ function populateCache(cacheRoot, repo, issues) {
48
+ const [owner, name] = repo.split("/");
49
+ if (owner === undefined || name === undefined) {
50
+ throw new Error(`invalid repo ${repo}`);
51
+ }
52
+ for (const issue of issues) {
53
+ const n = String(issue.number);
54
+ const entryDir = join(cacheRoot, "github-issue", owner, name, n);
55
+ mkdirSync(entryDir, { recursive: true });
56
+ writeFileSync(join(entryDir, "raw.json"), JSON.stringify(issue), { encoding: "utf8" });
57
+ writeFileSync(join(entryDir, "meta.json"), JSON.stringify({
58
+ source: "github-issue",
59
+ key: `${repo}/${n}`,
60
+ fetched_at: "2026-05-05T00:00:00Z",
61
+ ttl_seconds: 604800,
62
+ expires_at: "2099-01-01T00:00:00Z",
63
+ scan_result: {
64
+ passed: true,
65
+ scanned_at: "2026-05-05T00:00:00Z",
66
+ scanner_version: "2.0.0",
67
+ flags: [],
68
+ },
69
+ size_bytes: 100,
70
+ stale: false,
71
+ }), { encoding: "utf8" });
72
+ }
73
+ }
74
+ export function buildFixtureRepo(kind) {
75
+ const root = mkdtempSync(join(tmpdir(), "deft-triage-aux-b-parity-"));
76
+ if (kind === "subscribe") {
77
+ writeProjectDefinition(root);
78
+ return root;
79
+ }
80
+ if (kind === "bulk-empty") {
81
+ writeProjectDefinition(root);
82
+ mkdirSync(join(root, ".deft-cache"), { recursive: true });
83
+ return root;
84
+ }
85
+ writeProjectDefinition(root);
86
+ populateCache(join(root, ".deft-cache"), "deftai/parity", [
87
+ {
88
+ number: 99,
89
+ title: "parity issue",
90
+ labels: [{ name: "other-label" }],
91
+ author: { login: "bot" },
92
+ createdAt: "2020-01-01T00:00:00Z",
93
+ },
94
+ ]);
95
+ return root;
96
+ }
97
+ function resolveDeftRoot() {
98
+ if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
99
+ return resolve(process.env.DEFT_ROOT);
100
+ }
101
+ return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
102
+ }
103
+ const PY_SCRIPT = {
104
+ help: "triage_help.py",
105
+ subscribe: "triage_subscribe.py",
106
+ bulk: "triage_bulk.py",
107
+ smoketest: "triage_smoketest.py",
108
+ };
109
+ const TS_CLI = {
110
+ help: "triage-help.js",
111
+ subscribe: "triage-subscribe.js",
112
+ bulk: "triage-bulk.js",
113
+ smoketest: "triage-smoketest.js",
114
+ };
115
+ function runPython(deftRoot, testCase, repo) {
116
+ const script = join(deftRoot, "scripts", PY_SCRIPT[testCase.verb]);
117
+ if (testCase.verb === "help") {
118
+ const cap = runCapture("uv", ["run", "python", script, ...testCase.argv], deftRoot, testCase.env);
119
+ return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
120
+ }
121
+ if (testCase.verb === "bulk") {
122
+ const cap = runCapture("uv", ["run", "python", script, ...testCase.argv], repo, {
123
+ ...testCase.env,
124
+ DEFT_ROOT: deftRoot,
125
+ });
126
+ return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
127
+ }
128
+ if (testCase.verb === "smoketest") {
129
+ const cap = runCapture("uv", ["run", "python", script, ...testCase.argv], deftRoot, testCase.env);
130
+ return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
131
+ }
132
+ const cap = runCapture("uv", ["run", "python", script, ...testCase.argv, "--project-root", repo], deftRoot, testCase.env);
133
+ return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
134
+ }
135
+ function runTs(deftRoot, testCase, repo) {
136
+ const cli = join(deftRoot, "packages", "cli", "dist", TS_CLI[testCase.verb]);
137
+ const argv = [...testCase.argv];
138
+ if (testCase.verb === "subscribe") {
139
+ argv.push("--project-root", repo);
140
+ }
141
+ const cwd = testCase.verb === "bulk" ? repo : deftRoot;
142
+ const cap = runCapture("node", [cli, ...argv], cwd, {
143
+ ...testCase.env,
144
+ DEFT_ROOT: deftRoot,
145
+ });
146
+ return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
147
+ }
148
+ export function diffCase(python, ts, caseName) {
149
+ return {
150
+ caseName,
151
+ exitMismatch: python.exitCode !== ts.exitCode,
152
+ stdoutMismatch: normalizeOutput(python.stdout) !== normalizeOutput(ts.stdout),
153
+ stderrMismatch: normalizeOutput(python.stderr) !== normalizeOutput(ts.stderr),
154
+ pythonExit: python.exitCode,
155
+ tsExit: ts.exitCode,
156
+ };
157
+ }
158
+ export const PARITY_CASES = [
159
+ { name: "help-triage-list", verb: "help", argv: ["triage"] },
160
+ { name: "help-scope-list", verb: "help", argv: ["scope"] },
161
+ { name: "help-verb-queue", verb: "help", argv: ["help", "task triage:queue"] },
162
+ { name: "help-registry-list", verb: "help", argv: ["list"] },
163
+ { name: "help-bulk-intercept", verb: "bulk", argv: ["accept", "--help"] },
164
+ {
165
+ name: "subscribe-label-create",
166
+ verb: "subscribe",
167
+ argv: ["subscribe", "--label", "area:parity"],
168
+ fixtureRoot: "subscribe",
169
+ },
170
+ {
171
+ name: "subscribe-label-idempotent",
172
+ verb: "subscribe",
173
+ argv: ["subscribe", "--label", "dup-label"],
174
+ fixtureRoot: "subscribe",
175
+ env: { DEFT_TRIAGE_ACTOR: "agent:parity" },
176
+ },
177
+ {
178
+ name: "subscribe-unsubscribe-missing",
179
+ verb: "subscribe",
180
+ argv: ["unsubscribe", "--label", "ghost"],
181
+ fixtureRoot: "subscribe",
182
+ },
183
+ {
184
+ name: "bulk-empty-cache",
185
+ verb: "bulk",
186
+ argv: ["accept", "--repo", "deftai/parity"],
187
+ fixtureRoot: "bulk-empty",
188
+ },
189
+ {
190
+ name: "bulk-zero-match",
191
+ verb: "bulk",
192
+ argv: ["defer", "--repo", "deftai/parity", "--label", "no-such-label"],
193
+ fixtureRoot: "bulk-filter",
194
+ },
195
+ {
196
+ name: "smoketest-missing-fixture",
197
+ verb: "smoketest",
198
+ argv: ["--fixture", "/nonexistent/deft-smoketest-fixture"],
199
+ },
200
+ ];
201
+ export function runParity() {
202
+ const deftRoot = resolveDeftRoot();
203
+ const diffs = [];
204
+ const fixtureCache = new Map();
205
+ for (const testCase of PARITY_CASES) {
206
+ let pyRepo = deftRoot;
207
+ let tsRepo = deftRoot;
208
+ let ownsFixture = false;
209
+ if (testCase.fixtureRoot !== undefined) {
210
+ if (!fixtureCache.has(testCase.fixtureRoot)) {
211
+ fixtureCache.set(testCase.fixtureRoot, buildFixtureRepo(testCase.fixtureRoot));
212
+ }
213
+ const template = fixtureCache.get(testCase.fixtureRoot);
214
+ if (template === undefined) {
215
+ throw new Error(`missing fixture template ${testCase.fixtureRoot}`);
216
+ }
217
+ pyRepo = mkdtempSync(join(tmpdir(), "deft-parity-py-"));
218
+ tsRepo = mkdtempSync(join(tmpdir(), "deft-parity-ts-"));
219
+ for (const dest of [pyRepo, tsRepo]) {
220
+ mkdirSync(dest, { recursive: true });
221
+ for (const name of ["vbrief", ".deft-cache"]) {
222
+ const src = join(template, name);
223
+ if (existsSync(src)) {
224
+ cpRecursive(src, join(dest, name));
225
+ }
226
+ }
227
+ }
228
+ ownsFixture = true;
229
+ if (testCase.name === "subscribe-label-idempotent") {
230
+ for (const repo of [pyRepo, tsRepo]) {
231
+ const setup = runCapture("uv", [
232
+ "run",
233
+ "python",
234
+ join(deftRoot, "scripts", "triage_subscribe.py"),
235
+ "subscribe",
236
+ "--label",
237
+ "dup-label",
238
+ "--project-root",
239
+ repo,
240
+ ], deftRoot);
241
+ if (setup.status !== 0) {
242
+ throw new Error(`subscribe-label-idempotent setup failed for ${repo}: ${setup.stderr || setup.stdout}`);
243
+ }
244
+ }
245
+ }
246
+ }
247
+ try {
248
+ const python = runPython(deftRoot, testCase, pyRepo);
249
+ const ts = runTs(deftRoot, testCase, tsRepo);
250
+ diffs.push(diffCase(python, ts, testCase.name));
251
+ }
252
+ finally {
253
+ if (ownsFixture) {
254
+ rmSync(pyRepo, { recursive: true, force: true });
255
+ rmSync(tsRepo, { recursive: true, force: true });
256
+ }
257
+ }
258
+ }
259
+ const ok = diffs.every((d) => !d.exitMismatch && !d.stdoutMismatch && !d.stderrMismatch);
260
+ return { ok, diffs };
261
+ }
262
+ function cpRecursive(src, dest) {
263
+ mkdirSync(dest, { recursive: true });
264
+ for (const entry of readdirSync(src, { withFileTypes: true })) {
265
+ const s = join(src, entry.name);
266
+ const d = join(dest, entry.name);
267
+ if (entry.isDirectory()) {
268
+ cpRecursive(s, d);
269
+ }
270
+ else {
271
+ copyFileSync(s, d);
272
+ }
273
+ }
274
+ }
275
+ export function renderReport(result) {
276
+ if (result.ok) {
277
+ return `triage-aux-b parity: CLEAN -- Python and TS agree on ${PARITY_CASES.length} cases.`;
278
+ }
279
+ const lines = ["triage-aux-b parity: DIVERGENCE"];
280
+ for (const d of result.diffs) {
281
+ if (d.exitMismatch || d.stdoutMismatch || d.stderrMismatch) {
282
+ lines.push(` case: ${d.caseName}`);
283
+ if (d.exitMismatch)
284
+ lines.push(` exit: python=${d.pythonExit} ts=${d.tsExit}`);
285
+ if (d.stdoutMismatch)
286
+ lines.push(" stdout mismatch");
287
+ if (d.stderrMismatch)
288
+ lines.push(" stderr mismatch");
289
+ }
290
+ }
291
+ return lines.join("\n");
292
+ }
293
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
294
+ try {
295
+ const result = runParity();
296
+ if (result.ok) {
297
+ process.stdout.write(`${renderReport(result)}\n`);
298
+ process.exit(0);
299
+ }
300
+ process.stderr.write(`${renderReport(result)}\n`);
301
+ process.exit(1);
302
+ }
303
+ catch (err) {
304
+ process.stderr.write(`triage-aux-b parity: harness error -- ${String(err)}\n`);
305
+ process.exit(2);
306
+ }
307
+ }
308
+ //# sourceMappingURL=triage-aux-b-parity.js.map
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ export interface CommandCapture {
3
+ readonly exitCode: number;
4
+ readonly stdout: string;
5
+ readonly stderr: string;
6
+ }
7
+ export interface ParityCase {
8
+ readonly name: string;
9
+ readonly argv: string[];
10
+ readonly fixture?: FixtureOptions;
11
+ }
12
+ export interface FixtureOptions {
13
+ readonly scopeVbriefs?: Array<{
14
+ folder: string;
15
+ slug: string;
16
+ issue: number;
17
+ }>;
18
+ readonly preRunBootstrap?: boolean;
19
+ }
20
+ export interface ParityDiff {
21
+ readonly caseName: string;
22
+ readonly exitMismatch: boolean;
23
+ readonly stdoutMismatch: boolean;
24
+ readonly stderrMismatch: boolean;
25
+ readonly pythonExit: number;
26
+ readonly tsExit: number;
27
+ }
28
+ export interface ParityResult {
29
+ readonly ok: boolean;
30
+ readonly diffs: ParityDiff[];
31
+ }
32
+ /** Strip volatile absolute paths before compare. */
33
+ export declare function normalizeOutput(text: string): string;
34
+ /** Compare stdout, parsing JSON payloads when present. */
35
+ export declare function normalizeStdout(text: string): string;
36
+ /** Build a throwaway project root with optional vBRIEF fixtures. */
37
+ export declare function buildFixtureRepo(options?: FixtureOptions): string;
38
+ /** Diff one parity case between Python oracle and TS CLI. */
39
+ export declare function diffCase(python: CommandCapture, ts: CommandCapture, caseName: string): ParityDiff;
40
+ export declare const PARITY_CASES: readonly ParityCase[];
41
+ /** Run all parity cases; returns aggregate result. */
42
+ export declare function runParity(): ParityResult;
43
+ export declare function renderReport(result: ParityResult): string;
44
+ //# sourceMappingURL=triage-bootstrap-parity.d.ts.map
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Golden-output parity harness (#1725): builds throwaway fixture repos, runs
4
+ * BOTH the Python oracle (`scripts/triage_bootstrap.py`) and the ported TS
5
+ * triage:bootstrap CLI, and diffs exit codes + stdout/stderr (cache-off).
6
+ *
7
+ * Exit codes: 0 parity / 1 divergence / 2 harness setup error.
8
+ */
9
+ import { spawnSync } from "node:child_process";
10
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
11
+ import { tmpdir } from "node:os";
12
+ import { dirname, join, resolve } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ /** Strip volatile absolute paths before compare. */
15
+ export function normalizeOutput(text) {
16
+ return text
17
+ .replace(/project_root": "[^"]+"/g, 'project_root": "<ROOT>"')
18
+ .replace(/"project_root": "[^"]+"/g, '"project_root": "<ROOT>"')
19
+ .replace(/project_root=[^\s)]+/g, "project_root=<ROOT>")
20
+ .replace(/under \/tmp\/[^\s)]+/g, "under <ROOT>")
21
+ .replace(/under \/var\/[^\s)]+/g, "under <ROOT>");
22
+ }
23
+ /** Compare stdout, parsing JSON payloads when present. */
24
+ export function normalizeStdout(text) {
25
+ const trimmed = text.trim();
26
+ if (trimmed.startsWith("{")) {
27
+ try {
28
+ const parsed = JSON.parse(trimmed);
29
+ if (typeof parsed.project_root === "string") {
30
+ parsed.project_root = "<ROOT>";
31
+ }
32
+ const steps = parsed.steps;
33
+ if (Array.isArray(steps)) {
34
+ for (const step of steps) {
35
+ if (typeof step !== "object" || step === null)
36
+ continue;
37
+ const record = step;
38
+ if (typeof record.message === "string") {
39
+ record.message = normalizeOutput(record.message);
40
+ }
41
+ const details = record.details;
42
+ if (typeof details === "object" && details !== null) {
43
+ const detailRecord = details;
44
+ if (typeof detailRecord.audit_path === "string") {
45
+ detailRecord.audit_path = "<ROOT>/vbrief/.eval/candidates.jsonl";
46
+ }
47
+ if (typeof detailRecord.fetch_timeout_s === "number") {
48
+ detailRecord.fetch_timeout_s = Math.trunc(detailRecord.fetch_timeout_s);
49
+ }
50
+ if (typeof detailRecord.elapsed_s === "number") {
51
+ detailRecord.elapsed_s = Number(detailRecord.elapsed_s.toFixed(3));
52
+ }
53
+ }
54
+ }
55
+ }
56
+ return JSON.stringify(parsed);
57
+ }
58
+ catch {
59
+ return normalizeOutput(text);
60
+ }
61
+ }
62
+ return normalizeOutput(text);
63
+ }
64
+ function runCapture(cmd, args, cwd, env = {}) {
65
+ const merged = { ...process.env, ...env };
66
+ for (const key of Object.keys(merged)) {
67
+ if (merged[key] === undefined)
68
+ delete merged[key];
69
+ }
70
+ const result = spawnSync(cmd, args, {
71
+ cwd,
72
+ encoding: "utf8",
73
+ env: merged,
74
+ stdio: ["ignore", "pipe", "pipe"],
75
+ });
76
+ return {
77
+ status: result.status ?? 2,
78
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
79
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
80
+ };
81
+ }
82
+ function writeScopeVbrief(root, folder, slug, issueNumber) {
83
+ const dir = join(root, "vbrief", folder);
84
+ mkdirSync(dir, { recursive: true });
85
+ writeFileSync(join(dir, `${slug}.vbrief.json`), `${JSON.stringify({
86
+ vBRIEFInfo: { version: "0.6" },
87
+ plan: {
88
+ id: slug,
89
+ title: slug,
90
+ status: "proposed",
91
+ references: [
92
+ {
93
+ type: "x-vbrief/github-issue",
94
+ uri: `https://github.com/deftai/directive/issues/${issueNumber}`,
95
+ },
96
+ ],
97
+ },
98
+ })}\n`, { encoding: "utf8" });
99
+ }
100
+ /** Build a throwaway project root with optional vBRIEF fixtures. */
101
+ export function buildFixtureRepo(options = {}) {
102
+ const root = mkdtempSync(join(tmpdir(), "deft-triage-bootstrap-parity-"));
103
+ mkdirSync(join(root, "vbrief"), { recursive: true });
104
+ for (const item of options.scopeVbriefs ?? []) {
105
+ writeScopeVbrief(root, item.folder, item.slug, item.issue);
106
+ }
107
+ return root;
108
+ }
109
+ function resolveDeftRoot() {
110
+ if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
111
+ return resolve(process.env.DEFT_ROOT);
112
+ }
113
+ return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
114
+ }
115
+ function runPythonBootstrap(deftRoot, repo, argv) {
116
+ const args = ["run", "python", join(deftRoot, "scripts", "triage_bootstrap.py"), ...argv];
117
+ if (!argv.some((a) => a === "--project-root" || a.startsWith("--project-root="))) {
118
+ args.push("--project-root", repo);
119
+ }
120
+ const cap = runCapture("uv", args, deftRoot);
121
+ return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
122
+ }
123
+ function runTsBootstrap(deftRoot, repo, argv) {
124
+ const args = [join(deftRoot, "packages", "cli", "dist", "triage-bootstrap.js"), ...argv];
125
+ if (!argv.some((a) => a === "--project-root" || a.startsWith("--project-root="))) {
126
+ args.push("--project-root", repo);
127
+ }
128
+ const cap = runCapture("node", args, deftRoot);
129
+ return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
130
+ }
131
+ /** Diff one parity case between Python oracle and TS CLI. */
132
+ export function diffCase(python, ts, caseName) {
133
+ const pyOut = normalizeStdout(python.stdout);
134
+ const tsOut = normalizeStdout(ts.stdout);
135
+ const pyErr = normalizeOutput(python.stderr);
136
+ const tsErr = normalizeOutput(ts.stderr);
137
+ return {
138
+ caseName,
139
+ exitMismatch: python.exitCode !== ts.exitCode,
140
+ stdoutMismatch: pyOut !== tsOut,
141
+ stderrMismatch: pyErr !== tsErr,
142
+ pythonExit: python.exitCode,
143
+ tsExit: ts.exitCode,
144
+ };
145
+ }
146
+ export const PARITY_CASES = [
147
+ {
148
+ name: "json-no-repo-quiet",
149
+ argv: ["--json", "--quiet"],
150
+ },
151
+ {
152
+ name: "recap-no-repo-quiet",
153
+ argv: ["--quiet"],
154
+ },
155
+ {
156
+ name: "config-error-bad-root",
157
+ argv: ["--json", "--project-root", "/nonexistent-deft-bootstrap-parity-root"],
158
+ },
159
+ {
160
+ name: "json-invalid-repo-quiet",
161
+ argv: ["--json", "--quiet", "--repo", "bad"],
162
+ },
163
+ {
164
+ name: "json-idempotent-rerun",
165
+ argv: ["--json", "--quiet"],
166
+ fixture: { preRunBootstrap: true },
167
+ },
168
+ ];
169
+ /** Run all parity cases; returns aggregate result. */
170
+ export function runParity() {
171
+ const deftRoot = resolveDeftRoot();
172
+ const diffs = [];
173
+ for (const testCase of PARITY_CASES) {
174
+ const pyRepo = buildFixtureRepo(testCase.fixture ?? {});
175
+ const tsRepo = buildFixtureRepo(testCase.fixture ?? {});
176
+ try {
177
+ if (testCase.fixture?.preRunBootstrap === true) {
178
+ runPythonBootstrap(deftRoot, pyRepo, ["--quiet"]);
179
+ runTsBootstrap(deftRoot, tsRepo, ["--quiet"]);
180
+ }
181
+ const python = runPythonBootstrap(deftRoot, pyRepo, testCase.argv);
182
+ const ts = runTsBootstrap(deftRoot, tsRepo, testCase.argv);
183
+ diffs.push(diffCase(python, ts, testCase.name));
184
+ }
185
+ finally {
186
+ rmSync(pyRepo, { recursive: true, force: true });
187
+ rmSync(tsRepo, { recursive: true, force: true });
188
+ }
189
+ }
190
+ const ok = diffs.every((d) => !d.exitMismatch && !d.stdoutMismatch && !d.stderrMismatch);
191
+ return { ok, diffs };
192
+ }
193
+ export function renderReport(result) {
194
+ if (result.ok) {
195
+ return `triage:bootstrap parity: CLEAN -- Python and TS agree on ${PARITY_CASES.length} cases.`;
196
+ }
197
+ const lines = ["triage:bootstrap parity: DIVERGENCE"];
198
+ for (const d of result.diffs) {
199
+ if (d.exitMismatch || d.stdoutMismatch || d.stderrMismatch) {
200
+ lines.push(` case: ${d.caseName}`);
201
+ if (d.exitMismatch)
202
+ lines.push(` exit: python=${d.pythonExit} ts=${d.tsExit}`);
203
+ if (d.stdoutMismatch)
204
+ lines.push(" stdout mismatch");
205
+ if (d.stderrMismatch)
206
+ lines.push(" stderr mismatch");
207
+ }
208
+ }
209
+ return lines.join("\n");
210
+ }
211
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
212
+ try {
213
+ const result = runParity();
214
+ if (result.ok) {
215
+ process.stdout.write(`${renderReport(result)}\n`);
216
+ process.exit(0);
217
+ }
218
+ process.stderr.write(`${renderReport(result)}\n`);
219
+ process.exit(1);
220
+ }
221
+ catch (err) {
222
+ process.stderr.write(`triage:bootstrap parity: harness error -- ${String(err)}\n`);
223
+ process.exit(2);
224
+ }
225
+ }
226
+ //# sourceMappingURL=triage-bootstrap-parity.js.map
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ export interface ParsedArgs {
3
+ projectRoot: string;
4
+ repo: string | null;
5
+ limit?: number;
6
+ state?: string;
7
+ labels: string[];
8
+ author?: string;
9
+ batchSize?: number;
10
+ delayMs?: number;
11
+ fetchTimeoutS: number;
12
+ quiet: boolean;
13
+ emitJson: boolean;
14
+ error?: string;
15
+ }
16
+ type BootstrapModule = typeof import("../../core/dist/triage/bootstrap/index.js");
17
+ /** Parse triage-bootstrap CLI args, mirroring the Python argparse surface. */
18
+ export declare function parseArgs(argv: string[]): ParsedArgs;
19
+ /** Run triage:bootstrap with an injected core module (test seam). */
20
+ export declare function runWithModule(mod: BootstrapModule, argv: string[]): Promise<number>;
21
+ /** Run triage:bootstrap and return the process exit code. */
22
+ export declare function run(argv: string[]): Promise<number>;
23
+ export {};
24
+ //# sourceMappingURL=triage-bootstrap.d.ts.map