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