@deftai/directive 0.62.0 → 0.63.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 (97) hide show
  1. package/dist/{branch-parity.d.ts → branch-fixtures.d.ts} +1 -3
  2. package/dist/{branch-parity.js → branch-fixtures.js} +3 -110
  3. package/dist/dispatch.d.ts +1 -1
  4. package/dist/dispatch.js +0 -1
  5. package/dist/orchestration-cli/coverage-map.js +1 -1
  6. package/dist/{policy-parity.d.ts → policy-fixtures.d.ts} +1 -3
  7. package/dist/{policy-parity.js → policy-fixtures.js} +4 -100
  8. package/dist/{release-e2e-parity.d.ts → release-e2e-fixtures.d.ts} +1 -3
  9. package/dist/release-e2e-fixtures.js +38 -0
  10. package/dist/{story-ready-parity.d.ts → story-ready-fixtures.d.ts} +1 -3
  11. package/dist/{story-ready-parity.js → story-ready-fixtures.js} +4 -121
  12. package/dist/{triage-aux-a-parity.d.ts → triage-aux-a-fixtures.d.ts} +1 -3
  13. package/dist/{triage-aux-a-parity.js → triage-aux-a-fixtures.js} +3 -73
  14. package/dist/{triage-aux-b-parity.d.ts → triage-aux-b-fixtures.d.ts} +1 -3
  15. package/dist/triage-aux-b-fixtures.js +167 -0
  16. package/dist/{triage-bootstrap-parity.d.ts → triage-bootstrap-fixtures.d.ts} +1 -3
  17. package/dist/{triage-bootstrap-parity.js → triage-bootstrap-fixtures.js} +4 -91
  18. package/dist/{triage-classify-parity.d.ts → triage-classify-fixtures.d.ts} +1 -3
  19. package/dist/{triage-classify-parity.js → triage-classify-fixtures.js} +4 -94
  20. package/dist/{triage-queue-parity.d.ts → triage-queue-fixtures.d.ts} +1 -3
  21. package/dist/{triage-queue-parity.js → triage-queue-fixtures.js} +4 -86
  22. package/dist/{triage-scope-parity.d.ts → triage-scope-fixtures.d.ts} +1 -3
  23. package/dist/{triage-scope-parity.js → triage-scope-fixtures.js} +4 -91
  24. package/dist/{vbrief-preflight-parity.d.ts → vbrief-preflight-fixtures.d.ts} +1 -3
  25. package/dist/vbrief-preflight-fixtures.js +79 -0
  26. package/dist/{wip-cap-parity.d.ts → wip-cap-fixtures.d.ts} +1 -3
  27. package/dist/{wip-cap-parity.js → wip-cap-fixtures.js} +4 -91
  28. package/package.json +4 -15
  29. package/dist/cache-parity.d.ts +0 -36
  30. package/dist/cache-parity.js +0 -165
  31. package/dist/codebase-parity.d.ts +0 -31
  32. package/dist/codebase-parity.js +0 -303
  33. package/dist/doc-cli-parity.d.ts +0 -29
  34. package/dist/doc-cli-parity.js +0 -159
  35. package/dist/doctor-parity.d.ts +0 -42
  36. package/dist/doctor-parity.js +0 -157
  37. package/dist/intake-parity.d.ts +0 -30
  38. package/dist/intake-parity.js +0 -203
  39. package/dist/lifecycle-packs-parity.d.ts +0 -30
  40. package/dist/lifecycle-packs-parity.js +0 -377
  41. package/dist/orchestration-parity.d.ts +0 -38
  42. package/dist/orchestration-parity.js +0 -364
  43. package/dist/parity.d.ts +0 -36
  44. package/dist/parity.js +0 -176
  45. package/dist/platform-parity.d.ts +0 -26
  46. package/dist/platform-parity.js +0 -309
  47. package/dist/pr-closing-keywords-parity.d.ts +0 -45
  48. package/dist/pr-closing-keywords-parity.js +0 -259
  49. package/dist/pr-merge-readiness-parity.d.ts +0 -44
  50. package/dist/pr-merge-readiness-parity.js +0 -296
  51. package/dist/pr-monitor-parity.d.ts +0 -44
  52. package/dist/pr-monitor-parity.js +0 -283
  53. package/dist/pr-protected-issues-parity.d.ts +0 -41
  54. package/dist/pr-protected-issues-parity.js +0 -220
  55. package/dist/pr-wait-mergeable-parity.d.ts +0 -45
  56. package/dist/pr-wait-mergeable-parity.js +0 -340
  57. package/dist/release-e2e-parity.js +0 -114
  58. package/dist/release-parity.d.ts +0 -40
  59. package/dist/release-parity.js +0 -226
  60. package/dist/release-publish-parity.d.ts +0 -36
  61. package/dist/release-publish-parity.js +0 -138
  62. package/dist/release-rollback-parity.d.ts +0 -37
  63. package/dist/release-rollback-parity.js +0 -161
  64. package/dist/render-parity.d.ts +0 -36
  65. package/dist/render-parity.js +0 -385
  66. package/dist/scm-parity.d.ts +0 -39
  67. package/dist/scm-parity.js +0 -181
  68. package/dist/scope-lifecycle-parity.d.ts +0 -35
  69. package/dist/scope-lifecycle-parity.js +0 -177
  70. package/dist/session-parity.d.ts +0 -39
  71. package/dist/session-parity.js +0 -262
  72. package/dist/slice-parity.d.ts +0 -36
  73. package/dist/slice-parity.js +0 -304
  74. package/dist/swarm-parity.d.ts +0 -28
  75. package/dist/swarm-parity.js +0 -327
  76. package/dist/triage-actions-parity.d.ts +0 -36
  77. package/dist/triage-actions-parity.js +0 -357
  78. package/dist/triage-aux-b-parity.js +0 -308
  79. package/dist/triage-summary-parity.d.ts +0 -50
  80. package/dist/triage-summary-parity.js +0 -306
  81. package/dist/validate-content-parity.d.ts +0 -33
  82. package/dist/validate-content-parity.js +0 -356
  83. package/dist/vbrief-activate-parity.d.ts +0 -39
  84. package/dist/vbrief-activate-parity.js +0 -216
  85. package/dist/vbrief-build-parity.d.ts +0 -28
  86. package/dist/vbrief-build-parity.js +0 -399
  87. package/dist/vbrief-preflight-parity.js +0 -163
  88. package/dist/vbrief-reconcile-parity.d.ts +0 -23
  89. package/dist/vbrief-reconcile-parity.js +0 -609
  90. package/dist/vbrief-validate-parity.d.ts +0 -27
  91. package/dist/vbrief-validate-parity.js +0 -122
  92. package/dist/vbrief-validation-parity.d.ts +0 -28
  93. package/dist/vbrief-validation-parity.js +0 -645
  94. package/dist/verify-env-parity.d.ts +0 -28
  95. package/dist/verify-env-parity.js +0 -272
  96. package/dist/verify-source-parity.d.ts +0 -26
  97. package/dist/verify-source-parity.js +0 -178
@@ -1,296 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Golden-output parity harness (#1730): runs BOTH the Python oracle
4
- * (`scripts/pr_merge_readiness.py`) and the ported TS CLI with a fake `gh`
5
- * on PATH (cache-off), then diffs exit codes and byte-identical stdout/stderr.
6
- *
7
- * Exit codes: 0 parity / 1 divergence / 2 harness setup error.
8
- */
9
- import { spawnSync } from "node:child_process";
10
- import { chmodSync, 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
- const HEAD_SHA = "abc1234567890def1234567890abcdef12345678";
15
- const FAKE_GH_PY = `import json
16
- import os
17
- import sys
18
-
19
- def classify(cmd):
20
- joined = " ".join(cmd)
21
- if "nameWithOwner" in joined:
22
- return "repo-view"
23
- if "headRefOid" in joined:
24
- return "head-sha"
25
- if "/check-runs" in joined:
26
- return "check-runs"
27
- if "/pulls/" in joined and "/comments" not in joined:
28
- return "pr-view-rest"
29
- if "/issues/" in joined and "/comments" in joined and "--jq" in cmd:
30
- return "comments-jq"
31
- if "/issues/" in joined and "/comments" in joined:
32
- return "comments-rest"
33
- return "unknown"
34
-
35
- responses = json.loads(os.environ.get("DEFT_FAKE_GH_RESPONSES", "{}"))
36
- label = classify(sys.argv[1:])
37
- resp = responses.get(label, {"returncode": 1, "stderr": f"unexpected gh call: {label}", "stdout": ""})
38
- stdout = resp.get("stdout", "")
39
- stderr = resp.get("stderr", "")
40
- if stdout:
41
- sys.stdout.write(stdout)
42
- if stderr:
43
- sys.stderr.write(stderr)
44
- sys.exit(int(resp.get("returncode", 0)))
45
- `;
46
- function cleanJqBody(sha = HEAD_SHA, confidence = 5, extra = "") {
47
- return ("## Greptile Summary\n\n" +
48
- "No P0 or P1 issues found in this PR.\n\n" +
49
- `**Confidence Score: ${confidence}/5**\n\n` +
50
- "Last reviewed commit: [chore: small fix]" +
51
- `(https://github.com/deftai/directive/commit/${sha})\n` +
52
- extra);
53
- }
54
- function greptileRestPayload(sha = HEAD_SHA, confidence = 5, bodyExtra = "") {
55
- return JSON.stringify([
56
- { user: { login: "greptile-apps[bot]" }, body: cleanJqBody(sha, confidence, bodyExtra) },
57
- { user: { login: "human-reviewer" }, body: "LGTM" },
58
- ]);
59
- }
60
- function prRestPayload(sha = HEAD_SHA, state = "open", merged = false) {
61
- return JSON.stringify({
62
- state,
63
- merged,
64
- mergeable: true,
65
- mergeable_state: "clean",
66
- head: { sha, ref: "fix/foo" },
67
- });
68
- }
69
- function checkRunsPayload() {
70
- return JSON.stringify({
71
- total_count: 2,
72
- check_runs: [
73
- { name: "Greptile Review", status: "completed", conclusion: "success" },
74
- { name: "CI / build", status: "completed", conclusion: "success" },
75
- ],
76
- });
77
- }
78
- export const PARITY_SCENARIOS = [
79
- {
80
- name: "primary-clean-json",
81
- argv: ["1363", "--repo", "deftai/directive", "--json"],
82
- responses: {
83
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
84
- "comments-jq": { returncode: 0, stdout: cleanJqBody() },
85
- },
86
- },
87
- {
88
- name: "primary-blocked-confidence-json",
89
- argv: ["1363", "--repo", "deftai/directive", "--json"],
90
- responses: {
91
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
92
- "comments-jq": { returncode: 0, stdout: cleanJqBody(HEAD_SHA, 3) },
93
- },
94
- },
95
- {
96
- name: "fallback1-via-jq-fail-json",
97
- argv: ["1363", "--repo", "deftai/directive", "--json"],
98
- responses: {
99
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
100
- "comments-jq": { returncode: 1, stderr: "decode-crash" },
101
- "comments-rest": { returncode: 0, stdout: greptileRestPayload() },
102
- },
103
- },
104
- {
105
- name: "fallback2-never-clean-json",
106
- argv: ["1363", "--repo", "deftai/directive", "--json"],
107
- responses: {
108
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
109
- "comments-jq": { returncode: 1, stderr: "primary boom" },
110
- "comments-rest": { returncode: 1, stderr: "fallback1 boom" },
111
- "pr-view-rest": { returncode: 0, stdout: prRestPayload() },
112
- "check-runs": { returncode: 0, stdout: checkRunsPayload() },
113
- },
114
- },
115
- {
116
- name: "total-failure-error-json",
117
- argv: ["1363", "--repo", "deftai/directive", "--json"],
118
- responses: {
119
- "head-sha": { returncode: 1, stderr: "all-down" },
120
- "pr-view-rest": { returncode: 1, stderr: "all-down" },
121
- },
122
- },
123
- {
124
- name: "non-ascii-utf8-body-json",
125
- argv: ["1363", "--repo", "deftai/directive", "--json"],
126
- responses: {
127
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
128
- "comments-jq": {
129
- returncode: 0,
130
- stdout: cleanJqBody(HEAD_SHA, 5, "Review complete — no issues with smart “quotes”.\n"),
131
- },
132
- },
133
- },
134
- {
135
- name: "informal-clean-json",
136
- argv: ["1541", "--repo", "deftai/directive", "--json"],
137
- responses: {
138
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
139
- "comments-jq": {
140
- returncode: 0,
141
- stdout: "The review has completed — both previously flagged issues are now resolved.\n\n" +
142
- "The current diff is clean. No new issues to flag — looks solid. Good to proceed.\n",
143
- },
144
- },
145
- },
146
- {
147
- name: "primary-clean-human",
148
- argv: ["1363", "--repo", "deftai/directive"],
149
- responses: {
150
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
151
- "comments-jq": { returncode: 0, stdout: cleanJqBody() },
152
- },
153
- },
154
- ];
155
- function installFakeGh() {
156
- const binDir = mkdtempSync(join(tmpdir(), "deft-pr-merge-readiness-fake-gh-"));
157
- const ghBin = join(binDir, "gh");
158
- const ghxBin = join(binDir, "ghx");
159
- writeFileSync(ghBin, `#!/usr/bin/env python3\n${FAKE_GH_PY}`, "utf8");
160
- writeFileSync(ghxBin, `#!/usr/bin/env python3\n${FAKE_GH_PY}`, "utf8");
161
- chmodSync(ghBin, 0o755);
162
- chmodSync(ghxBin, 0o755);
163
- return {
164
- binDir,
165
- cleanup: () => {
166
- rmSync(binDir, { recursive: true, force: true });
167
- },
168
- };
169
- }
170
- function runCapture(cmd, args, cwd, env) {
171
- const result = spawnSync(cmd, args, {
172
- cwd,
173
- encoding: "utf8",
174
- env: { ...process.env, ...env },
175
- stdio: ["ignore", "pipe", "pipe"],
176
- });
177
- return {
178
- status: result.status ?? 2,
179
- stdout: typeof result.stdout === "string" ? result.stdout : "",
180
- stderr: typeof result.stderr === "string" ? result.stderr : "",
181
- };
182
- }
183
- function resolveDeftRoot() {
184
- if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
185
- return resolve(process.env.DEFT_ROOT);
186
- }
187
- return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
188
- }
189
- function normaliseHarnessNoise(text) {
190
- return text
191
- .split("\n")
192
- .filter((line) => !line.startsWith("Using CPython") &&
193
- !line.startsWith("Creating virtual environment") &&
194
- !line.startsWith("Installed "))
195
- .join("\n");
196
- }
197
- export function pickOutput(result, stream) {
198
- return stream === "stdout" ? result.stdout : result.stderr;
199
- }
200
- export function diffParity(python, ts, stream) {
201
- const pythonOutput = normaliseHarnessNoise(pickOutput(python, stream));
202
- const tsOutput = normaliseHarnessNoise(pickOutput(ts, stream));
203
- return {
204
- exitMismatch: python.exitCode !== ts.exitCode,
205
- outputMismatch: pythonOutput !== tsOutput,
206
- pythonOutput,
207
- tsOutput,
208
- };
209
- }
210
- function runScenario(deftRoot, scenario) {
211
- const fake = installFakeGh();
212
- try {
213
- const pathPrefix = `${fake.binDir}:${process.env.PATH ?? ""}`;
214
- const env = {
215
- DEFT_CACHE_DISABLE: "1",
216
- PYTHONUTF8: "1",
217
- PATH: pathPrefix,
218
- DEFT_FAKE_GH_RESPONSES: JSON.stringify(scenario.responses),
219
- };
220
- const py = runCapture("uv", ["run", "python", join(deftRoot, "scripts", "pr_merge_readiness.py"), ...scenario.argv], deftRoot, env);
221
- const ts = runCapture("node", [join(deftRoot, "packages", "cli", "dist", "pr-merge-readiness.js"), ...scenario.argv], deftRoot, env);
222
- return {
223
- python: {
224
- name: scenario.name,
225
- exitCode: py.status,
226
- stdout: py.stdout,
227
- stderr: py.stderr,
228
- },
229
- ts: {
230
- name: scenario.name,
231
- exitCode: ts.status,
232
- stdout: ts.stdout,
233
- stderr: ts.stderr,
234
- },
235
- };
236
- }
237
- finally {
238
- fake.cleanup();
239
- }
240
- }
241
- export function runParity() {
242
- const deftRoot = resolveDeftRoot();
243
- const scenarios = [];
244
- for (const scenario of PARITY_SCENARIOS) {
245
- const ran = runScenario(deftRoot, scenario);
246
- const stream = scenario.compareStream ?? "stdout";
247
- scenarios.push({
248
- name: scenario.name,
249
- pythonExit: ran.python.exitCode,
250
- tsExit: ran.ts.exitCode,
251
- stream,
252
- ...diffParity(ran.python, ran.ts, stream),
253
- });
254
- }
255
- const ok = scenarios.every((s) => !s.exitMismatch && !s.outputMismatch);
256
- return { ok, scenarios };
257
- }
258
- export function renderReport(result) {
259
- if (result.ok) {
260
- return `pr_merge_readiness parity: CLEAN -- Python and TS agree on ${result.scenarios.length} scenario(s).`;
261
- }
262
- const lines = ["pr_merge_readiness parity: DIVERGENCE"];
263
- for (const s of result.scenarios) {
264
- if (s.exitMismatch || s.outputMismatch) {
265
- lines.push(` scenario: ${s.name}`);
266
- if (s.exitMismatch) {
267
- lines.push(` exit mismatch: python=${s.pythonExit} ts=${s.tsExit}`);
268
- }
269
- if (s.outputMismatch) {
270
- lines.push(` stream: ${s.stream}`);
271
- lines.push(` python (${s.pythonOutput.length} bytes):`);
272
- lines.push(s.pythonOutput.slice(0, 500));
273
- lines.push(` ts (${s.tsOutput.length} bytes):`);
274
- lines.push(s.tsOutput.slice(0, 500));
275
- }
276
- }
277
- }
278
- return lines.join("\n");
279
- }
280
- if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
281
- try {
282
- const result = runParity();
283
- if (result.ok) {
284
- process.stdout.write(`${renderReport(result)}\n`);
285
- process.exit(0);
286
- }
287
- process.stderr.write(`${renderReport(result)}\n`);
288
- process.exit(1);
289
- }
290
- catch (err) {
291
- const msg = String(err).replace(/\r?\n/g, " ");
292
- process.stderr.write(`pr_merge_readiness parity: harness error -- ${msg}\n`);
293
- process.exit(2);
294
- }
295
- }
296
- //# sourceMappingURL=pr-merge-readiness-parity.js.map
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env node
2
- export interface FakeGhResponses {
3
- readonly [label: string]: {
4
- readonly returncode: number;
5
- readonly stdout?: string;
6
- readonly stderr?: string;
7
- };
8
- }
9
- export interface ParityScenario {
10
- readonly name: string;
11
- readonly argv: readonly string[];
12
- readonly responses: FakeGhResponses;
13
- }
14
- export interface ScenarioResult {
15
- readonly name: string;
16
- readonly exitCode: number;
17
- readonly stdout: string;
18
- readonly stderr: string;
19
- }
20
- export interface ParityResult {
21
- readonly ok: boolean;
22
- readonly scenarios: Array<{
23
- readonly name: string;
24
- readonly exitMismatch: boolean;
25
- readonly pythonExit: number;
26
- readonly tsExit: number;
27
- readonly stdoutMismatch: boolean;
28
- readonly stderrMismatch: boolean;
29
- readonly pythonStdout: string;
30
- readonly tsStdout: string;
31
- readonly pythonStderr: string;
32
- readonly tsStderr: string;
33
- }>;
34
- }
35
- export declare const PARITY_SCENARIOS: readonly ParityScenario[];
36
- export declare function normaliseHarnessNoise(text: string): string;
37
- export declare function diffParity(python: ScenarioResult, ts: ScenarioResult): {
38
- exitMismatch: boolean;
39
- stdoutMismatch: boolean;
40
- stderrMismatch: boolean;
41
- };
42
- export declare function runParity(): ParityResult;
43
- export declare function renderReport(result: ParityResult): string;
44
- //# sourceMappingURL=pr-monitor-parity.d.ts.map
@@ -1,283 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Golden-output parity harness (#1730): runs BOTH the Python oracle
4
- * (`scripts/monitor_pr.py`) and the ported TS CLI with a fake `gh`
5
- * on PATH (cache-off), then diffs exit codes and byte-identical stdout/stderr.
6
- *
7
- * Exit codes: 0 parity / 1 divergence / 2 harness setup error.
8
- */
9
- import { spawnSync } from "node:child_process";
10
- import { chmodSync, 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
- const HEAD_SHA = "abc1234567890def1234567890abcdef12345678";
15
- const FAKE_GH_PY = `import json
16
- import os
17
- import sys
18
-
19
- def classify(cmd):
20
- joined = " ".join(cmd)
21
- if "nameWithOwner" in joined:
22
- return "repo-view"
23
- if "headRefOid" in joined:
24
- return "head-sha"
25
- if "/check-runs" in joined:
26
- return "check-runs"
27
- if "/pulls/" in joined and "/comments" not in joined:
28
- return "pr-view-rest"
29
- if "/issues/" in joined and "/comments" in joined and "--jq" in cmd:
30
- return "comments-jq"
31
- if "/issues/" in joined and "/comments" in joined:
32
- return "comments-rest"
33
- return "unknown"
34
-
35
- responses = json.loads(os.environ.get("DEFT_FAKE_GH_RESPONSES", "{}"))
36
- label = classify(sys.argv[1:])
37
- resp = responses.get(label, {"returncode": 1, "stderr": f"unexpected gh call: {label}", "stdout": ""})
38
- stdout = resp.get("stdout", "")
39
- stderr = resp.get("stderr", "")
40
- if stdout:
41
- sys.stdout.write(stdout)
42
- if stderr:
43
- sys.stderr.write(stderr)
44
- sys.exit(int(resp.get("returncode", 0)))
45
- `;
46
- function cleanJqBody(sha = HEAD_SHA, confidence = 5) {
47
- return ("## Greptile Summary\n\n" +
48
- "No P0 or P1 issues found in this PR.\n\n" +
49
- `**Confidence Score: ${confidence}/5**\n\n` +
50
- "Last reviewed commit: [chore: small fix]" +
51
- `(https://github.com/deftai/directive/commit/${sha})\n`);
52
- }
53
- function greptileRestPayload(sha = HEAD_SHA, confidence = 5) {
54
- return JSON.stringify([
55
- { user: { login: "greptile-apps[bot]" }, body: cleanJqBody(sha, confidence) },
56
- { user: { login: "human-reviewer" }, body: "LGTM" },
57
- ]);
58
- }
59
- function prRestPayload(sha = HEAD_SHA, state = "open", merged = false) {
60
- return JSON.stringify({
61
- state,
62
- merged,
63
- mergeable: true,
64
- mergeable_state: "clean",
65
- head: { sha, ref: "fix/foo" },
66
- });
67
- }
68
- function checkRunsPayload() {
69
- return JSON.stringify({
70
- total_count: 2,
71
- check_runs: [
72
- { name: "Greptile Review", status: "completed", conclusion: "success" },
73
- { name: "CI / build", status: "completed", conclusion: "success" },
74
- ],
75
- });
76
- }
77
- export const PARITY_SCENARIOS = [
78
- {
79
- name: "clean-first-poll-json",
80
- argv: ["1363", "--repo", "deftai/directive", "--json", "--cap-minutes", "60"],
81
- responses: {
82
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
83
- "comments-jq": { returncode: 0, stdout: cleanJqBody() },
84
- },
85
- },
86
- {
87
- name: "clean-first-poll-human",
88
- argv: ["1363", "--repo", "deftai/directive", "--cap-minutes", "60"],
89
- responses: {
90
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
91
- "comments-jq": { returncode: 0, stdout: cleanJqBody() },
92
- },
93
- },
94
- {
95
- name: "transient-error-cap-zero-json",
96
- argv: ["1363", "--repo", "deftai/directive", "--json", "--cap-minutes", "0"],
97
- responses: {
98
- "head-sha": { returncode: 1, stderr: "all-down" },
99
- "pr-view-rest": { returncode: 1, stderr: "all-down" },
100
- },
101
- },
102
- {
103
- name: "terminal-merged-json",
104
- argv: ["1363", "--repo", "deftai/directive", "--json", "--cap-minutes", "60"],
105
- responses: {
106
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
107
- "comments-jq": { returncode: 1, stderr: "primary boom" },
108
- "comments-rest": { returncode: 1, stderr: "fallback1 boom" },
109
- "pr-view-rest": {
110
- returncode: 0,
111
- stdout: prRestPayload(HEAD_SHA, "closed", true),
112
- },
113
- "check-runs": { returncode: 0, stdout: checkRunsPayload() },
114
- },
115
- },
116
- {
117
- name: "fallback1-clean-json",
118
- argv: ["1363", "--repo", "deftai/directive", "--json", "--cap-minutes", "60"],
119
- responses: {
120
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
121
- "comments-jq": { returncode: 1, stderr: "decode-crash" },
122
- "comments-rest": { returncode: 0, stdout: greptileRestPayload() },
123
- },
124
- },
125
- {
126
- name: "blocked-cap-zero-human",
127
- argv: ["1363", "--repo", "deftai/directive", "--cap-minutes", "0"],
128
- responses: {
129
- "head-sha": { returncode: 0, stdout: `${HEAD_SHA}\n` },
130
- "comments-jq": { returncode: 0, stdout: cleanJqBody(HEAD_SHA, 3) },
131
- },
132
- },
133
- ];
134
- function installFakeGh() {
135
- const binDir = mkdtempSync(join(tmpdir(), "deft-pr-monitor-fake-gh-"));
136
- const ghBin = join(binDir, "gh");
137
- const ghxBin = join(binDir, "ghx");
138
- writeFileSync(ghBin, `#!/usr/bin/env python3\n${FAKE_GH_PY}`, "utf8");
139
- writeFileSync(ghxBin, `#!/usr/bin/env python3\n${FAKE_GH_PY}`, "utf8");
140
- chmodSync(ghBin, 0o755);
141
- chmodSync(ghxBin, 0o755);
142
- return {
143
- binDir,
144
- cleanup: () => {
145
- rmSync(binDir, { recursive: true, force: true });
146
- },
147
- };
148
- }
149
- function runCapture(cmd, args, cwd, env) {
150
- const result = spawnSync(cmd, args, {
151
- cwd,
152
- encoding: "utf8",
153
- env: { ...process.env, ...env },
154
- stdio: ["ignore", "pipe", "pipe"],
155
- });
156
- return {
157
- status: result.status ?? 2,
158
- stdout: typeof result.stdout === "string" ? result.stdout : "",
159
- stderr: typeof result.stderr === "string" ? result.stderr : "",
160
- };
161
- }
162
- function resolveDeftRoot() {
163
- if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
164
- return resolve(process.env.DEFT_ROOT);
165
- }
166
- return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
167
- }
168
- export function normaliseHarnessNoise(text) {
169
- return text
170
- .split("\n")
171
- .filter((line) => !line.startsWith("Using CPython") &&
172
- !line.startsWith("Creating virtual environment") &&
173
- !line.startsWith("Installed "))
174
- .join("\n");
175
- }
176
- export function diffParity(python, ts) {
177
- const pythonStdout = normaliseHarnessNoise(python.stdout);
178
- const tsStdout = normaliseHarnessNoise(ts.stdout);
179
- const pythonStderr = normaliseHarnessNoise(python.stderr);
180
- const tsStderr = normaliseHarnessNoise(ts.stderr);
181
- return {
182
- exitMismatch: python.exitCode !== ts.exitCode,
183
- stdoutMismatch: pythonStdout !== tsStdout,
184
- stderrMismatch: pythonStderr !== tsStderr,
185
- };
186
- }
187
- function runScenario(deftRoot, scenario) {
188
- const fake = installFakeGh();
189
- try {
190
- const pathPrefix = `${fake.binDir}:${process.env.PATH ?? ""}`;
191
- const env = {
192
- DEFT_CACHE_DISABLE: "1",
193
- PYTHONUTF8: "1",
194
- PATH: pathPrefix,
195
- DEFT_FAKE_GH_RESPONSES: JSON.stringify(scenario.responses),
196
- };
197
- const py = runCapture("uv", ["run", "python", join(deftRoot, "scripts", "monitor_pr.py"), ...scenario.argv], deftRoot, env);
198
- const ts = runCapture("node", [join(deftRoot, "packages", "cli", "dist", "pr-monitor.js"), ...scenario.argv], deftRoot, env);
199
- return {
200
- python: {
201
- name: scenario.name,
202
- exitCode: py.status,
203
- stdout: py.stdout,
204
- stderr: py.stderr,
205
- },
206
- ts: {
207
- name: scenario.name,
208
- exitCode: ts.status,
209
- stdout: ts.stdout,
210
- stderr: ts.stderr,
211
- },
212
- };
213
- }
214
- finally {
215
- fake.cleanup();
216
- }
217
- }
218
- export function runParity() {
219
- const deftRoot = resolveDeftRoot();
220
- const scenarios = [];
221
- for (const scenario of PARITY_SCENARIOS) {
222
- const ran = runScenario(deftRoot, scenario);
223
- const diff = diffParity(ran.python, ran.ts);
224
- scenarios.push({
225
- name: scenario.name,
226
- pythonExit: ran.python.exitCode,
227
- tsExit: ran.ts.exitCode,
228
- pythonStdout: normaliseHarnessNoise(ran.python.stdout),
229
- tsStdout: normaliseHarnessNoise(ran.ts.stdout),
230
- pythonStderr: normaliseHarnessNoise(ran.python.stderr),
231
- tsStderr: normaliseHarnessNoise(ran.ts.stderr),
232
- ...diff,
233
- });
234
- }
235
- const ok = scenarios.every((s) => !s.exitMismatch && !s.stdoutMismatch && !s.stderrMismatch);
236
- return { ok, scenarios };
237
- }
238
- export function renderReport(result) {
239
- if (result.ok) {
240
- return `monitor_pr parity: CLEAN -- Python and TS agree on ${result.scenarios.length} scenario(s).`;
241
- }
242
- const lines = ["monitor_pr parity: DIVERGENCE"];
243
- for (const s of result.scenarios) {
244
- if (s.exitMismatch || s.stdoutMismatch || s.stderrMismatch) {
245
- lines.push(` scenario: ${s.name}`);
246
- if (s.exitMismatch) {
247
- lines.push(` exit mismatch: python=${s.pythonExit} ts=${s.tsExit}`);
248
- }
249
- if (s.stdoutMismatch) {
250
- lines.push(` stdout mismatch:`);
251
- lines.push(` python (${s.pythonStdout.length} bytes):`);
252
- lines.push(s.pythonStdout.slice(0, 500));
253
- lines.push(` ts (${s.tsStdout.length} bytes):`);
254
- lines.push(s.tsStdout.slice(0, 500));
255
- }
256
- if (s.stderrMismatch) {
257
- lines.push(` stderr mismatch:`);
258
- lines.push(` python (${s.pythonStderr.length} bytes):`);
259
- lines.push(s.pythonStderr.slice(0, 500));
260
- lines.push(` ts (${s.tsStderr.length} bytes):`);
261
- lines.push(s.tsStderr.slice(0, 500));
262
- }
263
- }
264
- }
265
- return lines.join("\n");
266
- }
267
- if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
268
- try {
269
- const result = runParity();
270
- if (result.ok) {
271
- process.stdout.write(`${renderReport(result)}\n`);
272
- process.exit(0);
273
- }
274
- process.stderr.write(`${renderReport(result)}\n`);
275
- process.exit(1);
276
- }
277
- catch (err) {
278
- const msg = String(err).replace(/\r?\n/g, " ");
279
- process.stderr.write(`monitor_pr parity: harness error -- ${msg}\n`);
280
- process.exit(2);
281
- }
282
- }
283
- //# sourceMappingURL=pr-monitor-parity.js.map
@@ -1,41 +0,0 @@
1
- #!/usr/bin/env node
2
- export interface FakeGhResponses {
3
- readonly [label: string]: {
4
- readonly returncode: number;
5
- readonly stdout?: string;
6
- readonly stderr?: string;
7
- };
8
- }
9
- export interface ParityScenario {
10
- readonly name: string;
11
- readonly argv: readonly string[];
12
- readonly responses: FakeGhResponses;
13
- }
14
- export interface ScenarioResult {
15
- readonly name: string;
16
- readonly exitCode: number;
17
- readonly stdout: string;
18
- readonly stderr: string;
19
- }
20
- export interface ParityResult {
21
- readonly ok: boolean;
22
- readonly scenarios: Array<{
23
- readonly name: string;
24
- readonly exitMismatch: boolean;
25
- readonly pythonExit: number;
26
- readonly tsExit: number;
27
- readonly outputMismatch: boolean;
28
- readonly pythonOutput: string;
29
- readonly tsOutput: string;
30
- }>;
31
- }
32
- export declare const PARITY_SCENARIOS: readonly ParityScenario[];
33
- export declare function diffParity(python: ScenarioResult, ts: ScenarioResult): {
34
- exitMismatch: boolean;
35
- outputMismatch: boolean;
36
- pythonOutput: string;
37
- tsOutput: string;
38
- };
39
- export declare function runParity(): ParityResult;
40
- export declare function renderReport(result: ParityResult): string;
41
- //# sourceMappingURL=pr-protected-issues-parity.d.ts.map