@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,223 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Golden-output parity harness (#1729): runs BOTH the Python oracle
4
+ * (`scripts/release.py`) and the ported TS release CLI with identical argv,
5
+ * then diffs exit codes and normalised stderr/stdout. Exit 0 only on
6
+ * byte-identical results (volatile ISO dates normalised in stderr).
7
+ *
8
+ * Exit codes: 0 parity / 1 divergence / 2 harness setup error.
9
+ */
10
+ import { execFileSync, spawnSync } from "node:child_process";
11
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { dirname, join, resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ export const SAMPLE_CHANGELOG = ` Changelog
16
+
17
+ All notable changes to the project.
18
+
19
+ ## [Unreleased]
20
+
21
+ ### Added
22
+ - New release automation (#74)
23
+
24
+ ### Changed
25
+ - Refactored module X
26
+
27
+ ### Fixed
28
+ - Bug Y
29
+
30
+ ## [0.20.2] - 2026-04-24
31
+
32
+ ### Added
33
+ - Prior change
34
+
35
+ ## [0.20.0] - 2026-04-23
36
+
37
+ ### Added
38
+ - Older change
39
+
40
+ [Unreleased]: https://github.com/deftai/directive/compare/v0.20.2...HEAD
41
+ [0.20.2]: https://github.com/deftai/directive/compare/v0.20.0...v0.20.2
42
+ [0.20.0]: https://github.com/deftai/directive/compare/v0.19.0...v0.20.0
43
+ `;
44
+ export const PARITY_SCENARIOS = [
45
+ { name: "invalid-version", argv: ["not-a-version"] },
46
+ {
47
+ name: "dry-run-fixture",
48
+ argv: [
49
+ "0.21.0",
50
+ "--dry-run",
51
+ "--skip-tag",
52
+ "--skip-release",
53
+ "--repo",
54
+ "deftai/directive",
55
+ "--allow-vbrief-drift",
56
+ "--skip-ci",
57
+ ],
58
+ setup(root) {
59
+ writeFileSync(join(root, "CHANGELOG.md"), SAMPLE_CHANGELOG, "utf8");
60
+ execFileSync("git", ["init", "-q", "-b", "master", root]);
61
+ execFileSync("git", ["config", "user.email", "parity@test.local"], { cwd: root });
62
+ execFileSync("git", ["config", "user.name", "deft-parity"], { cwd: root });
63
+ execFileSync("git", ["add", "CHANGELOG.md"], { cwd: root });
64
+ execFileSync("git", ["commit", "-q", "-m", "init"], {
65
+ cwd: root,
66
+ env: {
67
+ ...process.env,
68
+ GIT_AUTHOR_NAME: "deft-parity",
69
+ GIT_AUTHOR_EMAIL: "parity@test.local",
70
+ GIT_COMMITTER_NAME: "deft-parity",
71
+ GIT_COMMITTER_EMAIL: "parity@test.local",
72
+ },
73
+ });
74
+ },
75
+ },
76
+ { name: "help", argv: ["--help"], compareStdout: true },
77
+ ];
78
+ function runCapture(cmd, args, cwd, env = {}) {
79
+ const merged = {
80
+ ...process.env,
81
+ ...env,
82
+ DEFT_CACHE_DISABLE: "1",
83
+ PYTHONUTF8: "1",
84
+ };
85
+ for (const key of Object.keys(merged)) {
86
+ if (merged[key] === undefined)
87
+ delete merged[key];
88
+ }
89
+ try {
90
+ const result = spawnSync(cmd, args, {
91
+ cwd,
92
+ encoding: "utf8",
93
+ env: merged,
94
+ stdio: ["ignore", "pipe", "pipe"],
95
+ });
96
+ return {
97
+ status: result.status ?? 2,
98
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
99
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
100
+ };
101
+ }
102
+ catch (err) {
103
+ const e = err;
104
+ return {
105
+ status: typeof e.status === "number" ? e.status : 2,
106
+ stdout: typeof e.stdout === "string" ? e.stdout : "",
107
+ stderr: typeof e.stderr === "string" ? e.stderr : "",
108
+ };
109
+ }
110
+ }
111
+ function resolveDeftRoot() {
112
+ if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
113
+ return resolve(process.env.DEFT_ROOT);
114
+ }
115
+ return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
116
+ }
117
+ /** Normalise volatile ISO dates in stderr while preserving semantics. */
118
+ export function normaliseStderr(text) {
119
+ return text.replace(/\d{4}-\d{2}-\d{2}/g, "YYYY-MM-DD");
120
+ }
121
+ export function pickOutput(result, stream) {
122
+ return stream === "stdout" ? result.stdout : result.stderr;
123
+ }
124
+ function runScenario(deftRoot, scenario) {
125
+ let cwd = deftRoot;
126
+ let tempRoot;
127
+ let projectRoot;
128
+ const argv = [...scenario.argv];
129
+ if (scenario.setup) {
130
+ tempRoot = mkdtempSync(join(tmpdir(), "deft-release-parity-"));
131
+ projectRoot = tempRoot;
132
+ scenario.setup(tempRoot);
133
+ const idx = argv.indexOf("--project-root");
134
+ if (idx === -1) {
135
+ argv.push("--project-root", tempRoot);
136
+ }
137
+ cwd = deftRoot;
138
+ }
139
+ try {
140
+ const pyArgs = ["run", "python", join(deftRoot, "scripts", "release.py"), ...argv];
141
+ const tsArgs = [join(deftRoot, "packages", "cli", "dist", "release.js"), ...argv];
142
+ const py = runCapture("uv", pyArgs, cwd);
143
+ const ts = runCapture("node", tsArgs, cwd);
144
+ return {
145
+ python: { name: scenario.name, exitCode: py.status, stdout: py.stdout, stderr: py.stderr },
146
+ ts: { name: scenario.name, exitCode: ts.status, stdout: ts.stdout, stderr: ts.stderr },
147
+ projectRoot,
148
+ };
149
+ }
150
+ finally {
151
+ if (tempRoot) {
152
+ rmSync(tempRoot, { recursive: true, force: true });
153
+ }
154
+ }
155
+ }
156
+ export function diffParity(python, ts, stream) {
157
+ let pythonOutput = pickOutput(python, stream);
158
+ let tsOutput = pickOutput(ts, stream);
159
+ if (stream === "stderr") {
160
+ pythonOutput = normaliseStderr(pythonOutput);
161
+ tsOutput = normaliseStderr(tsOutput);
162
+ }
163
+ return {
164
+ exitMismatch: python.exitCode !== ts.exitCode,
165
+ outputMismatch: pythonOutput !== tsOutput,
166
+ pythonOutput,
167
+ tsOutput,
168
+ };
169
+ }
170
+ export function runParity() {
171
+ const deftRoot = resolveDeftRoot();
172
+ const scenarios = [];
173
+ for (const scenario of PARITY_SCENARIOS) {
174
+ const ran = runScenario(deftRoot, scenario);
175
+ const stream = scenario.compareStdout ? "stdout" : "stderr";
176
+ scenarios.push({
177
+ name: scenario.name,
178
+ pythonExit: ran.python.exitCode,
179
+ tsExit: ran.ts.exitCode,
180
+ stream,
181
+ ...diffParity(ran.python, ran.ts, stream),
182
+ });
183
+ }
184
+ const ok = scenarios.every((s) => !s.exitMismatch && !s.outputMismatch);
185
+ return { ok, scenarios };
186
+ }
187
+ export function renderReport(result) {
188
+ if (result.ok) {
189
+ return `release parity: CLEAN -- Python and TS agree on ${result.scenarios.length} scenario(s).`;
190
+ }
191
+ const lines = ["release parity: DIVERGENCE"];
192
+ for (const s of result.scenarios) {
193
+ if (s.exitMismatch || s.outputMismatch) {
194
+ lines.push(` scenario: ${s.name}`);
195
+ if (s.exitMismatch) {
196
+ lines.push(` exit mismatch: python=${s.pythonExit} ts=${s.tsExit}`);
197
+ }
198
+ if (s.outputMismatch) {
199
+ lines.push(` stream: ${s.stream}`);
200
+ lines.push(` python (${s.pythonOutput.length} bytes)`);
201
+ lines.push(` ts (${s.tsOutput.length} bytes)`);
202
+ }
203
+ }
204
+ }
205
+ return lines.join("\n");
206
+ }
207
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
208
+ try {
209
+ const result = runParity();
210
+ if (result.ok) {
211
+ process.stdout.write(`${renderReport(result)}\n`);
212
+ process.exit(0);
213
+ }
214
+ process.stderr.write(`${renderReport(result)}\n`);
215
+ process.exit(1);
216
+ }
217
+ catch (err) {
218
+ const msg = String(err).replace(/\r?\n/g, " ");
219
+ process.stderr.write(`release parity: harness error -- ${msg}\n`);
220
+ process.exit(2);
221
+ }
222
+ }
223
+ //# sourceMappingURL=release-parity.js.map
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ export interface ScenarioResult {
3
+ readonly name: string;
4
+ readonly exitCode: number;
5
+ readonly stdout: string;
6
+ readonly stderr: string;
7
+ }
8
+ export interface ParityScenario {
9
+ readonly name: string;
10
+ readonly argv: readonly string[];
11
+ readonly compareStdout?: boolean;
12
+ }
13
+ export interface ParityResult {
14
+ readonly ok: boolean;
15
+ readonly scenarios: Array<{
16
+ readonly name: string;
17
+ readonly exitMismatch: boolean;
18
+ readonly pythonExit: number;
19
+ readonly tsExit: number;
20
+ readonly outputMismatch: boolean;
21
+ readonly pythonOutput: string;
22
+ readonly tsOutput: string;
23
+ readonly stream: "stdout" | "stderr";
24
+ }>;
25
+ }
26
+ export declare const PARITY_SCENARIOS: readonly ParityScenario[];
27
+ export declare function pickOutput(result: ScenarioResult, stream: "stdout" | "stderr"): string;
28
+ export declare function diffParity(python: ScenarioResult, ts: ScenarioResult, stream: "stdout" | "stderr"): {
29
+ exitMismatch: boolean;
30
+ outputMismatch: boolean;
31
+ pythonOutput: string;
32
+ tsOutput: string;
33
+ };
34
+ export declare function runParity(): ParityResult;
35
+ export declare function renderReport(result: ParityResult): string;
36
+ //# sourceMappingURL=release-publish-parity.d.ts.map
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Golden-output parity harness (#1729): runs BOTH the Python oracle
4
+ * (`scripts/release_publish.py`) and the ported TS release:publish CLI
5
+ * with identical argv, then diffs exit codes and normalised stderr/stdout.
6
+ * Exit 0 only on byte-identical results (cache off).
7
+ *
8
+ * Exit codes: 0 parity / 1 divergence / 2 harness setup error.
9
+ */
10
+ import { spawnSync } from "node:child_process";
11
+ import { dirname, join, resolve } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ export const PARITY_SCENARIOS = [
14
+ { name: "invalid-version", argv: ["not-a-version"] },
15
+ { name: "missing-version", argv: [] },
16
+ {
17
+ name: "dry-run",
18
+ argv: ["0.21.0", "--dry-run", "--repo", "deftai/directive"],
19
+ },
20
+ { name: "help", argv: ["--help"], compareStdout: true },
21
+ ];
22
+ function runCapture(cmd, args, cwd, env = {}) {
23
+ const merged = {
24
+ ...process.env,
25
+ ...env,
26
+ DEFT_CACHE_DISABLE: "1",
27
+ PYTHONUTF8: "1",
28
+ };
29
+ for (const key of Object.keys(merged)) {
30
+ if (merged[key] === undefined)
31
+ delete merged[key];
32
+ }
33
+ try {
34
+ const result = spawnSync(cmd, args, {
35
+ cwd,
36
+ encoding: "utf8",
37
+ env: merged,
38
+ stdio: ["ignore", "pipe", "pipe"],
39
+ });
40
+ return {
41
+ status: result.status ?? 2,
42
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
43
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
44
+ };
45
+ }
46
+ catch (err) {
47
+ const e = err;
48
+ return {
49
+ status: typeof e.status === "number" ? e.status : 2,
50
+ stdout: typeof e.stdout === "string" ? e.stdout : "",
51
+ stderr: typeof e.stderr === "string" ? e.stderr : "",
52
+ };
53
+ }
54
+ }
55
+ function resolveDeftRoot() {
56
+ if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
57
+ return resolve(process.env.DEFT_ROOT);
58
+ }
59
+ return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
60
+ }
61
+ export function pickOutput(result, stream) {
62
+ return stream === "stdout" ? result.stdout : result.stderr;
63
+ }
64
+ function runScenario(deftRoot, scenario) {
65
+ const argv = [...scenario.argv];
66
+ const pyArgs = ["run", "python", join(deftRoot, "scripts", "release_publish.py"), ...argv];
67
+ const tsArgs = [join(deftRoot, "packages", "cli", "dist", "release-publish.js"), ...argv];
68
+ const py = runCapture("uv", pyArgs, deftRoot);
69
+ const ts = runCapture("node", tsArgs, deftRoot);
70
+ return {
71
+ python: { name: scenario.name, exitCode: py.status, stdout: py.stdout, stderr: py.stderr },
72
+ ts: { name: scenario.name, exitCode: ts.status, stdout: ts.stdout, stderr: ts.stderr },
73
+ };
74
+ }
75
+ export function diffParity(python, ts, stream) {
76
+ const pythonOutput = pickOutput(python, stream);
77
+ const tsOutput = pickOutput(ts, stream);
78
+ return {
79
+ exitMismatch: python.exitCode !== ts.exitCode,
80
+ outputMismatch: pythonOutput !== tsOutput,
81
+ pythonOutput,
82
+ tsOutput,
83
+ };
84
+ }
85
+ export function runParity() {
86
+ const deftRoot = resolveDeftRoot();
87
+ const scenarios = [];
88
+ for (const scenario of PARITY_SCENARIOS) {
89
+ const ran = runScenario(deftRoot, scenario);
90
+ const stream = scenario.compareStdout ? "stdout" : "stderr";
91
+ scenarios.push({
92
+ name: scenario.name,
93
+ pythonExit: ran.python.exitCode,
94
+ tsExit: ran.ts.exitCode,
95
+ stream,
96
+ ...diffParity(ran.python, ran.ts, stream),
97
+ });
98
+ }
99
+ const ok = scenarios.every((s) => !s.exitMismatch && !s.outputMismatch);
100
+ return { ok, scenarios };
101
+ }
102
+ export function renderReport(result) {
103
+ if (result.ok) {
104
+ return `release-publish parity: CLEAN -- Python and TS agree on ${result.scenarios.length} scenario(s).`;
105
+ }
106
+ const lines = ["release-publish parity: DIVERGENCE"];
107
+ for (const s of result.scenarios) {
108
+ if (s.exitMismatch || s.outputMismatch) {
109
+ lines.push(` scenario: ${s.name}`);
110
+ if (s.exitMismatch) {
111
+ lines.push(` exit mismatch: python=${s.pythonExit} ts=${s.tsExit}`);
112
+ }
113
+ if (s.outputMismatch) {
114
+ lines.push(` stream: ${s.stream}`);
115
+ lines.push(` python (${s.pythonOutput.length} bytes)`);
116
+ lines.push(` ts (${s.tsOutput.length} bytes)`);
117
+ }
118
+ }
119
+ }
120
+ return lines.join("\n");
121
+ }
122
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
123
+ try {
124
+ const result = runParity();
125
+ if (result.ok) {
126
+ process.stdout.write(`${renderReport(result)}\n`);
127
+ process.exit(0);
128
+ }
129
+ process.stderr.write(`${renderReport(result)}\n`);
130
+ process.exit(1);
131
+ }
132
+ catch (err) {
133
+ const msg = String(err).replace(/\r?\n/g, " ");
134
+ process.stderr.write(`release-publish parity: harness error -- ${msg}\n`);
135
+ process.exit(2);
136
+ }
137
+ }
138
+ //# sourceMappingURL=release-publish-parity.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function run(argv: string[]): number;
3
+ //# sourceMappingURL=release-publish.d.ts.map
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { cmdReleasePublish } from "../../core/dist/release-publish/main.js";
4
+ export function run(argv) {
5
+ return cmdReleasePublish(argv);
6
+ }
7
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
8
+ process.exit(run(process.argv.slice(2)));
9
+ }
10
+ //# sourceMappingURL=release-publish.js.map
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ export interface ScenarioResult {
3
+ readonly name: string;
4
+ readonly exitCode: number;
5
+ readonly stdout: string;
6
+ readonly stderr: string;
7
+ }
8
+ export interface ParityScenario {
9
+ readonly name: string;
10
+ readonly argv: readonly string[];
11
+ readonly compareStdout?: boolean;
12
+ }
13
+ export interface ParityResult {
14
+ readonly ok: boolean;
15
+ readonly scenarios: Array<{
16
+ readonly name: string;
17
+ readonly exitMismatch: boolean;
18
+ readonly pythonExit: number;
19
+ readonly tsExit: number;
20
+ readonly outputMismatch: boolean;
21
+ readonly pythonOutput: string;
22
+ readonly tsOutput: string;
23
+ readonly stream: "stdout" | "stderr";
24
+ }>;
25
+ }
26
+ export declare const PARITY_SCENARIOS: readonly ParityScenario[];
27
+ export declare function normaliseStderr(text: string): string;
28
+ export declare function pickOutput(result: ScenarioResult, stream: "stdout" | "stderr"): string;
29
+ export declare function diffParity(python: ScenarioResult, ts: ScenarioResult, stream: "stdout" | "stderr"): {
30
+ exitMismatch: boolean;
31
+ outputMismatch: boolean;
32
+ pythonOutput: string;
33
+ tsOutput: string;
34
+ };
35
+ export declare function runParity(): ParityResult;
36
+ export declare function renderReport(result: ParityResult): string;
37
+ //# sourceMappingURL=release-rollback-parity.d.ts.map
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Golden-output parity harness (#1729): runs BOTH the Python oracle
4
+ * (`scripts/release_rollback.py`) and the ported TS release:rollback CLI
5
+ * with identical argv, then diffs exit codes and normalised stderr/stdout.
6
+ *
7
+ * Exit codes: 0 parity / 1 divergence / 2 harness setup error.
8
+ */
9
+ import { spawnSync } from "node:child_process";
10
+ import { dirname, join, resolve } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ export const PARITY_SCENARIOS = [
13
+ { name: "invalid-version", argv: ["not-a-version"] },
14
+ {
15
+ name: "negative-allow-low-downloads",
16
+ argv: ["0.21.0", "--allow-low-downloads", "-1"],
17
+ },
18
+ {
19
+ name: "dry-run-absent",
20
+ argv: ["0.99.0", "--dry-run", "--repo", "deftai/directive"],
21
+ },
22
+ {
23
+ name: "allow-low-downloads-missing-value",
24
+ argv: ["0.21.0", "--allow-low-downloads", "--dry-run"],
25
+ },
26
+ { name: "help", argv: ["--help"], compareStdout: true },
27
+ ];
28
+ function runCapture(cmd, args, cwd, env = {}) {
29
+ const merged = {
30
+ ...process.env,
31
+ ...env,
32
+ DEFT_CACHE_DISABLE: "1",
33
+ PYTHONUTF8: "1",
34
+ };
35
+ for (const key of Object.keys(merged)) {
36
+ if (merged[key] === undefined)
37
+ delete merged[key];
38
+ }
39
+ try {
40
+ const result = spawnSync(cmd, args, {
41
+ cwd,
42
+ encoding: "utf8",
43
+ env: merged,
44
+ stdio: ["ignore", "pipe", "pipe"],
45
+ });
46
+ return {
47
+ status: result.status ?? 2,
48
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
49
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
50
+ };
51
+ }
52
+ catch (err) {
53
+ const e = err;
54
+ return {
55
+ status: typeof e.status === "number" ? e.status : 2,
56
+ stdout: typeof e.stdout === "string" ? e.stdout : "",
57
+ stderr: typeof e.stderr === "string" ? e.stderr : "",
58
+ };
59
+ }
60
+ }
61
+ function resolveDeftRoot() {
62
+ if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
63
+ return resolve(process.env.DEFT_ROOT);
64
+ }
65
+ return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
66
+ }
67
+ export function normaliseStderr(text) {
68
+ return text.replace(/\d{4}-\d{2}-\d{2}/g, "YYYY-MM-DD");
69
+ }
70
+ export function pickOutput(result, stream) {
71
+ return stream === "stdout" ? result.stdout : result.stderr;
72
+ }
73
+ function runScenario(deftRoot, scenario) {
74
+ const argv = [...scenario.argv];
75
+ const pyArgs = ["run", "python", join(deftRoot, "scripts", "release_rollback.py"), ...argv];
76
+ const tsArgs = [join(deftRoot, "packages", "cli", "dist", "release-rollback.js"), ...argv];
77
+ const py = runCapture("uv", pyArgs, deftRoot);
78
+ const ts = runCapture("node", tsArgs, deftRoot);
79
+ return {
80
+ python: {
81
+ name: scenario.name,
82
+ exitCode: py.status,
83
+ stdout: py.stdout,
84
+ stderr: py.stderr,
85
+ },
86
+ ts: {
87
+ name: scenario.name,
88
+ exitCode: ts.status,
89
+ stdout: ts.stdout,
90
+ stderr: ts.stderr,
91
+ },
92
+ };
93
+ }
94
+ export function diffParity(python, ts, stream) {
95
+ let pythonOutput = pickOutput(python, stream);
96
+ let tsOutput = pickOutput(ts, stream);
97
+ if (stream === "stderr") {
98
+ pythonOutput = normaliseStderr(pythonOutput);
99
+ tsOutput = normaliseStderr(tsOutput);
100
+ }
101
+ return {
102
+ exitMismatch: python.exitCode !== ts.exitCode,
103
+ outputMismatch: pythonOutput !== tsOutput,
104
+ pythonOutput,
105
+ tsOutput,
106
+ };
107
+ }
108
+ export function runParity() {
109
+ const deftRoot = resolveDeftRoot();
110
+ const scenarios = [];
111
+ for (const scenario of PARITY_SCENARIOS) {
112
+ const ran = runScenario(deftRoot, scenario);
113
+ const stream = scenario.compareStdout ? "stdout" : "stderr";
114
+ scenarios.push({
115
+ name: scenario.name,
116
+ pythonExit: ran.python.exitCode,
117
+ tsExit: ran.ts.exitCode,
118
+ stream,
119
+ ...diffParity(ran.python, ran.ts, stream),
120
+ });
121
+ }
122
+ const ok = scenarios.every((s) => !s.exitMismatch && !s.outputMismatch);
123
+ return { ok, scenarios };
124
+ }
125
+ export function renderReport(result) {
126
+ if (result.ok) {
127
+ return `release-rollback parity: CLEAN -- Python and TS agree on ${result.scenarios.length} scenario(s).`;
128
+ }
129
+ const lines = ["release-rollback parity: DIVERGENCE"];
130
+ for (const s of result.scenarios) {
131
+ if (s.exitMismatch || s.outputMismatch) {
132
+ lines.push(` scenario: ${s.name}`);
133
+ if (s.exitMismatch) {
134
+ lines.push(` exit mismatch: python=${s.pythonExit} ts=${s.tsExit}`);
135
+ }
136
+ if (s.outputMismatch) {
137
+ lines.push(` stream: ${s.stream}`);
138
+ lines.push(` python (${s.pythonOutput.length} bytes)`);
139
+ lines.push(` ts (${s.tsOutput.length} bytes)`);
140
+ }
141
+ }
142
+ }
143
+ return lines.join("\n");
144
+ }
145
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
146
+ try {
147
+ const result = runParity();
148
+ if (result.ok) {
149
+ process.stdout.write(`${renderReport(result)}\n`);
150
+ process.exit(0);
151
+ }
152
+ process.stderr.write(`${renderReport(result)}\n`);
153
+ process.exit(1);
154
+ }
155
+ catch (err) {
156
+ const msg = String(err).replace(/\r?\n/g, " ");
157
+ process.stderr.write(`release-rollback parity: harness error -- ${msg}\n`);
158
+ process.exit(2);
159
+ }
160
+ }
161
+ //# sourceMappingURL=release-rollback-parity.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function run(argv: string[]): number;
3
+ //# sourceMappingURL=release-rollback.d.ts.map
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { cmdRollback } from "../../core/dist/release-rollback/main.js";
4
+ export function run(argv) {
5
+ return cmdRollback(argv);
6
+ }
7
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
8
+ process.exit(run(process.argv.slice(2)));
9
+ }
10
+ //# sourceMappingURL=release-rollback.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function run(argv: string[]): number;
3
+ //# sourceMappingURL=release.d.ts.map
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { cmdRelease } from "../../core/dist/release/main.js";
4
+ export function run(argv) {
5
+ return cmdRelease(argv);
6
+ }
7
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
8
+ process.exit(run(process.argv.slice(2)));
9
+ }
10
+ //# sourceMappingURL=release.js.map
@@ -0,0 +1,20 @@
1
+ /** Repo root (four levels up from packages/cli/src/render-cli). */
2
+ export declare function resolveRepoRoot(): string;
3
+ /** Built deft-ts dispatcher entrypoint. */
4
+ export declare function resolveBinPath(): string;
5
+ export interface DeftTsResult {
6
+ readonly exitCode: number;
7
+ readonly stdout: string;
8
+ readonly stderr: string;
9
+ }
10
+ /** Invoke `node packages/cli/dist/bin.js [...argv]` and capture output. */
11
+ export declare function runDeftTsArgv(argv: readonly string[], options?: {
12
+ cwd?: string;
13
+ env?: Record<string, string>;
14
+ }): DeftTsResult;
15
+ /** Invoke `node packages/cli/dist/bin.js <verb> [...args]` and capture output. */
16
+ export declare function runDeftTs(verb: string, args?: readonly string[], options?: {
17
+ cwd?: string;
18
+ env?: Record<string, string>;
19
+ }): DeftTsResult;
20
+ //# sourceMappingURL=deft-ts-runner.d.ts.map