@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
package/dist/scm-parity.js
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Golden-output parity harness (#1724): runs BOTH the Python oracle
|
|
4
|
-
* (`scripts/scm.py`) and the ported TS SCM CLI with identical argv, then
|
|
5
|
-
* diffs exit codes and normalised stdout/stderr. Exit 0 only on identical
|
|
6
|
-
* results.
|
|
7
|
-
*
|
|
8
|
-
* Exit codes: 0 parity / 1 divergence / 2 harness setup error.
|
|
9
|
-
*/
|
|
10
|
-
import { execFileSync } from "node:child_process";
|
|
11
|
-
import { dirname, join, resolve } from "node:path";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
/** Validation-only scenarios (no live gh network). */
|
|
14
|
-
export const PARITY_SCENARIOS = [
|
|
15
|
-
{ name: "usage-too-short", argv: [] },
|
|
16
|
-
{ name: "unknown-namespace", argv: ["isue", "list"] },
|
|
17
|
-
{ name: "unknown-issue-verb", argv: ["issue", "merge"] },
|
|
18
|
-
{
|
|
19
|
-
name: "rest-rejected-close",
|
|
20
|
-
argv: ["issue", "close", "--rest", "1", "--repo", "deftai/directive"],
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: "rest-rejected-edit",
|
|
24
|
-
argv: ["issue", "edit", "--rest", "1", "--repo", "deftai/directive"],
|
|
25
|
-
},
|
|
26
|
-
{ name: "rest-view-missing-repo", argv: ["issue", "view", "--rest", "1"] },
|
|
27
|
-
{
|
|
28
|
-
name: "rest-view-missing-positional",
|
|
29
|
-
argv: ["issue", "view", "--rest", "--repo", "deftai/directive"],
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
name: "rest-view-non-integer",
|
|
33
|
-
argv: ["issue", "view", "--rest", "abc", "--repo", "deftai/directive"],
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: "rest-view-unknown-flag",
|
|
37
|
-
argv: ["issue", "view", "--rest", "1", "--repo", "deftai/directive", "--state", "closed"],
|
|
38
|
-
},
|
|
39
|
-
{ name: "rest-list-missing-repo", argv: ["issue", "list", "--rest"] },
|
|
40
|
-
{
|
|
41
|
-
name: "rest-list-unknown-flag",
|
|
42
|
-
argv: ["issue", "list", "--rest", "--repo", "deftai/directive", "--unknown-flag", "x"],
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
name: "rest-list-leftover-positional",
|
|
46
|
-
argv: ["issue", "list", "--rest", "123", "--repo", "deftai/directive"],
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
name: "rest-list-non-integer-limit",
|
|
50
|
-
argv: ["issue", "list", "--rest", "--repo", "deftai/directive", "--limit", "many"],
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: "rest-view-invalid-repo",
|
|
54
|
-
argv: ["issue", "view", "--rest", "1", "--repo", "directive"],
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: "rest-list-invalid-repo",
|
|
58
|
-
argv: ["issue", "list", "--rest", "--repo", "directive"],
|
|
59
|
-
},
|
|
60
|
-
];
|
|
61
|
-
function runCapture(cmd, args, cwd) {
|
|
62
|
-
try {
|
|
63
|
-
const stdout = execFileSync(cmd, args, {
|
|
64
|
-
cwd,
|
|
65
|
-
encoding: "utf8",
|
|
66
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
67
|
-
});
|
|
68
|
-
return { status: 0, stdout, stderr: "" };
|
|
69
|
-
}
|
|
70
|
-
catch (err) {
|
|
71
|
-
const e = err;
|
|
72
|
-
return {
|
|
73
|
-
status: typeof e.status === "number" ? e.status : 2,
|
|
74
|
-
stdout: typeof e.stdout === "string" ? e.stdout : "",
|
|
75
|
-
stderr: typeof e.stderr === "string" ? e.stderr : "",
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
/** Normalise gate output for comparison. */
|
|
80
|
-
export function normaliseMessage(stdout, stderr, exitCode) {
|
|
81
|
-
const raw = exitCode === 0 ? stdout : stderr;
|
|
82
|
-
return raw
|
|
83
|
-
.split("\n")
|
|
84
|
-
.filter((line) => !line.startsWith("Using CPython") &&
|
|
85
|
-
!line.startsWith("Creating virtual environment") &&
|
|
86
|
-
!line.startsWith("Installed "))
|
|
87
|
-
.join("\n")
|
|
88
|
-
.trim()
|
|
89
|
-
.replace(/\s+/g, " ");
|
|
90
|
-
}
|
|
91
|
-
function resolveDeftRoot() {
|
|
92
|
-
if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
|
|
93
|
-
return resolve(process.env.DEFT_ROOT);
|
|
94
|
-
}
|
|
95
|
-
return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
96
|
-
}
|
|
97
|
-
function runScenario(deftRoot, scenario) {
|
|
98
|
-
const pyArgs = ["run", "python", join(deftRoot, "scripts", "scm.py"), ...scenario.argv];
|
|
99
|
-
const tsArgs = [join(deftRoot, "packages", "core", "dist", "scm", "cli.js"), ...scenario.argv];
|
|
100
|
-
const py = runCapture("uv", pyArgs, deftRoot);
|
|
101
|
-
const ts = runCapture("node", tsArgs, deftRoot);
|
|
102
|
-
return {
|
|
103
|
-
python: {
|
|
104
|
-
name: scenario.name,
|
|
105
|
-
exitCode: py.status,
|
|
106
|
-
stdout: py.stdout,
|
|
107
|
-
stderr: py.stderr,
|
|
108
|
-
},
|
|
109
|
-
ts: {
|
|
110
|
-
name: scenario.name,
|
|
111
|
-
exitCode: ts.status,
|
|
112
|
-
stdout: ts.stdout,
|
|
113
|
-
stderr: ts.stderr,
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
/** Diff python vs TS outputs for one scenario. */
|
|
118
|
-
export function diffParity(python, ts) {
|
|
119
|
-
const pythonMessage = normaliseMessage(python.stdout, python.stderr, python.exitCode);
|
|
120
|
-
const tsMessage = normaliseMessage(ts.stdout, ts.stderr, ts.exitCode);
|
|
121
|
-
return {
|
|
122
|
-
exitMismatch: python.exitCode !== ts.exitCode,
|
|
123
|
-
messageMismatch: pythonMessage !== tsMessage,
|
|
124
|
-
pythonMessage,
|
|
125
|
-
tsMessage,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
/** Run all parity scenarios and return a structured result. */
|
|
129
|
-
export function runParity() {
|
|
130
|
-
const deftRoot = resolveDeftRoot();
|
|
131
|
-
const scenarios = [];
|
|
132
|
-
for (const scenario of PARITY_SCENARIOS) {
|
|
133
|
-
const ran = runScenario(deftRoot, scenario);
|
|
134
|
-
const diff = diffParity(ran.python, ran.ts);
|
|
135
|
-
scenarios.push({
|
|
136
|
-
name: scenario.name,
|
|
137
|
-
pythonExit: ran.python.exitCode,
|
|
138
|
-
tsExit: ran.ts.exitCode,
|
|
139
|
-
...diff,
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
const ok = scenarios.every((s) => !s.exitMismatch && !s.messageMismatch);
|
|
143
|
-
return { ok, scenarios };
|
|
144
|
-
}
|
|
145
|
-
/** Render a human-readable parity report. */
|
|
146
|
-
export function renderReport(result) {
|
|
147
|
-
if (result.ok) {
|
|
148
|
-
return `scm parity: CLEAN -- Python and TS agree on ${result.scenarios.length} scenario(s).`;
|
|
149
|
-
}
|
|
150
|
-
const lines = ["scm parity: DIVERGENCE"];
|
|
151
|
-
for (const s of result.scenarios) {
|
|
152
|
-
if (s.exitMismatch || s.messageMismatch) {
|
|
153
|
-
lines.push(` scenario: ${s.name}`);
|
|
154
|
-
if (s.exitMismatch) {
|
|
155
|
-
lines.push(` exit mismatch: python=${s.pythonExit} ts=${s.tsExit}`);
|
|
156
|
-
}
|
|
157
|
-
if (s.messageMismatch) {
|
|
158
|
-
lines.push(` python: ${s.pythonMessage}`);
|
|
159
|
-
lines.push(` ts: ${s.tsMessage}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return lines.join("\n");
|
|
164
|
-
}
|
|
165
|
-
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
166
|
-
try {
|
|
167
|
-
const result = runParity();
|
|
168
|
-
if (result.ok) {
|
|
169
|
-
process.stdout.write(`${renderReport(result)}\n`);
|
|
170
|
-
process.exit(0);
|
|
171
|
-
}
|
|
172
|
-
process.stderr.write(`${renderReport(result)}\n`);
|
|
173
|
-
process.exit(1);
|
|
174
|
-
}
|
|
175
|
-
catch (err) {
|
|
176
|
-
const msg = String(err).replace(/\r?\n/g, " ");
|
|
177
|
-
process.stderr.write(`scm parity: harness error -- ${msg}\n`);
|
|
178
|
-
process.exit(2);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
//# sourceMappingURL=scm-parity.js.map
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
export interface CommandCapture {
|
|
3
|
-
readonly exitCode: number;
|
|
4
|
-
readonly stdout: string;
|
|
5
|
-
readonly stderr: string;
|
|
6
|
-
}
|
|
7
|
-
export interface ParityCase {
|
|
8
|
-
readonly name: string;
|
|
9
|
-
readonly argv: readonly string[];
|
|
10
|
-
readonly fileRel?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface ParityDiff {
|
|
13
|
-
readonly caseName: string;
|
|
14
|
-
readonly exitMismatch: boolean;
|
|
15
|
-
readonly stdoutMismatch: boolean;
|
|
16
|
-
readonly stderrMismatch: boolean;
|
|
17
|
-
readonly pythonExit: number;
|
|
18
|
-
readonly tsExit: number;
|
|
19
|
-
readonly pythonStdout: string;
|
|
20
|
-
readonly pythonStderr: string;
|
|
21
|
-
readonly tsStdout: string;
|
|
22
|
-
readonly tsStderr: string;
|
|
23
|
-
}
|
|
24
|
-
export interface ParityResult {
|
|
25
|
-
readonly ok: boolean;
|
|
26
|
-
readonly diffs: ParityDiff[];
|
|
27
|
-
}
|
|
28
|
-
export declare function normalizeOutput(text: string): string;
|
|
29
|
-
export declare const PARITY_CASES: readonly ParityCase[];
|
|
30
|
-
export declare function buildArgv(repo: string, testCase: ParityCase): string[];
|
|
31
|
-
export declare function diffCase(python: CommandCapture, ts: CommandCapture, caseName: string): ParityDiff;
|
|
32
|
-
export declare function runParity(): ParityResult;
|
|
33
|
-
export declare function renderReport(result: ParityResult): string;
|
|
34
|
-
export declare function runParityCli(): number;
|
|
35
|
-
//# sourceMappingURL=scope-lifecycle-parity.d.ts.map
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Golden-output parity harness (#1726): runs BOTH the Python oracle
|
|
4
|
-
* (`scripts/scope_lifecycle.py`) and the ported TS scope lifecycle CLI,
|
|
5
|
-
* then diffs exit codes and normalised stdout/stderr.
|
|
6
|
-
*/
|
|
7
|
-
import { spawnSync } from "node:child_process";
|
|
8
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
-
import { tmpdir } from "node:os";
|
|
10
|
-
import { dirname, join, resolve } from "node:path";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
|
-
const FIXTURE_NAME = "2026-04-12-add-oauth.vbrief.json";
|
|
13
|
-
export function normalizeOutput(text) {
|
|
14
|
-
return text
|
|
15
|
-
.replace(/--project-root [^\s]+/g, "--project-root <ROOT>")
|
|
16
|
-
.trim()
|
|
17
|
-
.replace(/\s+/g, " ");
|
|
18
|
-
}
|
|
19
|
-
function runCapture(cmd, args, cwd) {
|
|
20
|
-
const result = spawnSync(cmd, args, {
|
|
21
|
-
cwd,
|
|
22
|
-
encoding: "utf8",
|
|
23
|
-
env: process.env,
|
|
24
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
25
|
-
});
|
|
26
|
-
return {
|
|
27
|
-
exitCode: result.status ?? 2,
|
|
28
|
-
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
29
|
-
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
function resolveDeftRoot() {
|
|
33
|
-
if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
|
|
34
|
-
return resolve(process.env.DEFT_ROOT);
|
|
35
|
-
}
|
|
36
|
-
return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
37
|
-
}
|
|
38
|
-
function writeFixture(repo, folder, status) {
|
|
39
|
-
const full = join(repo, "vbrief", folder, FIXTURE_NAME);
|
|
40
|
-
mkdirSync(dirname(full), { recursive: true });
|
|
41
|
-
writeFileSync(full, `${JSON.stringify({
|
|
42
|
-
vBRIEFInfo: { version: "0.5" },
|
|
43
|
-
plan: { title: "Add OAuth support", status, items: [] },
|
|
44
|
-
}, null, 2)}\n`, "utf8");
|
|
45
|
-
return full;
|
|
46
|
-
}
|
|
47
|
-
export const PARITY_CASES = [
|
|
48
|
-
{ name: "usage-no-args", argv: [] },
|
|
49
|
-
{
|
|
50
|
-
name: "invalid-transition-promote-active",
|
|
51
|
-
argv: ["promote", "<FILE>"],
|
|
52
|
-
fileRel: `vbrief/active/${FIXTURE_NAME}`,
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: "promote-success",
|
|
56
|
-
argv: ["promote", "<FILE>"],
|
|
57
|
-
fileRel: `vbrief/proposed/${FIXTURE_NAME}`,
|
|
58
|
-
},
|
|
59
|
-
];
|
|
60
|
-
export function buildArgv(repo, testCase) {
|
|
61
|
-
return testCase.argv.map((arg) => {
|
|
62
|
-
if (arg === "<FILE>" && testCase.fileRel !== undefined) {
|
|
63
|
-
return join(repo, testCase.fileRel);
|
|
64
|
-
}
|
|
65
|
-
return arg;
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
function runPythonScope(deftRoot, repo, argv) {
|
|
69
|
-
return runCapture("uv", [
|
|
70
|
-
"run",
|
|
71
|
-
"python",
|
|
72
|
-
join(deftRoot, "scripts", "scope_lifecycle.py"),
|
|
73
|
-
...argv,
|
|
74
|
-
"--project-root",
|
|
75
|
-
repo,
|
|
76
|
-
], deftRoot);
|
|
77
|
-
}
|
|
78
|
-
function runTsScope(deftRoot, repo, argv) {
|
|
79
|
-
return runCapture("node", [
|
|
80
|
-
join(deftRoot, "packages", "core", "dist", "scope", "cli.js"),
|
|
81
|
-
...argv,
|
|
82
|
-
"--project-root",
|
|
83
|
-
repo,
|
|
84
|
-
], deftRoot);
|
|
85
|
-
}
|
|
86
|
-
export function diffCase(python, ts, caseName) {
|
|
87
|
-
return {
|
|
88
|
-
caseName,
|
|
89
|
-
exitMismatch: python.exitCode !== ts.exitCode,
|
|
90
|
-
stdoutMismatch: normalizeOutput(python.stdout) !== normalizeOutput(ts.stdout),
|
|
91
|
-
stderrMismatch: normalizeOutput(python.stderr) !== normalizeOutput(ts.stderr),
|
|
92
|
-
pythonExit: python.exitCode,
|
|
93
|
-
tsExit: ts.exitCode,
|
|
94
|
-
pythonStdout: python.stdout,
|
|
95
|
-
pythonStderr: python.stderr,
|
|
96
|
-
tsStdout: ts.stdout,
|
|
97
|
-
tsStderr: ts.stderr,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
function setupFixtureRepo(testCase) {
|
|
101
|
-
const repo = mkdtempSync(join(tmpdir(), "deft-scope-parity-"));
|
|
102
|
-
mkdirSync(join(repo, "vbrief"), { recursive: true });
|
|
103
|
-
if (testCase.fileRel?.includes("/proposed/")) {
|
|
104
|
-
writeFixture(repo, "proposed", "proposed");
|
|
105
|
-
}
|
|
106
|
-
if (testCase.fileRel?.includes("/active/")) {
|
|
107
|
-
writeFixture(repo, "active", "running");
|
|
108
|
-
}
|
|
109
|
-
return repo;
|
|
110
|
-
}
|
|
111
|
-
export function runParity() {
|
|
112
|
-
const deftRoot = resolveDeftRoot();
|
|
113
|
-
const diffs = [];
|
|
114
|
-
for (const testCase of PARITY_CASES) {
|
|
115
|
-
const mutates = testCase.name === "promote-success";
|
|
116
|
-
const pyRepo = setupFixtureRepo(testCase);
|
|
117
|
-
const tsRepo = mutates ? setupFixtureRepo(testCase) : pyRepo;
|
|
118
|
-
try {
|
|
119
|
-
const pyArgv = buildArgv(pyRepo, testCase);
|
|
120
|
-
const tsArgv = buildArgv(tsRepo, testCase);
|
|
121
|
-
const py = runPythonScope(deftRoot, pyRepo, pyArgv);
|
|
122
|
-
const ts = runTsScope(deftRoot, tsRepo, tsArgv);
|
|
123
|
-
diffs.push(diffCase(py, ts, testCase.name));
|
|
124
|
-
}
|
|
125
|
-
finally {
|
|
126
|
-
rmSync(pyRepo, { recursive: true, force: true });
|
|
127
|
-
if (tsRepo !== pyRepo) {
|
|
128
|
-
rmSync(tsRepo, { recursive: true, force: true });
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
const ok = diffs.every((d) => !d.exitMismatch && !d.stdoutMismatch && !d.stderrMismatch);
|
|
133
|
-
return { ok, diffs };
|
|
134
|
-
}
|
|
135
|
-
export function renderReport(result) {
|
|
136
|
-
if (result.ok) {
|
|
137
|
-
return `scope-lifecycle parity: CLEAN -- Python and TS agree on ${result.diffs.length} scenario(s).`;
|
|
138
|
-
}
|
|
139
|
-
const lines = ["scope-lifecycle parity: DIVERGENCE"];
|
|
140
|
-
for (const d of result.diffs) {
|
|
141
|
-
if (d.exitMismatch || d.stdoutMismatch || d.stderrMismatch) {
|
|
142
|
-
lines.push(` scenario: ${d.caseName}`);
|
|
143
|
-
if (d.exitMismatch) {
|
|
144
|
-
lines.push(` exit mismatch: python=${d.pythonExit} ts=${d.tsExit}`);
|
|
145
|
-
}
|
|
146
|
-
if (d.stdoutMismatch) {
|
|
147
|
-
lines.push(` python stdout: ${normalizeOutput(d.pythonStdout)}`);
|
|
148
|
-
lines.push(` ts stdout: ${normalizeOutput(d.tsStdout)}`);
|
|
149
|
-
}
|
|
150
|
-
if (d.stderrMismatch) {
|
|
151
|
-
lines.push(` python stderr: ${normalizeOutput(d.pythonStderr)}`);
|
|
152
|
-
lines.push(` ts stderr: ${normalizeOutput(d.tsStderr)}`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return lines.join("\n");
|
|
157
|
-
}
|
|
158
|
-
export function runParityCli() {
|
|
159
|
-
try {
|
|
160
|
-
const result = runParity();
|
|
161
|
-
if (result.ok) {
|
|
162
|
-
process.stdout.write(`${renderReport(result)}\n`);
|
|
163
|
-
return 0;
|
|
164
|
-
}
|
|
165
|
-
process.stderr.write(`${renderReport(result)}\n`);
|
|
166
|
-
return 1;
|
|
167
|
-
}
|
|
168
|
-
catch (err) {
|
|
169
|
-
const msg = String(err).replace(/\r?\n/g, " ");
|
|
170
|
-
process.stderr.write(`scope-lifecycle parity: harness error -- ${msg}\n`);
|
|
171
|
-
return 2;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
175
|
-
process.exit(runParityCli());
|
|
176
|
-
}
|
|
177
|
-
//# sourceMappingURL=scope-lifecycle-parity.js.map
|
package/dist/session-parity.d.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
export interface Capture {
|
|
3
|
-
readonly exitCode: number;
|
|
4
|
-
readonly stdout: string;
|
|
5
|
-
readonly stderr: string;
|
|
6
|
-
}
|
|
7
|
-
export interface Fixture {
|
|
8
|
-
readonly root: string;
|
|
9
|
-
readonly head: string;
|
|
10
|
-
}
|
|
11
|
-
export interface ParityCase {
|
|
12
|
-
readonly name: string;
|
|
13
|
-
readonly setup: () => Fixture;
|
|
14
|
-
readonly tier: "quick" | "gated";
|
|
15
|
-
readonly bypass: boolean;
|
|
16
|
-
readonly nowIso: string;
|
|
17
|
-
readonly runner?: (command: readonly string[], projectRoot: string) => {
|
|
18
|
-
code: number;
|
|
19
|
-
stdout: string;
|
|
20
|
-
stderr: string;
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
export declare const PARITY_CASES: readonly ParityCase[];
|
|
24
|
-
export interface ParityDiff {
|
|
25
|
-
readonly name: string;
|
|
26
|
-
readonly exitMismatch: boolean;
|
|
27
|
-
readonly stdoutMismatch: boolean;
|
|
28
|
-
readonly pythonExit: number;
|
|
29
|
-
readonly tsExit: number;
|
|
30
|
-
readonly pythonStdout: string;
|
|
31
|
-
readonly tsStdout: string;
|
|
32
|
-
}
|
|
33
|
-
export interface ParityResult {
|
|
34
|
-
readonly ok: boolean;
|
|
35
|
-
readonly diffs: ParityDiff[];
|
|
36
|
-
}
|
|
37
|
-
export declare function runParity(): ParityResult;
|
|
38
|
-
export declare function renderReport(result: ParityResult): string;
|
|
39
|
-
//# sourceMappingURL=session-parity.d.ts.map
|
package/dist/session-parity.js
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Golden-output parity harness (#1787 s1): runs BOTH the Python oracle
|
|
4
|
-
* (`scripts/verify_session_ritual.py`) and the ported TS session core over
|
|
5
|
-
* shared fixtures (cache-off) and diffs JSON stdout + exit codes.
|
|
6
|
-
*/
|
|
7
|
-
import { execFileSync, spawnSync } from "node:child_process";
|
|
8
|
-
import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
-
import { tmpdir } from "node:os";
|
|
10
|
-
import { dirname, join, resolve } from "node:path";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
|
-
import { emitVerifyJson, newRitualStatePayload, ritualStep, verifySessionRitual, writeRitualState, } from "@deftai/directive-core/session";
|
|
13
|
-
const FIXED_NOW = "2026-06-09T01:00:00Z";
|
|
14
|
-
function initGitRepo(policy = { sessionRitualStalenessHours: 4 }) {
|
|
15
|
-
const root = mkdtempSync(join(tmpdir(), "deft-session-parity-"));
|
|
16
|
-
writeFileSync(join(root, "README.md"), "fixture\n", "utf8");
|
|
17
|
-
mkdirSync(join(root, "vbrief"), { recursive: true });
|
|
18
|
-
writeFileSync(join(root, "vbrief", "PROJECT-DEFINITION.vbrief.json"), JSON.stringify({
|
|
19
|
-
vBRIEFInfo: { version: "0.6" },
|
|
20
|
-
plan: { title: "T", status: "running", items: [], policy },
|
|
21
|
-
}), "utf8");
|
|
22
|
-
execFileSync("git", ["init", "-q"], { cwd: root, encoding: "utf8" });
|
|
23
|
-
execFileSync("git", ["config", "user.email", "parity@test.local"], {
|
|
24
|
-
cwd: root,
|
|
25
|
-
encoding: "utf8",
|
|
26
|
-
});
|
|
27
|
-
execFileSync("git", ["config", "user.name", "deft-parity"], { cwd: root, encoding: "utf8" });
|
|
28
|
-
execFileSync("git", ["add", "-A"], { cwd: root, encoding: "utf8" });
|
|
29
|
-
execFileSync("git", ["commit", "-q", "-m", "init"], {
|
|
30
|
-
cwd: root,
|
|
31
|
-
encoding: "utf8",
|
|
32
|
-
env: {
|
|
33
|
-
...process.env,
|
|
34
|
-
GIT_AUTHOR_NAME: "deft-parity",
|
|
35
|
-
GIT_AUTHOR_EMAIL: "parity@test.local",
|
|
36
|
-
GIT_COMMITTER_NAME: "deft-parity",
|
|
37
|
-
GIT_COMMITTER_EMAIL: "parity@test.local",
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
const head = execFileSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).trim();
|
|
41
|
-
return { root, head };
|
|
42
|
-
}
|
|
43
|
-
function fakeGitRunner(head, worktree) {
|
|
44
|
-
return (_root, args) => {
|
|
45
|
-
if (args[0] === "rev-parse" && args[1] === "--verify" && args[2] === "HEAD") {
|
|
46
|
-
return { code: 0, stdout: head, stderr: "" };
|
|
47
|
-
}
|
|
48
|
-
if (args[0] === "rev-parse" && args[1] === "--show-toplevel") {
|
|
49
|
-
return { code: 0, stdout: worktree, stderr: "" };
|
|
50
|
-
}
|
|
51
|
-
return { code: 0, stdout: "", stderr: "" };
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function writeFreshState(fixture, startedAt, gated = {}) {
|
|
55
|
-
const now = new Date(startedAt);
|
|
56
|
-
const worktree = realpathSync(fixture.root);
|
|
57
|
-
writeRitualState(fixture.root, newRitualStatePayload({
|
|
58
|
-
sessionId: "parity-session",
|
|
59
|
-
gitHead: fixture.head,
|
|
60
|
-
worktreePath: worktree,
|
|
61
|
-
startedAt: now,
|
|
62
|
-
quickSteps: {
|
|
63
|
-
alignment: ritualStep({ ok: true, ts: now }),
|
|
64
|
-
branch_policy: ritualStep({ ok: true, ts: now }),
|
|
65
|
-
triage_welcome: ritualStep({ ok: true, ts: now }),
|
|
66
|
-
},
|
|
67
|
-
gatedSteps: gated,
|
|
68
|
-
}));
|
|
69
|
-
}
|
|
70
|
-
function loadPyModule(_deftRoot, name, rel) {
|
|
71
|
-
return `spec = importlib.util.spec_from_file_location(${JSON.stringify(name)}, root / ${JSON.stringify(rel)})
|
|
72
|
-
mod = importlib.util.module_from_spec(spec)
|
|
73
|
-
sys.modules[${JSON.stringify(name)}] = mod
|
|
74
|
-
spec.loader.exec_module(mod)`;
|
|
75
|
-
}
|
|
76
|
-
function runPythonVerify(deftRoot, fixture, scenario) {
|
|
77
|
-
const runnerSnippet = scenario.runner
|
|
78
|
-
? `def runner(args, cwd):
|
|
79
|
-
return (0, "OK", "")`
|
|
80
|
-
: "";
|
|
81
|
-
const script = `import importlib.util, json, sys
|
|
82
|
-
from datetime import datetime, UTC
|
|
83
|
-
from pathlib import Path
|
|
84
|
-
root = Path(${JSON.stringify(deftRoot)})
|
|
85
|
-
fixture = Path(${JSON.stringify(fixture.root)})
|
|
86
|
-
${loadPyModule(deftRoot, "verify_session_ritual", "scripts/verify_session_ritual.py")}
|
|
87
|
-
${runnerSnippet}
|
|
88
|
-
now = datetime.fromisoformat(${JSON.stringify(scenario.nowIso.replace("Z", "+00:00"))})
|
|
89
|
-
kwargs = dict(tier=${JSON.stringify(scenario.tier)}, now=now, bypass=${scenario.bypass ? "True" : "False"})
|
|
90
|
-
${scenario.runner ? "kwargs['runner'] = runner" : ""}
|
|
91
|
-
result = mod.verify(fixture, **kwargs)
|
|
92
|
-
print(json.dumps({"ready": result.code == 0, "exit_code": result.code, "tier": result.tier, "message": result.message, "state_path": str(result.state_path), "bypassed": result.bypassed, "would_fail_code": result.would_fail_code}, sort_keys=True))
|
|
93
|
-
sys.exit(result.code)`;
|
|
94
|
-
const result = spawnSync("uv", ["run", "python", "-c", script], {
|
|
95
|
-
cwd: deftRoot,
|
|
96
|
-
encoding: "utf8",
|
|
97
|
-
env: { ...process.env, DEFT_CACHE_DISABLE: "1", PYTHONUTF8: "1" },
|
|
98
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
99
|
-
});
|
|
100
|
-
return {
|
|
101
|
-
exitCode: result.status ?? 2,
|
|
102
|
-
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
103
|
-
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
function runTsVerify(fixture, scenario) {
|
|
107
|
-
const result = verifySessionRitual(fixture.root, {
|
|
108
|
-
tier: scenario.tier,
|
|
109
|
-
now: new Date(scenario.nowIso),
|
|
110
|
-
bypass: scenario.bypass,
|
|
111
|
-
runGit: fakeGitRunner(fixture.head, realpathSync(fixture.root)),
|
|
112
|
-
runner: scenario.runner,
|
|
113
|
-
});
|
|
114
|
-
return {
|
|
115
|
-
exitCode: result.code,
|
|
116
|
-
stdout: `${emitVerifyJson(result)}\n`,
|
|
117
|
-
stderr: "",
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
const mockGatedRunner = () => () => ({
|
|
121
|
-
code: 0,
|
|
122
|
-
stdout: "OK",
|
|
123
|
-
stderr: "",
|
|
124
|
-
});
|
|
125
|
-
export const PARITY_CASES = [
|
|
126
|
-
{
|
|
127
|
-
name: "quick-tier",
|
|
128
|
-
setup() {
|
|
129
|
-
const fixture = initGitRepo();
|
|
130
|
-
writeFreshState(fixture, FIXED_NOW);
|
|
131
|
-
return fixture;
|
|
132
|
-
},
|
|
133
|
-
tier: "quick",
|
|
134
|
-
bypass: false,
|
|
135
|
-
nowIso: FIXED_NOW,
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
name: "gated-tier",
|
|
139
|
-
setup() {
|
|
140
|
-
const fixture = initGitRepo();
|
|
141
|
-
writeFreshState(fixture, FIXED_NOW);
|
|
142
|
-
return fixture;
|
|
143
|
-
},
|
|
144
|
-
tier: "gated",
|
|
145
|
-
bypass: false,
|
|
146
|
-
nowIso: FIXED_NOW,
|
|
147
|
-
runner: mockGatedRunner(),
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
name: "stale-expiry",
|
|
151
|
-
setup() {
|
|
152
|
-
const fixture = initGitRepo({ sessionRitualStalenessHours: 1 });
|
|
153
|
-
writeFreshState(fixture, "2026-06-08T00:00:00Z");
|
|
154
|
-
return fixture;
|
|
155
|
-
},
|
|
156
|
-
tier: "quick",
|
|
157
|
-
bypass: false,
|
|
158
|
-
nowIso: FIXED_NOW,
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
name: "defer",
|
|
162
|
-
setup() {
|
|
163
|
-
const fixture = initGitRepo();
|
|
164
|
-
const now = new Date(FIXED_NOW);
|
|
165
|
-
const worktree = realpathSync(fixture.root);
|
|
166
|
-
writeRitualState(fixture.root, newRitualStatePayload({
|
|
167
|
-
sessionId: "parity-session",
|
|
168
|
-
gitHead: fixture.head,
|
|
169
|
-
worktreePath: worktree,
|
|
170
|
-
startedAt: now,
|
|
171
|
-
quickSteps: {
|
|
172
|
-
alignment: ritualStep({ ok: true, ts: now }),
|
|
173
|
-
branch_policy: ritualStep({ ok: true, ts: now }),
|
|
174
|
-
triage_welcome: ritualStep({ ok: true, ts: now }),
|
|
175
|
-
},
|
|
176
|
-
gatedSteps: {
|
|
177
|
-
doctor: ritualStep({ ok: true, ts: now, deferredReason: "operator deferred" }),
|
|
178
|
-
cache_fresh: ritualStep({ ok: true, ts: now, deferredReason: "operator deferred" }),
|
|
179
|
-
},
|
|
180
|
-
}));
|
|
181
|
-
return fixture;
|
|
182
|
-
},
|
|
183
|
-
tier: "gated",
|
|
184
|
-
bypass: false,
|
|
185
|
-
nowIso: FIXED_NOW,
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
name: "skip",
|
|
189
|
-
setup() {
|
|
190
|
-
return initGitRepo();
|
|
191
|
-
},
|
|
192
|
-
tier: "quick",
|
|
193
|
-
bypass: true,
|
|
194
|
-
nowIso: FIXED_NOW,
|
|
195
|
-
},
|
|
196
|
-
];
|
|
197
|
-
function resolveDeftRoot() {
|
|
198
|
-
if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
|
|
199
|
-
return resolve(process.env.DEFT_ROOT);
|
|
200
|
-
}
|
|
201
|
-
return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
202
|
-
}
|
|
203
|
-
export function runParity() {
|
|
204
|
-
const deftRoot = resolveDeftRoot();
|
|
205
|
-
const diffs = [];
|
|
206
|
-
for (const scenario of PARITY_CASES) {
|
|
207
|
-
const fixture = scenario.setup();
|
|
208
|
-
try {
|
|
209
|
-
const py = runPythonVerify(deftRoot, fixture, scenario);
|
|
210
|
-
const ts = runTsVerify(fixture, scenario);
|
|
211
|
-
diffs.push({
|
|
212
|
-
name: scenario.name,
|
|
213
|
-
exitMismatch: py.exitCode !== ts.exitCode,
|
|
214
|
-
stdoutMismatch: py.stdout !== ts.stdout,
|
|
215
|
-
pythonExit: py.exitCode,
|
|
216
|
-
tsExit: ts.exitCode,
|
|
217
|
-
pythonStdout: py.stdout,
|
|
218
|
-
tsStdout: ts.stdout,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
finally {
|
|
222
|
-
rmSync(fixture.root, { recursive: true, force: true });
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
const ok = diffs.every((d) => !d.exitMismatch && !d.stdoutMismatch);
|
|
226
|
-
return { ok, diffs };
|
|
227
|
-
}
|
|
228
|
-
export function renderReport(result) {
|
|
229
|
-
if (result.ok) {
|
|
230
|
-
return `session parity: CLEAN -- Python and TS agree on ${result.diffs.length} case(s).`;
|
|
231
|
-
}
|
|
232
|
-
const lines = ["session parity: DIVERGENCE"];
|
|
233
|
-
for (const d of result.diffs) {
|
|
234
|
-
if (d.exitMismatch || d.stdoutMismatch) {
|
|
235
|
-
lines.push(` case: ${d.name}`);
|
|
236
|
-
if (d.exitMismatch) {
|
|
237
|
-
lines.push(` exit mismatch: python=${d.pythonExit} ts=${d.tsExit}`);
|
|
238
|
-
}
|
|
239
|
-
if (d.stdoutMismatch) {
|
|
240
|
-
lines.push(` python stdout: ${JSON.stringify(d.pythonStdout)}`);
|
|
241
|
-
lines.push(` ts stdout: ${JSON.stringify(d.tsStdout)}`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return lines.join("\n");
|
|
246
|
-
}
|
|
247
|
-
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
248
|
-
try {
|
|
249
|
-
const result = runParity();
|
|
250
|
-
if (result.ok) {
|
|
251
|
-
process.stdout.write(`${renderReport(result)}\n`);
|
|
252
|
-
process.exit(0);
|
|
253
|
-
}
|
|
254
|
-
process.stderr.write(`${renderReport(result)}\n`);
|
|
255
|
-
process.exit(1);
|
|
256
|
-
}
|
|
257
|
-
catch (err) {
|
|
258
|
-
process.stderr.write(`session parity: harness error -- ${String(err)}\n`);
|
|
259
|
-
process.exit(2);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
//# sourceMappingURL=session-parity.js.map
|