@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,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