@deftai/directive 0.62.0 → 0.64.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 (99) 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 +3 -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/verify-source-cli/verify-cursor-tier1.d.ts +12 -0
  27. package/dist/verify-source-cli/verify-cursor-tier1.js +51 -0
  28. package/dist/{wip-cap-parity.d.ts → wip-cap-fixtures.d.ts} +1 -3
  29. package/dist/{wip-cap-parity.js → wip-cap-fixtures.js} +4 -91
  30. package/package.json +4 -15
  31. package/dist/cache-parity.d.ts +0 -36
  32. package/dist/cache-parity.js +0 -165
  33. package/dist/codebase-parity.d.ts +0 -31
  34. package/dist/codebase-parity.js +0 -303
  35. package/dist/doc-cli-parity.d.ts +0 -29
  36. package/dist/doc-cli-parity.js +0 -159
  37. package/dist/doctor-parity.d.ts +0 -42
  38. package/dist/doctor-parity.js +0 -157
  39. package/dist/intake-parity.d.ts +0 -30
  40. package/dist/intake-parity.js +0 -203
  41. package/dist/lifecycle-packs-parity.d.ts +0 -30
  42. package/dist/lifecycle-packs-parity.js +0 -377
  43. package/dist/orchestration-parity.d.ts +0 -38
  44. package/dist/orchestration-parity.js +0 -364
  45. package/dist/parity.d.ts +0 -36
  46. package/dist/parity.js +0 -176
  47. package/dist/platform-parity.d.ts +0 -26
  48. package/dist/platform-parity.js +0 -309
  49. package/dist/pr-closing-keywords-parity.d.ts +0 -45
  50. package/dist/pr-closing-keywords-parity.js +0 -259
  51. package/dist/pr-merge-readiness-parity.d.ts +0 -44
  52. package/dist/pr-merge-readiness-parity.js +0 -296
  53. package/dist/pr-monitor-parity.d.ts +0 -44
  54. package/dist/pr-monitor-parity.js +0 -283
  55. package/dist/pr-protected-issues-parity.d.ts +0 -41
  56. package/dist/pr-protected-issues-parity.js +0 -220
  57. package/dist/pr-wait-mergeable-parity.d.ts +0 -45
  58. package/dist/pr-wait-mergeable-parity.js +0 -340
  59. package/dist/release-e2e-parity.js +0 -114
  60. package/dist/release-parity.d.ts +0 -40
  61. package/dist/release-parity.js +0 -226
  62. package/dist/release-publish-parity.d.ts +0 -36
  63. package/dist/release-publish-parity.js +0 -138
  64. package/dist/release-rollback-parity.d.ts +0 -37
  65. package/dist/release-rollback-parity.js +0 -161
  66. package/dist/render-parity.d.ts +0 -36
  67. package/dist/render-parity.js +0 -385
  68. package/dist/scm-parity.d.ts +0 -39
  69. package/dist/scm-parity.js +0 -181
  70. package/dist/scope-lifecycle-parity.d.ts +0 -35
  71. package/dist/scope-lifecycle-parity.js +0 -177
  72. package/dist/session-parity.d.ts +0 -39
  73. package/dist/session-parity.js +0 -262
  74. package/dist/slice-parity.d.ts +0 -36
  75. package/dist/slice-parity.js +0 -304
  76. package/dist/swarm-parity.d.ts +0 -28
  77. package/dist/swarm-parity.js +0 -327
  78. package/dist/triage-actions-parity.d.ts +0 -36
  79. package/dist/triage-actions-parity.js +0 -357
  80. package/dist/triage-aux-b-parity.js +0 -308
  81. package/dist/triage-summary-parity.d.ts +0 -50
  82. package/dist/triage-summary-parity.js +0 -306
  83. package/dist/validate-content-parity.d.ts +0 -33
  84. package/dist/validate-content-parity.js +0 -356
  85. package/dist/vbrief-activate-parity.d.ts +0 -39
  86. package/dist/vbrief-activate-parity.js +0 -216
  87. package/dist/vbrief-build-parity.d.ts +0 -28
  88. package/dist/vbrief-build-parity.js +0 -399
  89. package/dist/vbrief-preflight-parity.js +0 -163
  90. package/dist/vbrief-reconcile-parity.d.ts +0 -23
  91. package/dist/vbrief-reconcile-parity.js +0 -609
  92. package/dist/vbrief-validate-parity.d.ts +0 -27
  93. package/dist/vbrief-validate-parity.js +0 -122
  94. package/dist/vbrief-validation-parity.d.ts +0 -28
  95. package/dist/vbrief-validation-parity.js +0 -645
  96. package/dist/verify-env-parity.d.ts +0 -28
  97. package/dist/verify-env-parity.js +0 -272
  98. package/dist/verify-source-parity.d.ts +0 -26
  99. 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