@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.
- package/dist/{branch-parity.d.ts → branch-fixtures.d.ts} +1 -3
- package/dist/{branch-parity.js → branch-fixtures.js} +3 -110
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +4 -1
- package/dist/framework-check-updates.d.ts +10 -0
- package/dist/framework-check-updates.js +68 -0
- package/dist/install-cli/coverage-map.js +3 -2
- package/dist/orchestration-cli/coverage-map.js +1 -1
- package/dist/{policy-parity.d.ts → policy-fixtures.d.ts} +1 -3
- package/dist/{policy-parity.js → policy-fixtures.js} +4 -100
- package/dist/{release-e2e-parity.d.ts → release-e2e-fixtures.d.ts} +1 -3
- package/dist/release-e2e-fixtures.js +38 -0
- package/dist/{story-ready-parity.d.ts → story-ready-fixtures.d.ts} +1 -3
- package/dist/{story-ready-parity.js → story-ready-fixtures.js} +4 -121
- package/dist/{triage-aux-a-parity.d.ts → triage-aux-a-fixtures.d.ts} +1 -3
- package/dist/{triage-aux-a-parity.js → triage-aux-a-fixtures.js} +3 -73
- package/dist/{triage-aux-b-parity.d.ts → triage-aux-b-fixtures.d.ts} +1 -3
- package/dist/triage-aux-b-fixtures.js +167 -0
- package/dist/{triage-bootstrap-parity.d.ts → triage-bootstrap-fixtures.d.ts} +1 -3
- package/dist/{triage-bootstrap-parity.js → triage-bootstrap-fixtures.js} +4 -91
- package/dist/{triage-classify-parity.d.ts → triage-classify-fixtures.d.ts} +1 -3
- package/dist/{triage-classify-parity.js → triage-classify-fixtures.js} +4 -94
- package/dist/{triage-queue-parity.d.ts → triage-queue-fixtures.d.ts} +1 -3
- package/dist/{triage-queue-parity.js → triage-queue-fixtures.js} +4 -86
- package/dist/{triage-scope-parity.d.ts → triage-scope-fixtures.d.ts} +1 -3
- package/dist/{triage-scope-parity.js → triage-scope-fixtures.js} +4 -91
- package/dist/umbrella-current-shape.d.ts +9 -0
- package/dist/umbrella-current-shape.js +56 -0
- package/dist/{vbrief-preflight-parity.d.ts → vbrief-preflight-fixtures.d.ts} +1 -3
- package/dist/vbrief-preflight-fixtures.js +79 -0
- package/dist/{wip-cap-parity.d.ts → wip-cap-fixtures.d.ts} +1 -3
- package/dist/{wip-cap-parity.js → wip-cap-fixtures.js} +4 -91
- package/package.json +4 -15
- package/dist/cache-parity.d.ts +0 -36
- package/dist/cache-parity.js +0 -165
- package/dist/codebase-parity.d.ts +0 -31
- package/dist/codebase-parity.js +0 -303
- package/dist/doc-cli-parity.d.ts +0 -29
- package/dist/doc-cli-parity.js +0 -159
- package/dist/doctor-parity.d.ts +0 -42
- package/dist/doctor-parity.js +0 -157
- package/dist/intake-parity.d.ts +0 -30
- package/dist/intake-parity.js +0 -203
- package/dist/lifecycle-packs-parity.d.ts +0 -30
- package/dist/lifecycle-packs-parity.js +0 -377
- package/dist/orchestration-parity.d.ts +0 -38
- package/dist/orchestration-parity.js +0 -364
- package/dist/parity.d.ts +0 -36
- package/dist/parity.js +0 -176
- package/dist/platform-parity.d.ts +0 -26
- package/dist/platform-parity.js +0 -309
- package/dist/pr-closing-keywords-parity.d.ts +0 -45
- package/dist/pr-closing-keywords-parity.js +0 -259
- package/dist/pr-merge-readiness-parity.d.ts +0 -44
- package/dist/pr-merge-readiness-parity.js +0 -296
- package/dist/pr-monitor-parity.d.ts +0 -44
- package/dist/pr-monitor-parity.js +0 -283
- package/dist/pr-protected-issues-parity.d.ts +0 -41
- package/dist/pr-protected-issues-parity.js +0 -220
- package/dist/pr-wait-mergeable-parity.d.ts +0 -45
- package/dist/pr-wait-mergeable-parity.js +0 -340
- package/dist/release-e2e-parity.js +0 -114
- package/dist/release-parity.d.ts +0 -40
- package/dist/release-parity.js +0 -226
- package/dist/release-publish-parity.d.ts +0 -36
- package/dist/release-publish-parity.js +0 -138
- package/dist/release-rollback-parity.d.ts +0 -37
- package/dist/release-rollback-parity.js +0 -161
- package/dist/render-parity.d.ts +0 -36
- package/dist/render-parity.js +0 -385
- package/dist/scm-parity.d.ts +0 -39
- package/dist/scm-parity.js +0 -181
- package/dist/scope-lifecycle-parity.d.ts +0 -35
- package/dist/scope-lifecycle-parity.js +0 -177
- package/dist/session-parity.d.ts +0 -39
- package/dist/session-parity.js +0 -262
- package/dist/slice-parity.d.ts +0 -36
- package/dist/slice-parity.js +0 -304
- package/dist/swarm-parity.d.ts +0 -28
- package/dist/swarm-parity.js +0 -327
- package/dist/triage-actions-parity.d.ts +0 -36
- package/dist/triage-actions-parity.js +0 -357
- package/dist/triage-aux-b-parity.js +0 -308
- package/dist/triage-summary-parity.d.ts +0 -50
- package/dist/triage-summary-parity.js +0 -306
- package/dist/validate-content-parity.d.ts +0 -33
- package/dist/validate-content-parity.js +0 -356
- package/dist/vbrief-activate-parity.d.ts +0 -39
- package/dist/vbrief-activate-parity.js +0 -216
- package/dist/vbrief-build-parity.d.ts +0 -28
- package/dist/vbrief-build-parity.js +0 -399
- package/dist/vbrief-preflight-parity.js +0 -163
- package/dist/vbrief-reconcile-parity.d.ts +0 -23
- package/dist/vbrief-reconcile-parity.js +0 -609
- package/dist/vbrief-validate-parity.d.ts +0 -27
- package/dist/vbrief-validate-parity.js +0 -122
- package/dist/vbrief-validation-parity.d.ts +0 -28
- package/dist/vbrief-validation-parity.js +0 -645
- package/dist/verify-env-parity.d.ts +0 -28
- package/dist/verify-env-parity.js +0 -272
- package/dist/verify-source-parity.d.ts +0 -26
- package/dist/verify-source-parity.js +0 -178
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Golden-output parity harness (#1725): runs BOTH the Python oracle
|
|
4
|
-
* (`scripts/triage_actions.py`) and the ported TS triage-actions CLI with
|
|
5
|
-
* isolated fixture roots, then diffs exit codes and normalised stdout/stderr.
|
|
6
|
-
*
|
|
7
|
-
* Exit codes: 0 parity / 1 divergence / 2 harness setup error.
|
|
8
|
-
*/
|
|
9
|
-
import { spawnSync } from "node:child_process";
|
|
10
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { tmpdir } from "node:os";
|
|
12
|
-
import { dirname, join, resolve } from "node:path";
|
|
13
|
-
import { fileURLToPath } from "node:url";
|
|
14
|
-
import { cachePut } from "@deftai/directive-core/dist/cache/operations.js";
|
|
15
|
-
/** Strip volatile UUIDs and timestamps before compare. */
|
|
16
|
-
export function normalizeOutput(text) {
|
|
17
|
-
return text
|
|
18
|
-
.replace(/(?:\/private)?\/(?:tmp|var\/folders\/[^\s"']+\/T)\/deft-triage-actions-parity-[^/\s"']+/g, "<FIXTURE>")
|
|
19
|
-
.replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g, "<UUID>")
|
|
20
|
-
.replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/g, "<TS>")
|
|
21
|
-
.replace(/Using CPython[^\n]*\n/g, "")
|
|
22
|
-
.replace(/Creating virtual environment[^\n]*\n/g, "")
|
|
23
|
-
.replace(/Installed \d+ packages[^\n]*\n/g, "")
|
|
24
|
-
.replace(/Downloading[^\n]*\n/g, "")
|
|
25
|
-
.replace(/Built[^\n]*\n/g, "")
|
|
26
|
-
.replace(/triage_actions: needs-ac comment not posted[^\n]*/g, "triage_actions: needs-ac comment not posted <GH-FAIL>");
|
|
27
|
-
}
|
|
28
|
-
/** Keep only operator-facing triage stderr; drop uv/tooling noise from Python spawns. */
|
|
29
|
-
export function normalizeStderr(text) {
|
|
30
|
-
return normalizeOutput(text)
|
|
31
|
-
.split("\n")
|
|
32
|
-
.filter((line) => line.length === 0 || line.startsWith("triage_actions:"))
|
|
33
|
-
.join("\n");
|
|
34
|
-
}
|
|
35
|
-
function runCapture(cmd, args, cwd, env = {}) {
|
|
36
|
-
const merged = {
|
|
37
|
-
...process.env,
|
|
38
|
-
...env,
|
|
39
|
-
GITHUB_TOKEN: "",
|
|
40
|
-
GH_TOKEN: "",
|
|
41
|
-
GH_ENTERPRISE_TOKEN: "",
|
|
42
|
-
};
|
|
43
|
-
for (const key of Object.keys(merged)) {
|
|
44
|
-
if (merged[key] === undefined)
|
|
45
|
-
delete merged[key];
|
|
46
|
-
}
|
|
47
|
-
const result = spawnSync(cmd, args, {
|
|
48
|
-
cwd,
|
|
49
|
-
encoding: "utf8",
|
|
50
|
-
env: merged,
|
|
51
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
52
|
-
});
|
|
53
|
-
return {
|
|
54
|
-
status: result.status ?? 2,
|
|
55
|
-
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
56
|
-
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
/** Build a throwaway project root with an empty audit-log parent directory. */
|
|
60
|
-
export function buildFixtureRepo() {
|
|
61
|
-
const root = mkdtempSync(join(tmpdir(), "deft-triage-actions-parity-"));
|
|
62
|
-
mkdirSync(join(root, "vbrief", ".eval"), { recursive: true });
|
|
63
|
-
return root;
|
|
64
|
-
}
|
|
65
|
-
function resolveDeftRoot() {
|
|
66
|
-
if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
|
|
67
|
-
return resolve(process.env.DEFT_ROOT);
|
|
68
|
-
}
|
|
69
|
-
return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
70
|
-
}
|
|
71
|
-
function pythonWrapperScript(deftRoot, fixtureRoot) {
|
|
72
|
-
return [
|
|
73
|
-
"import os, sys",
|
|
74
|
-
"from pathlib import Path",
|
|
75
|
-
`fixture = Path(${JSON.stringify(fixtureRoot)})`,
|
|
76
|
-
`deft_root = Path(${JSON.stringify(deftRoot)})`,
|
|
77
|
-
"sys.path.insert(0, str(deft_root / 'scripts'))",
|
|
78
|
-
"import candidates_log as cl",
|
|
79
|
-
"cl.DEFAULT_LOG_PATH = fixture / 'vbrief/.eval/candidates.jsonl'",
|
|
80
|
-
"import cache as cache_mod",
|
|
81
|
-
"cache_mod.DEFAULT_CACHE_ROOT = fixture / '.deft-cache'",
|
|
82
|
-
"import triage_actions",
|
|
83
|
-
"triage_actions.candidates_log = cl",
|
|
84
|
-
"triage_actions.cache = cache_mod",
|
|
85
|
-
"def _parity_gh(args):",
|
|
86
|
-
" raise triage_actions.UpstreamCloseError('gh disabled for parity')",
|
|
87
|
-
"triage_actions._run_gh = _parity_gh",
|
|
88
|
-
"raise SystemExit(triage_actions.main(sys.argv[1:]))",
|
|
89
|
-
].join("\n");
|
|
90
|
-
}
|
|
91
|
-
function runPythonTriageAction(deftRoot, fixtureRoot, argv) {
|
|
92
|
-
const cap = runCapture("uv", ["run", "python", "-c", pythonWrapperScript(deftRoot, fixtureRoot), ...argv], deftRoot, { TRIAGE_PARITY_FIXTURE: fixtureRoot, DEFT_TRIAGE_ACTIONS_PARITY: "1" });
|
|
93
|
-
return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
|
|
94
|
-
}
|
|
95
|
-
function runTsTriageAction(deftRoot, fixtureRoot, argv) {
|
|
96
|
-
const cap = runCapture("node", [
|
|
97
|
-
join(deftRoot, "packages", "cli", "dist", "triage-actions.js"),
|
|
98
|
-
...argv,
|
|
99
|
-
"--project-root",
|
|
100
|
-
fixtureRoot,
|
|
101
|
-
], deftRoot, { DEFT_TRIAGE_ACTIONS_PARITY: "1" });
|
|
102
|
-
return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
|
|
103
|
-
}
|
|
104
|
-
/** Diff one parity case between Python oracle and TS CLI. */
|
|
105
|
-
export function diffCase(python, ts, caseName) {
|
|
106
|
-
const pyOut = normalizeOutput(python.stdout);
|
|
107
|
-
const tsOut = normalizeOutput(ts.stdout);
|
|
108
|
-
const pyErr = normalizeStderr(python.stderr);
|
|
109
|
-
const tsErr = normalizeStderr(ts.stderr);
|
|
110
|
-
return {
|
|
111
|
-
caseName,
|
|
112
|
-
exitMismatch: python.exitCode !== ts.exitCode,
|
|
113
|
-
stdoutMismatch: pyOut !== tsOut,
|
|
114
|
-
stderrMismatch: pyErr !== tsErr,
|
|
115
|
-
pythonExit: python.exitCode,
|
|
116
|
-
tsExit: ts.exitCode,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
export const PARITY_CASES = [
|
|
120
|
-
{
|
|
121
|
-
name: "defer-invalid-resume-on",
|
|
122
|
-
argv: [
|
|
123
|
-
"defer",
|
|
124
|
-
"--issue",
|
|
125
|
-
"7",
|
|
126
|
-
"--repo",
|
|
127
|
-
"deftai/directive",
|
|
128
|
-
"--reason",
|
|
129
|
-
"later",
|
|
130
|
-
"--resume-on",
|
|
131
|
-
"not-valid",
|
|
132
|
-
],
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
name: "defer-success",
|
|
136
|
-
argv: ["defer", "--issue", "7", "--repo", "deftai/directive", "--reason", "later"],
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
name: "defer-with-resume-on",
|
|
140
|
-
argv: [
|
|
141
|
-
"defer",
|
|
142
|
-
"--issue",
|
|
143
|
-
"8",
|
|
144
|
-
"--repo",
|
|
145
|
-
"deftai/directive",
|
|
146
|
-
"--reason",
|
|
147
|
-
"blocked",
|
|
148
|
-
"--resume-on",
|
|
149
|
-
"ref:closed:#99",
|
|
150
|
-
],
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
name: "accept-idempotent",
|
|
154
|
-
argv: ["accept", "--issue", "9", "--repo", "deftai/directive", "--actor", "agent:test"],
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
name: "needs-ac-default",
|
|
158
|
-
argv: ["needs-ac", "--issue", "10", "--repo", "deftai/directive", "--actor", "agent:test"],
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
name: "status-empty",
|
|
162
|
-
argv: ["status", "--issue", "11", "--repo", "deftai/directive"],
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
name: "status-with-defer",
|
|
166
|
-
argv: ["status", "--issue", "12", "--repo", "deftai/directive"],
|
|
167
|
-
seed: "defer-status",
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
name: "reset-no-prior",
|
|
171
|
-
argv: ["reset", "--issue", "13", "--repo", "deftai/directive"],
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
name: "reset-success",
|
|
175
|
-
argv: ["reset", "--issue", "14", "--repo", "deftai/directive", "--actor", "agent:test"],
|
|
176
|
-
seed: "defer-reset",
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
name: "reset-idempotent",
|
|
180
|
-
argv: ["reset", "--issue", "15", "--repo", "deftai/directive"],
|
|
181
|
-
seed: "reset-idempotent",
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
name: "history-empty",
|
|
185
|
-
argv: ["history", "--issue", "16", "--repo", "deftai/directive"],
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
name: "history-multi",
|
|
189
|
-
argv: ["history", "--issue", "17", "--repo", "deftai/directive"],
|
|
190
|
-
seed: "history-multi",
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
name: "mark-duplicate-missing-cache",
|
|
194
|
-
argv: ["mark-duplicate", "--issue", "18", "--repo", "deftai/directive", "--of", "99"],
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
name: "mark-duplicate-success",
|
|
198
|
-
argv: [
|
|
199
|
-
"mark-duplicate",
|
|
200
|
-
"--issue",
|
|
201
|
-
"19",
|
|
202
|
-
"--repo",
|
|
203
|
-
"deftai/directive",
|
|
204
|
-
"--of",
|
|
205
|
-
"20",
|
|
206
|
-
"--actor",
|
|
207
|
-
"agent:test",
|
|
208
|
-
],
|
|
209
|
-
seed: "mark-duplicate-cache",
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
name: "mark-duplicate-idempotent",
|
|
213
|
-
argv: ["mark-duplicate", "--issue", "21", "--repo", "deftai/directive", "--of", "22"],
|
|
214
|
-
seed: "mark-duplicate-idempotent",
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
name: "mark-duplicate-self",
|
|
218
|
-
argv: ["mark-duplicate", "--issue", "23", "--repo", "deftai/directive", "--of", "23"],
|
|
219
|
-
seed: "mark-duplicate-cache-self",
|
|
220
|
-
},
|
|
221
|
-
];
|
|
222
|
-
function seedAcceptFixture(fixtureRoot) {
|
|
223
|
-
const entry = {
|
|
224
|
-
actor: "agent:test",
|
|
225
|
-
decision: "accept",
|
|
226
|
-
decision_id: "prior-id-0000-0000-0000-000000000001",
|
|
227
|
-
issue_number: 9,
|
|
228
|
-
repo: "deftai/directive",
|
|
229
|
-
timestamp: "2026-06-18T12:00:00Z",
|
|
230
|
-
};
|
|
231
|
-
writeFileSync(join(fixtureRoot, "vbrief/.eval/candidates.jsonl"), `${JSON.stringify(entry, Object.keys(entry).sort())}\n`, "utf8");
|
|
232
|
-
}
|
|
233
|
-
function seedAuditEntry(fixtureRoot, issueNumber, decision, extras = {}) {
|
|
234
|
-
const entry = {
|
|
235
|
-
actor: "agent:test",
|
|
236
|
-
decision,
|
|
237
|
-
decision_id: "11111111-1111-1111-1111-111111111111",
|
|
238
|
-
issue_number: issueNumber,
|
|
239
|
-
repo: "deftai/directive",
|
|
240
|
-
timestamp: "2026-06-18T12:00:00Z",
|
|
241
|
-
...extras,
|
|
242
|
-
};
|
|
243
|
-
const path = join(fixtureRoot, "vbrief/.eval/candidates.jsonl");
|
|
244
|
-
const line = `${JSON.stringify(entry, Object.keys(entry).sort())}\n`;
|
|
245
|
-
writeFileSync(path, line, { encoding: "utf8", flag: "a" });
|
|
246
|
-
}
|
|
247
|
-
function seedCacheIssue(fixtureRoot, issueNumber) {
|
|
248
|
-
const cacheRoot = join(fixtureRoot, ".deft-cache");
|
|
249
|
-
cachePut("github-issue", `deftai/directive/${issueNumber}`, { number: issueNumber, title: "parity fixture", body: "fixture body" }, { cacheRoot });
|
|
250
|
-
}
|
|
251
|
-
function seedFixture(fixtureRoot, seed) {
|
|
252
|
-
if (seed === undefined)
|
|
253
|
-
return;
|
|
254
|
-
switch (seed) {
|
|
255
|
-
case "defer-status":
|
|
256
|
-
seedAuditEntry(fixtureRoot, 12, "defer", { reason: "later" });
|
|
257
|
-
break;
|
|
258
|
-
case "defer-reset":
|
|
259
|
-
seedAuditEntry(fixtureRoot, 14, "defer", { reason: "later" });
|
|
260
|
-
break;
|
|
261
|
-
case "reset-idempotent":
|
|
262
|
-
seedAuditEntry(fixtureRoot, 15, "reset", {
|
|
263
|
-
decision_id: "22222222-2222-2222-2222-222222222222",
|
|
264
|
-
prior_decision_id: "11111111-1111-1111-1111-111111111111",
|
|
265
|
-
});
|
|
266
|
-
break;
|
|
267
|
-
case "history-multi":
|
|
268
|
-
seedAuditEntry(fixtureRoot, 17, "defer", {
|
|
269
|
-
decision_id: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
|
270
|
-
reason: "first",
|
|
271
|
-
timestamp: "2026-06-18T10:00:00Z",
|
|
272
|
-
});
|
|
273
|
-
seedAuditEntry(fixtureRoot, 17, "needs-ac", {
|
|
274
|
-
decision_id: "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
|
275
|
-
reason: "needs criteria",
|
|
276
|
-
timestamp: "2026-06-18T11:00:00Z",
|
|
277
|
-
});
|
|
278
|
-
break;
|
|
279
|
-
case "mark-duplicate-cache":
|
|
280
|
-
seedCacheIssue(fixtureRoot, 20);
|
|
281
|
-
break;
|
|
282
|
-
case "mark-duplicate-idempotent":
|
|
283
|
-
seedCacheIssue(fixtureRoot, 22);
|
|
284
|
-
seedAuditEntry(fixtureRoot, 21, "mark-duplicate", {
|
|
285
|
-
decision_id: "cccccccc-cccc-cccc-cccc-cccccccccccc",
|
|
286
|
-
linked_to: 22,
|
|
287
|
-
});
|
|
288
|
-
break;
|
|
289
|
-
case "mark-duplicate-cache-self":
|
|
290
|
-
seedCacheIssue(fixtureRoot, 23);
|
|
291
|
-
break;
|
|
292
|
-
default:
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
/** Run all parity cases; returns aggregate result. */
|
|
297
|
-
export function runParity() {
|
|
298
|
-
const deftRoot = resolveDeftRoot();
|
|
299
|
-
const diffs = [];
|
|
300
|
-
for (const testCase of PARITY_CASES) {
|
|
301
|
-
const pyFixture = buildFixtureRepo();
|
|
302
|
-
const tsFixture = buildFixtureRepo();
|
|
303
|
-
if (testCase.name === "accept-idempotent") {
|
|
304
|
-
seedAcceptFixture(pyFixture);
|
|
305
|
-
seedAcceptFixture(tsFixture);
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
seedFixture(pyFixture, testCase.seed);
|
|
309
|
-
seedFixture(tsFixture, testCase.seed);
|
|
310
|
-
}
|
|
311
|
-
try {
|
|
312
|
-
const python = runPythonTriageAction(deftRoot, pyFixture, testCase.argv);
|
|
313
|
-
const ts = runTsTriageAction(deftRoot, tsFixture, testCase.argv);
|
|
314
|
-
diffs.push(diffCase(python, ts, testCase.name));
|
|
315
|
-
}
|
|
316
|
-
finally {
|
|
317
|
-
rmSync(pyFixture, { recursive: true, force: true });
|
|
318
|
-
rmSync(tsFixture, { recursive: true, force: true });
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
const ok = diffs.every((d) => !d.exitMismatch && !d.stdoutMismatch && !d.stderrMismatch);
|
|
322
|
-
return { ok, diffs };
|
|
323
|
-
}
|
|
324
|
-
export function renderReport(result) {
|
|
325
|
-
if (result.ok) {
|
|
326
|
-
return `triage-actions parity: CLEAN -- Python and TS agree on ${PARITY_CASES.length} cases.`;
|
|
327
|
-
}
|
|
328
|
-
const lines = ["triage-actions parity: DIVERGENCE"];
|
|
329
|
-
for (const d of result.diffs) {
|
|
330
|
-
if (d.exitMismatch || d.stdoutMismatch || d.stderrMismatch) {
|
|
331
|
-
lines.push(` case: ${d.caseName}`);
|
|
332
|
-
if (d.exitMismatch)
|
|
333
|
-
lines.push(` exit: python=${d.pythonExit} ts=${d.tsExit}`);
|
|
334
|
-
if (d.stdoutMismatch)
|
|
335
|
-
lines.push(" stdout mismatch");
|
|
336
|
-
if (d.stderrMismatch)
|
|
337
|
-
lines.push(" stderr mismatch");
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return lines.join("\n");
|
|
341
|
-
}
|
|
342
|
-
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
343
|
-
try {
|
|
344
|
-
const result = runParity();
|
|
345
|
-
if (result.ok) {
|
|
346
|
-
process.stdout.write(`${renderReport(result)}\n`);
|
|
347
|
-
process.exit(0);
|
|
348
|
-
}
|
|
349
|
-
process.stderr.write(`${renderReport(result)}\n`);
|
|
350
|
-
process.exit(1);
|
|
351
|
-
}
|
|
352
|
-
catch (err) {
|
|
353
|
-
process.stderr.write(`triage-actions parity: harness error -- ${String(err)}\n`);
|
|
354
|
-
process.exit(2);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
//# sourceMappingURL=triage-actions-parity.js.map
|
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Golden-output parity harness (#1725): runs BOTH Python oracles and ported TS
|
|
4
|
-
* CLIs for triage aux verbs B (bulk / subscribe / help / smoketest).
|
|
5
|
-
*
|
|
6
|
-
* Exit codes: 0 parity / 1 divergence / 2 harness setup error.
|
|
7
|
-
*/
|
|
8
|
-
import { spawnSync } from "node:child_process";
|
|
9
|
-
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync, } from "node:fs";
|
|
10
|
-
import { tmpdir } from "node:os";
|
|
11
|
-
import { dirname, join, resolve } from "node:path";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
export function normalizeOutput(text) {
|
|
14
|
-
return text
|
|
15
|
-
.replace(/project_root=[^\s)]+/g, "project_root=<ROOT>")
|
|
16
|
-
.replace(/\/tmp\/deft-[^\s/]+/g, "<TMPROOT>")
|
|
17
|
-
.replace(/change_id": "[^"]+"/g, 'change_id": "<UUID>"')
|
|
18
|
-
.replace(/Using CPython[^\n]*\n/g, "")
|
|
19
|
-
.replace(/Creating virtual environment[^\n]*\n/g, "")
|
|
20
|
-
.replace(/Installed \d+ packages[^\n]*\n/g, "");
|
|
21
|
-
}
|
|
22
|
-
function runCapture(cmd, args, cwd, env = {}) {
|
|
23
|
-
const merged = { ...process.env, ...env };
|
|
24
|
-
for (const key of Object.keys(merged)) {
|
|
25
|
-
if (merged[key] === undefined)
|
|
26
|
-
delete merged[key];
|
|
27
|
-
}
|
|
28
|
-
const result = spawnSync(cmd, args, {
|
|
29
|
-
cwd,
|
|
30
|
-
encoding: "utf8",
|
|
31
|
-
env: merged,
|
|
32
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
33
|
-
});
|
|
34
|
-
return {
|
|
35
|
-
status: result.status ?? 2,
|
|
36
|
-
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
37
|
-
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
function writeProjectDefinition(root, policy = {}) {
|
|
41
|
-
mkdirSync(join(root, "vbrief"), { recursive: true });
|
|
42
|
-
writeFileSync(join(root, "vbrief", "PROJECT-DEFINITION.vbrief.json"), `${JSON.stringify({
|
|
43
|
-
vBRIEFInfo: { version: "0.6" },
|
|
44
|
-
plan: { title: "T", status: "running", items: [], policy },
|
|
45
|
-
}, null, 2)}\n`, { encoding: "utf8" });
|
|
46
|
-
}
|
|
47
|
-
function populateCache(cacheRoot, repo, issues) {
|
|
48
|
-
const [owner, name] = repo.split("/");
|
|
49
|
-
if (owner === undefined || name === undefined) {
|
|
50
|
-
throw new Error(`invalid repo ${repo}`);
|
|
51
|
-
}
|
|
52
|
-
for (const issue of issues) {
|
|
53
|
-
const n = String(issue.number);
|
|
54
|
-
const entryDir = join(cacheRoot, "github-issue", owner, name, n);
|
|
55
|
-
mkdirSync(entryDir, { recursive: true });
|
|
56
|
-
writeFileSync(join(entryDir, "raw.json"), JSON.stringify(issue), { encoding: "utf8" });
|
|
57
|
-
writeFileSync(join(entryDir, "meta.json"), JSON.stringify({
|
|
58
|
-
source: "github-issue",
|
|
59
|
-
key: `${repo}/${n}`,
|
|
60
|
-
fetched_at: "2026-05-05T00:00:00Z",
|
|
61
|
-
ttl_seconds: 604800,
|
|
62
|
-
expires_at: "2099-01-01T00:00:00Z",
|
|
63
|
-
scan_result: {
|
|
64
|
-
passed: true,
|
|
65
|
-
scanned_at: "2026-05-05T00:00:00Z",
|
|
66
|
-
scanner_version: "2.0.0",
|
|
67
|
-
flags: [],
|
|
68
|
-
},
|
|
69
|
-
size_bytes: 100,
|
|
70
|
-
stale: false,
|
|
71
|
-
}), { encoding: "utf8" });
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
export function buildFixtureRepo(kind) {
|
|
75
|
-
const root = mkdtempSync(join(tmpdir(), "deft-triage-aux-b-parity-"));
|
|
76
|
-
if (kind === "subscribe") {
|
|
77
|
-
writeProjectDefinition(root);
|
|
78
|
-
return root;
|
|
79
|
-
}
|
|
80
|
-
if (kind === "bulk-empty") {
|
|
81
|
-
writeProjectDefinition(root);
|
|
82
|
-
mkdirSync(join(root, ".deft-cache"), { recursive: true });
|
|
83
|
-
return root;
|
|
84
|
-
}
|
|
85
|
-
writeProjectDefinition(root);
|
|
86
|
-
populateCache(join(root, ".deft-cache"), "deftai/parity", [
|
|
87
|
-
{
|
|
88
|
-
number: 99,
|
|
89
|
-
title: "parity issue",
|
|
90
|
-
labels: [{ name: "other-label" }],
|
|
91
|
-
author: { login: "bot" },
|
|
92
|
-
createdAt: "2020-01-01T00:00:00Z",
|
|
93
|
-
},
|
|
94
|
-
]);
|
|
95
|
-
return root;
|
|
96
|
-
}
|
|
97
|
-
function resolveDeftRoot() {
|
|
98
|
-
if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
|
|
99
|
-
return resolve(process.env.DEFT_ROOT);
|
|
100
|
-
}
|
|
101
|
-
return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
102
|
-
}
|
|
103
|
-
const PY_SCRIPT = {
|
|
104
|
-
help: "triage_help.py",
|
|
105
|
-
subscribe: "triage_subscribe.py",
|
|
106
|
-
bulk: "triage_bulk.py",
|
|
107
|
-
smoketest: "triage_smoketest.py",
|
|
108
|
-
};
|
|
109
|
-
const TS_CLI = {
|
|
110
|
-
help: "triage-help.js",
|
|
111
|
-
subscribe: "triage-subscribe.js",
|
|
112
|
-
bulk: "triage-bulk.js",
|
|
113
|
-
smoketest: "triage-smoketest.js",
|
|
114
|
-
};
|
|
115
|
-
function runPython(deftRoot, testCase, repo) {
|
|
116
|
-
const script = join(deftRoot, "scripts", PY_SCRIPT[testCase.verb]);
|
|
117
|
-
if (testCase.verb === "help") {
|
|
118
|
-
const cap = runCapture("uv", ["run", "python", script, ...testCase.argv], deftRoot, testCase.env);
|
|
119
|
-
return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
|
|
120
|
-
}
|
|
121
|
-
if (testCase.verb === "bulk") {
|
|
122
|
-
const cap = runCapture("uv", ["run", "python", script, ...testCase.argv], repo, {
|
|
123
|
-
...testCase.env,
|
|
124
|
-
DEFT_ROOT: deftRoot,
|
|
125
|
-
});
|
|
126
|
-
return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
|
|
127
|
-
}
|
|
128
|
-
if (testCase.verb === "smoketest") {
|
|
129
|
-
const cap = runCapture("uv", ["run", "python", script, ...testCase.argv], deftRoot, testCase.env);
|
|
130
|
-
return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
|
|
131
|
-
}
|
|
132
|
-
const cap = runCapture("uv", ["run", "python", script, ...testCase.argv, "--project-root", repo], deftRoot, testCase.env);
|
|
133
|
-
return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
|
|
134
|
-
}
|
|
135
|
-
function runTs(deftRoot, testCase, repo) {
|
|
136
|
-
const cli = join(deftRoot, "packages", "cli", "dist", TS_CLI[testCase.verb]);
|
|
137
|
-
const argv = [...testCase.argv];
|
|
138
|
-
if (testCase.verb === "subscribe") {
|
|
139
|
-
argv.push("--project-root", repo);
|
|
140
|
-
}
|
|
141
|
-
const cwd = testCase.verb === "bulk" ? repo : deftRoot;
|
|
142
|
-
const cap = runCapture("node", [cli, ...argv], cwd, {
|
|
143
|
-
...testCase.env,
|
|
144
|
-
DEFT_ROOT: deftRoot,
|
|
145
|
-
});
|
|
146
|
-
return { exitCode: cap.status, stdout: cap.stdout, stderr: cap.stderr };
|
|
147
|
-
}
|
|
148
|
-
export function diffCase(python, ts, caseName) {
|
|
149
|
-
return {
|
|
150
|
-
caseName,
|
|
151
|
-
exitMismatch: python.exitCode !== ts.exitCode,
|
|
152
|
-
stdoutMismatch: normalizeOutput(python.stdout) !== normalizeOutput(ts.stdout),
|
|
153
|
-
stderrMismatch: normalizeOutput(python.stderr) !== normalizeOutput(ts.stderr),
|
|
154
|
-
pythonExit: python.exitCode,
|
|
155
|
-
tsExit: ts.exitCode,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
export const PARITY_CASES = [
|
|
159
|
-
{ name: "help-triage-list", verb: "help", argv: ["triage"] },
|
|
160
|
-
{ name: "help-scope-list", verb: "help", argv: ["scope"] },
|
|
161
|
-
{ name: "help-verb-queue", verb: "help", argv: ["help", "task triage:queue"] },
|
|
162
|
-
{ name: "help-registry-list", verb: "help", argv: ["list"] },
|
|
163
|
-
{ name: "help-bulk-intercept", verb: "bulk", argv: ["accept", "--help"] },
|
|
164
|
-
{
|
|
165
|
-
name: "subscribe-label-create",
|
|
166
|
-
verb: "subscribe",
|
|
167
|
-
argv: ["subscribe", "--label", "area:parity"],
|
|
168
|
-
fixtureRoot: "subscribe",
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
name: "subscribe-label-idempotent",
|
|
172
|
-
verb: "subscribe",
|
|
173
|
-
argv: ["subscribe", "--label", "dup-label"],
|
|
174
|
-
fixtureRoot: "subscribe",
|
|
175
|
-
env: { DEFT_TRIAGE_ACTOR: "agent:parity" },
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
name: "subscribe-unsubscribe-missing",
|
|
179
|
-
verb: "subscribe",
|
|
180
|
-
argv: ["unsubscribe", "--label", "ghost"],
|
|
181
|
-
fixtureRoot: "subscribe",
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
name: "bulk-empty-cache",
|
|
185
|
-
verb: "bulk",
|
|
186
|
-
argv: ["accept", "--repo", "deftai/parity"],
|
|
187
|
-
fixtureRoot: "bulk-empty",
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
name: "bulk-zero-match",
|
|
191
|
-
verb: "bulk",
|
|
192
|
-
argv: ["defer", "--repo", "deftai/parity", "--label", "no-such-label"],
|
|
193
|
-
fixtureRoot: "bulk-filter",
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
name: "smoketest-missing-fixture",
|
|
197
|
-
verb: "smoketest",
|
|
198
|
-
argv: ["--fixture", "/nonexistent/deft-smoketest-fixture"],
|
|
199
|
-
},
|
|
200
|
-
];
|
|
201
|
-
export function runParity() {
|
|
202
|
-
const deftRoot = resolveDeftRoot();
|
|
203
|
-
const diffs = [];
|
|
204
|
-
const fixtureCache = new Map();
|
|
205
|
-
for (const testCase of PARITY_CASES) {
|
|
206
|
-
let pyRepo = deftRoot;
|
|
207
|
-
let tsRepo = deftRoot;
|
|
208
|
-
let ownsFixture = false;
|
|
209
|
-
if (testCase.fixtureRoot !== undefined) {
|
|
210
|
-
if (!fixtureCache.has(testCase.fixtureRoot)) {
|
|
211
|
-
fixtureCache.set(testCase.fixtureRoot, buildFixtureRepo(testCase.fixtureRoot));
|
|
212
|
-
}
|
|
213
|
-
const template = fixtureCache.get(testCase.fixtureRoot);
|
|
214
|
-
if (template === undefined) {
|
|
215
|
-
throw new Error(`missing fixture template ${testCase.fixtureRoot}`);
|
|
216
|
-
}
|
|
217
|
-
pyRepo = mkdtempSync(join(tmpdir(), "deft-parity-py-"));
|
|
218
|
-
tsRepo = mkdtempSync(join(tmpdir(), "deft-parity-ts-"));
|
|
219
|
-
for (const dest of [pyRepo, tsRepo]) {
|
|
220
|
-
mkdirSync(dest, { recursive: true });
|
|
221
|
-
for (const name of ["vbrief", ".deft-cache"]) {
|
|
222
|
-
const src = join(template, name);
|
|
223
|
-
if (existsSync(src)) {
|
|
224
|
-
cpRecursive(src, join(dest, name));
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
ownsFixture = true;
|
|
229
|
-
if (testCase.name === "subscribe-label-idempotent") {
|
|
230
|
-
for (const repo of [pyRepo, tsRepo]) {
|
|
231
|
-
const setup = runCapture("uv", [
|
|
232
|
-
"run",
|
|
233
|
-
"python",
|
|
234
|
-
join(deftRoot, "scripts", "triage_subscribe.py"),
|
|
235
|
-
"subscribe",
|
|
236
|
-
"--label",
|
|
237
|
-
"dup-label",
|
|
238
|
-
"--project-root",
|
|
239
|
-
repo,
|
|
240
|
-
], deftRoot);
|
|
241
|
-
if (setup.status !== 0) {
|
|
242
|
-
throw new Error(`subscribe-label-idempotent setup failed for ${repo}: ${setup.stderr || setup.stdout}`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
try {
|
|
248
|
-
const python = runPython(deftRoot, testCase, pyRepo);
|
|
249
|
-
const ts = runTs(deftRoot, testCase, tsRepo);
|
|
250
|
-
diffs.push(diffCase(python, ts, testCase.name));
|
|
251
|
-
}
|
|
252
|
-
finally {
|
|
253
|
-
if (ownsFixture) {
|
|
254
|
-
rmSync(pyRepo, { recursive: true, force: true });
|
|
255
|
-
rmSync(tsRepo, { recursive: true, force: true });
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
const ok = diffs.every((d) => !d.exitMismatch && !d.stdoutMismatch && !d.stderrMismatch);
|
|
260
|
-
return { ok, diffs };
|
|
261
|
-
}
|
|
262
|
-
function cpRecursive(src, dest) {
|
|
263
|
-
mkdirSync(dest, { recursive: true });
|
|
264
|
-
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
265
|
-
const s = join(src, entry.name);
|
|
266
|
-
const d = join(dest, entry.name);
|
|
267
|
-
if (entry.isDirectory()) {
|
|
268
|
-
cpRecursive(s, d);
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
copyFileSync(s, d);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
export function renderReport(result) {
|
|
276
|
-
if (result.ok) {
|
|
277
|
-
return `triage-aux-b parity: CLEAN -- Python and TS agree on ${PARITY_CASES.length} cases.`;
|
|
278
|
-
}
|
|
279
|
-
const lines = ["triage-aux-b parity: DIVERGENCE"];
|
|
280
|
-
for (const d of result.diffs) {
|
|
281
|
-
if (d.exitMismatch || d.stdoutMismatch || d.stderrMismatch) {
|
|
282
|
-
lines.push(` case: ${d.caseName}`);
|
|
283
|
-
if (d.exitMismatch)
|
|
284
|
-
lines.push(` exit: python=${d.pythonExit} ts=${d.tsExit}`);
|
|
285
|
-
if (d.stdoutMismatch)
|
|
286
|
-
lines.push(" stdout mismatch");
|
|
287
|
-
if (d.stderrMismatch)
|
|
288
|
-
lines.push(" stderr mismatch");
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
return lines.join("\n");
|
|
292
|
-
}
|
|
293
|
-
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
294
|
-
try {
|
|
295
|
-
const result = runParity();
|
|
296
|
-
if (result.ok) {
|
|
297
|
-
process.stdout.write(`${renderReport(result)}\n`);
|
|
298
|
-
process.exit(0);
|
|
299
|
-
}
|
|
300
|
-
process.stderr.write(`${renderReport(result)}\n`);
|
|
301
|
-
process.exit(1);
|
|
302
|
-
}
|
|
303
|
-
catch (err) {
|
|
304
|
-
process.stderr.write(`triage-aux-b parity: harness error -- ${String(err)}\n`);
|
|
305
|
-
process.exit(2);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
//# sourceMappingURL=triage-aux-b-parity.js.map
|