@deftai/directive 0.119.12 → 0.120.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/authz.js +41 -7
- package/dist/changelog-check.js +14 -6
- package/dist/check.js +5 -0
- package/dist/dispatch.d.ts +2 -2
- package/dist/dispatch.js +21 -0
- package/dist/init-cli/update.js +7 -0
- package/dist/ownership-doctor.d.ts +10 -0
- package/dist/ownership-doctor.js +96 -0
- package/dist/ownership-fix.d.ts +10 -0
- package/dist/ownership-fix.js +93 -0
- package/dist/pre-pr-run.d.ts +39 -0
- package/dist/pre-pr-run.js +514 -0
- package/dist/vbrief-preflight.js +6 -0
- package/dist/verify-completed-tracked.d.ts +4 -1
- package/dist/verify-completed-tracked.js +60 -9
- package/dist/verify-consumer-header-placeholder.d.ts +12 -0
- package/dist/verify-consumer-header-placeholder.js +73 -0
- package/dist/verify-ownership.d.ts +10 -0
- package/dist/verify-ownership.js +90 -0
- package/dist/verify-persisted-planning-narratives.d.ts +12 -0
- package/dist/verify-persisted-planning-narratives.js +82 -0
- package/dist/verify-review-monitor.js +40 -11
- package/dist/wsl-ownership-guard.d.ts +2 -0
- package/dist/wsl-ownership-guard.js +15 -0
- package/package.json +3 -3
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent-facing pre-PR controller entry (#4912 Limb 3).
|
|
4
|
+
* `deft pre-pr:run` starts the prescribed workflow. Opening the skill file
|
|
5
|
+
* is not completion. Generic mark-complete cannot mint a pass.
|
|
6
|
+
*/
|
|
7
|
+
import { completeRun, computeControllerObservedHash, controllerPublisher, digestApprovedCriteria, evaluateLivePrePrCheck, getDefaultPrePrStore, markComplete, noteSkillFileOpen, observeCommandPhase, PRE_PR_PHASE_IDS, phaseSpec, resolveRecordFromStore, startControllerRun, submitReviewerReport, } from "@deftai/directive-core/pre-pr-controller";
|
|
8
|
+
import { isDirectEntrypoint } from "./entrypoint.js";
|
|
9
|
+
const USAGE = `Usage: deft pre-pr:run [options]
|
|
10
|
+
|
|
11
|
+
Start the repository-controlled pre-PR workflow. Opening the skill file is
|
|
12
|
+
not completion. A generic mark-complete cannot mint a pass.
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--repo <owner/name>
|
|
16
|
+
--base-sha <sha>
|
|
17
|
+
--head-sha <sha>
|
|
18
|
+
--tree-hash <sha>
|
|
19
|
+
--pr-body-hash <hex>
|
|
20
|
+
--pr-node-id <id>
|
|
21
|
+
--approved-revision <sha>
|
|
22
|
+
--scope <path> (repeatable)
|
|
23
|
+
--acceptance <text>
|
|
24
|
+
--generation <n>
|
|
25
|
+
--skill-version <ver>
|
|
26
|
+
--policy-version <ver>
|
|
27
|
+
--run-id <id> lookup hint only
|
|
28
|
+
--complete publisher-backed complete (fails without observables)
|
|
29
|
+
--observe-command record a command-observable phase before --complete
|
|
30
|
+
--observe-semantic record semantic / final-no-change evidence
|
|
31
|
+
--phase <id>
|
|
32
|
+
--command <text>
|
|
33
|
+
--exit-code <n> required with --observe-command; omit is a parse error
|
|
34
|
+
--input-hash <hex>
|
|
35
|
+
--skip-reason <text> skip also requires a non-zero --exit-code
|
|
36
|
+
--supplied-contents-hash <hex>
|
|
37
|
+
--controller-observed-hash <hex>
|
|
38
|
+
optional; must match independently hashed reviewed files
|
|
39
|
+
--criteria-digest <hex>
|
|
40
|
+
--reviewed-file <path> (repeatable)
|
|
41
|
+
--reviewer-report-ref <ref>
|
|
42
|
+
--mark-complete always refuses; cannot mint a pass
|
|
43
|
+
--skill-open always refuses; opening the skill is not completion
|
|
44
|
+
--evaluate evaluate live binding against the private store
|
|
45
|
+
--json
|
|
46
|
+
--help
|
|
47
|
+
`;
|
|
48
|
+
export function parseArgs(argv) {
|
|
49
|
+
const parsed = {
|
|
50
|
+
repo: "",
|
|
51
|
+
baseSha: "",
|
|
52
|
+
headSha: "",
|
|
53
|
+
treeHash: "",
|
|
54
|
+
prBodyHash: "",
|
|
55
|
+
prNodeId: null,
|
|
56
|
+
approvedRevision: "",
|
|
57
|
+
scope: [],
|
|
58
|
+
acceptance: "",
|
|
59
|
+
generation: 1,
|
|
60
|
+
skillVersion: "0.1",
|
|
61
|
+
policyVersion: "1",
|
|
62
|
+
runId: null,
|
|
63
|
+
complete: false,
|
|
64
|
+
observeCommand: false,
|
|
65
|
+
observeSemantic: false,
|
|
66
|
+
phase: null,
|
|
67
|
+
command: "",
|
|
68
|
+
exitCode: null,
|
|
69
|
+
inputHash: null,
|
|
70
|
+
skipReason: null,
|
|
71
|
+
suppliedContentsHash: "",
|
|
72
|
+
controllerObservedHash: "",
|
|
73
|
+
criteriaDigest: null,
|
|
74
|
+
reviewedFiles: [],
|
|
75
|
+
reviewerReportRef: null,
|
|
76
|
+
markComplete: false,
|
|
77
|
+
skillOpen: false,
|
|
78
|
+
evaluate: false,
|
|
79
|
+
json: false,
|
|
80
|
+
help: false,
|
|
81
|
+
};
|
|
82
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
83
|
+
const arg = argv[i];
|
|
84
|
+
if (arg === "--help" || arg === "-h") {
|
|
85
|
+
parsed.help = true;
|
|
86
|
+
}
|
|
87
|
+
else if (arg === "--json") {
|
|
88
|
+
parsed.json = true;
|
|
89
|
+
}
|
|
90
|
+
else if (arg === "--complete") {
|
|
91
|
+
parsed.complete = true;
|
|
92
|
+
}
|
|
93
|
+
else if (arg === "--observe-command") {
|
|
94
|
+
parsed.observeCommand = true;
|
|
95
|
+
}
|
|
96
|
+
else if (arg === "--observe-semantic") {
|
|
97
|
+
parsed.observeSemantic = true;
|
|
98
|
+
}
|
|
99
|
+
else if (arg === "--phase" || arg?.startsWith("--phase=")) {
|
|
100
|
+
const value = arg === "--phase" ? argv[i + 1] : arg.slice("--phase=".length);
|
|
101
|
+
if (value === undefined)
|
|
102
|
+
return { ...parsed, error: "argument --phase: expected one argument" };
|
|
103
|
+
parsed.phase = value;
|
|
104
|
+
if (arg === "--phase")
|
|
105
|
+
i += 1;
|
|
106
|
+
}
|
|
107
|
+
else if (arg === "--command" || arg?.startsWith("--command=")) {
|
|
108
|
+
const value = arg === "--command" ? argv[i + 1] : arg.slice("--command=".length);
|
|
109
|
+
if (value === undefined)
|
|
110
|
+
return { ...parsed, error: "argument --command: expected one argument" };
|
|
111
|
+
parsed.command = value;
|
|
112
|
+
if (arg === "--command")
|
|
113
|
+
i += 1;
|
|
114
|
+
}
|
|
115
|
+
else if (arg === "--exit-code" || arg?.startsWith("--exit-code=")) {
|
|
116
|
+
const value = arg === "--exit-code" ? argv[i + 1] : arg.slice("--exit-code=".length);
|
|
117
|
+
if (value === undefined) {
|
|
118
|
+
return { ...parsed, error: "argument --exit-code: expected one argument" };
|
|
119
|
+
}
|
|
120
|
+
const n = Number.parseInt(value, 10);
|
|
121
|
+
if (!Number.isFinite(n)) {
|
|
122
|
+
return { ...parsed, error: "argument --exit-code: expected an integer" };
|
|
123
|
+
}
|
|
124
|
+
parsed.exitCode = n;
|
|
125
|
+
if (arg === "--exit-code")
|
|
126
|
+
i += 1;
|
|
127
|
+
}
|
|
128
|
+
else if (arg === "--input-hash" || arg?.startsWith("--input-hash=")) {
|
|
129
|
+
const value = arg === "--input-hash" ? argv[i + 1] : arg.slice("--input-hash=".length);
|
|
130
|
+
if (value === undefined) {
|
|
131
|
+
return { ...parsed, error: "argument --input-hash: expected one argument" };
|
|
132
|
+
}
|
|
133
|
+
parsed.inputHash = value;
|
|
134
|
+
if (arg === "--input-hash")
|
|
135
|
+
i += 1;
|
|
136
|
+
}
|
|
137
|
+
else if (arg === "--skip-reason" || arg?.startsWith("--skip-reason=")) {
|
|
138
|
+
const value = arg === "--skip-reason" ? argv[i + 1] : arg.slice("--skip-reason=".length);
|
|
139
|
+
if (value === undefined) {
|
|
140
|
+
return { ...parsed, error: "argument --skip-reason: expected one argument" };
|
|
141
|
+
}
|
|
142
|
+
parsed.skipReason = value;
|
|
143
|
+
if (arg === "--skip-reason")
|
|
144
|
+
i += 1;
|
|
145
|
+
}
|
|
146
|
+
else if (arg === "--supplied-contents-hash" || arg?.startsWith("--supplied-contents-hash=")) {
|
|
147
|
+
const value = arg === "--supplied-contents-hash"
|
|
148
|
+
? argv[i + 1]
|
|
149
|
+
: arg.slice("--supplied-contents-hash=".length);
|
|
150
|
+
if (value === undefined) {
|
|
151
|
+
return { ...parsed, error: "argument --supplied-contents-hash: expected one argument" };
|
|
152
|
+
}
|
|
153
|
+
parsed.suppliedContentsHash = value;
|
|
154
|
+
if (arg === "--supplied-contents-hash")
|
|
155
|
+
i += 1;
|
|
156
|
+
}
|
|
157
|
+
else if (arg === "--controller-observed-hash" ||
|
|
158
|
+
arg?.startsWith("--controller-observed-hash=")) {
|
|
159
|
+
const value = arg === "--controller-observed-hash"
|
|
160
|
+
? argv[i + 1]
|
|
161
|
+
: arg.slice("--controller-observed-hash=".length);
|
|
162
|
+
if (value === undefined) {
|
|
163
|
+
return {
|
|
164
|
+
...parsed,
|
|
165
|
+
error: "argument --controller-observed-hash: expected one argument",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
parsed.controllerObservedHash = value;
|
|
169
|
+
if (arg === "--controller-observed-hash")
|
|
170
|
+
i += 1;
|
|
171
|
+
}
|
|
172
|
+
else if (arg === "--criteria-digest" || arg?.startsWith("--criteria-digest=")) {
|
|
173
|
+
const value = arg === "--criteria-digest" ? argv[i + 1] : arg.slice("--criteria-digest=".length);
|
|
174
|
+
if (value === undefined) {
|
|
175
|
+
return { ...parsed, error: "argument --criteria-digest: expected one argument" };
|
|
176
|
+
}
|
|
177
|
+
parsed.criteriaDigest = value;
|
|
178
|
+
if (arg === "--criteria-digest")
|
|
179
|
+
i += 1;
|
|
180
|
+
}
|
|
181
|
+
else if (arg === "--reviewed-file" || arg?.startsWith("--reviewed-file=")) {
|
|
182
|
+
const value = arg === "--reviewed-file" ? argv[i + 1] : arg.slice("--reviewed-file=".length);
|
|
183
|
+
if (value === undefined) {
|
|
184
|
+
return { ...parsed, error: "argument --reviewed-file: expected one argument" };
|
|
185
|
+
}
|
|
186
|
+
parsed.reviewedFiles.push(value);
|
|
187
|
+
if (arg === "--reviewed-file")
|
|
188
|
+
i += 1;
|
|
189
|
+
}
|
|
190
|
+
else if (arg === "--reviewer-report-ref" || arg?.startsWith("--reviewer-report-ref=")) {
|
|
191
|
+
const value = arg === "--reviewer-report-ref" ? argv[i + 1] : arg.slice("--reviewer-report-ref=".length);
|
|
192
|
+
if (value === undefined) {
|
|
193
|
+
return { ...parsed, error: "argument --reviewer-report-ref: expected one argument" };
|
|
194
|
+
}
|
|
195
|
+
parsed.reviewerReportRef = value;
|
|
196
|
+
if (arg === "--reviewer-report-ref")
|
|
197
|
+
i += 1;
|
|
198
|
+
}
|
|
199
|
+
else if (arg === "--mark-complete") {
|
|
200
|
+
parsed.markComplete = true;
|
|
201
|
+
}
|
|
202
|
+
else if (arg === "--skill-open") {
|
|
203
|
+
parsed.skillOpen = true;
|
|
204
|
+
}
|
|
205
|
+
else if (arg === "--evaluate") {
|
|
206
|
+
parsed.evaluate = true;
|
|
207
|
+
}
|
|
208
|
+
else if (arg === "--repo" || arg?.startsWith("--repo=")) {
|
|
209
|
+
const value = arg === "--repo" ? argv[i + 1] : arg.slice("--repo=".length);
|
|
210
|
+
if (value === undefined)
|
|
211
|
+
return { ...parsed, error: "argument --repo: expected one argument" };
|
|
212
|
+
parsed.repo = value;
|
|
213
|
+
if (arg === "--repo")
|
|
214
|
+
i += 1;
|
|
215
|
+
}
|
|
216
|
+
else if (arg === "--base-sha" || arg?.startsWith("--base-sha=")) {
|
|
217
|
+
const value = arg === "--base-sha" ? argv[i + 1] : arg.slice("--base-sha=".length);
|
|
218
|
+
if (value === undefined)
|
|
219
|
+
return { ...parsed, error: "argument --base-sha: expected one argument" };
|
|
220
|
+
parsed.baseSha = value;
|
|
221
|
+
if (arg === "--base-sha")
|
|
222
|
+
i += 1;
|
|
223
|
+
}
|
|
224
|
+
else if (arg === "--head-sha" || arg?.startsWith("--head-sha=")) {
|
|
225
|
+
const value = arg === "--head-sha" ? argv[i + 1] : arg.slice("--head-sha=".length);
|
|
226
|
+
if (value === undefined)
|
|
227
|
+
return { ...parsed, error: "argument --head-sha: expected one argument" };
|
|
228
|
+
parsed.headSha = value;
|
|
229
|
+
if (arg === "--head-sha")
|
|
230
|
+
i += 1;
|
|
231
|
+
}
|
|
232
|
+
else if (arg === "--tree-hash" || arg?.startsWith("--tree-hash=")) {
|
|
233
|
+
const value = arg === "--tree-hash" ? argv[i + 1] : arg.slice("--tree-hash=".length);
|
|
234
|
+
if (value === undefined)
|
|
235
|
+
return { ...parsed, error: "argument --tree-hash: expected one argument" };
|
|
236
|
+
parsed.treeHash = value;
|
|
237
|
+
if (arg === "--tree-hash")
|
|
238
|
+
i += 1;
|
|
239
|
+
}
|
|
240
|
+
else if (arg === "--pr-body-hash" || arg?.startsWith("--pr-body-hash=")) {
|
|
241
|
+
const value = arg === "--pr-body-hash" ? argv[i + 1] : arg.slice("--pr-body-hash=".length);
|
|
242
|
+
if (value === undefined) {
|
|
243
|
+
return { ...parsed, error: "argument --pr-body-hash: expected one argument" };
|
|
244
|
+
}
|
|
245
|
+
parsed.prBodyHash = value;
|
|
246
|
+
if (arg === "--pr-body-hash")
|
|
247
|
+
i += 1;
|
|
248
|
+
}
|
|
249
|
+
else if (arg === "--pr-node-id" || arg?.startsWith("--pr-node-id=")) {
|
|
250
|
+
const value = arg === "--pr-node-id" ? argv[i + 1] : arg.slice("--pr-node-id=".length);
|
|
251
|
+
if (value === undefined) {
|
|
252
|
+
return { ...parsed, error: "argument --pr-node-id: expected one argument" };
|
|
253
|
+
}
|
|
254
|
+
parsed.prNodeId = value;
|
|
255
|
+
if (arg === "--pr-node-id")
|
|
256
|
+
i += 1;
|
|
257
|
+
}
|
|
258
|
+
else if (arg === "--approved-revision" || arg?.startsWith("--approved-revision=")) {
|
|
259
|
+
const value = arg === "--approved-revision" ? argv[i + 1] : arg.slice("--approved-revision=".length);
|
|
260
|
+
if (value === undefined) {
|
|
261
|
+
return { ...parsed, error: "argument --approved-revision: expected one argument" };
|
|
262
|
+
}
|
|
263
|
+
parsed.approvedRevision = value;
|
|
264
|
+
if (arg === "--approved-revision")
|
|
265
|
+
i += 1;
|
|
266
|
+
}
|
|
267
|
+
else if (arg === "--scope" || arg?.startsWith("--scope=")) {
|
|
268
|
+
const value = arg === "--scope" ? argv[i + 1] : arg.slice("--scope=".length);
|
|
269
|
+
if (value === undefined)
|
|
270
|
+
return { ...parsed, error: "argument --scope: expected one argument" };
|
|
271
|
+
parsed.scope.push(value);
|
|
272
|
+
if (arg === "--scope")
|
|
273
|
+
i += 1;
|
|
274
|
+
}
|
|
275
|
+
else if (arg === "--acceptance" || arg?.startsWith("--acceptance=")) {
|
|
276
|
+
const value = arg === "--acceptance" ? argv[i + 1] : arg.slice("--acceptance=".length);
|
|
277
|
+
if (value === undefined) {
|
|
278
|
+
return { ...parsed, error: "argument --acceptance: expected one argument" };
|
|
279
|
+
}
|
|
280
|
+
parsed.acceptance = value;
|
|
281
|
+
if (arg === "--acceptance")
|
|
282
|
+
i += 1;
|
|
283
|
+
}
|
|
284
|
+
else if (arg === "--generation" || arg?.startsWith("--generation=")) {
|
|
285
|
+
const value = arg === "--generation" ? argv[i + 1] : arg.slice("--generation=".length);
|
|
286
|
+
if (value === undefined) {
|
|
287
|
+
return { ...parsed, error: "argument --generation: expected one argument" };
|
|
288
|
+
}
|
|
289
|
+
const n = Number.parseInt(value, 10);
|
|
290
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
291
|
+
return { ...parsed, error: "argument --generation: expected a positive integer" };
|
|
292
|
+
}
|
|
293
|
+
parsed.generation = n;
|
|
294
|
+
if (arg === "--generation")
|
|
295
|
+
i += 1;
|
|
296
|
+
}
|
|
297
|
+
else if (arg === "--skill-version" || arg?.startsWith("--skill-version=")) {
|
|
298
|
+
const value = arg === "--skill-version" ? argv[i + 1] : arg.slice("--skill-version=".length);
|
|
299
|
+
if (value === undefined) {
|
|
300
|
+
return { ...parsed, error: "argument --skill-version: expected one argument" };
|
|
301
|
+
}
|
|
302
|
+
parsed.skillVersion = value;
|
|
303
|
+
if (arg === "--skill-version")
|
|
304
|
+
i += 1;
|
|
305
|
+
}
|
|
306
|
+
else if (arg === "--policy-version" || arg?.startsWith("--policy-version=")) {
|
|
307
|
+
const value = arg === "--policy-version" ? argv[i + 1] : arg.slice("--policy-version=".length);
|
|
308
|
+
if (value === undefined) {
|
|
309
|
+
return { ...parsed, error: "argument --policy-version: expected one argument" };
|
|
310
|
+
}
|
|
311
|
+
parsed.policyVersion = value;
|
|
312
|
+
if (arg === "--policy-version")
|
|
313
|
+
i += 1;
|
|
314
|
+
}
|
|
315
|
+
else if (arg === "--run-id" || arg?.startsWith("--run-id=")) {
|
|
316
|
+
const value = arg === "--run-id" ? argv[i + 1] : arg.slice("--run-id=".length);
|
|
317
|
+
if (value === undefined)
|
|
318
|
+
return { ...parsed, error: "argument --run-id: expected one argument" };
|
|
319
|
+
parsed.runId = value;
|
|
320
|
+
if (arg === "--run-id")
|
|
321
|
+
i += 1;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return parsed;
|
|
328
|
+
}
|
|
329
|
+
function emit(json, payload, text, err) {
|
|
330
|
+
const line = json ? `${JSON.stringify(payload)}\n` : `${text}\n`;
|
|
331
|
+
if (err)
|
|
332
|
+
process.stderr.write(line);
|
|
333
|
+
else
|
|
334
|
+
process.stdout.write(line);
|
|
335
|
+
}
|
|
336
|
+
function asPhaseId(value) {
|
|
337
|
+
return PRE_PR_PHASE_IDS.includes(value) ? value : null;
|
|
338
|
+
}
|
|
339
|
+
export function run(argv) {
|
|
340
|
+
const args = parseArgs(argv);
|
|
341
|
+
if (args.help) {
|
|
342
|
+
process.stdout.write(USAGE);
|
|
343
|
+
return 0;
|
|
344
|
+
}
|
|
345
|
+
if (args.error !== undefined) {
|
|
346
|
+
process.stderr.write(`pre_pr_run: ${args.error}\n`);
|
|
347
|
+
return 2;
|
|
348
|
+
}
|
|
349
|
+
if (args.skillOpen) {
|
|
350
|
+
const d = noteSkillFileOpen();
|
|
351
|
+
emit(args.json, d, d.message, true);
|
|
352
|
+
return 1;
|
|
353
|
+
}
|
|
354
|
+
const store = getDefaultPrePrStore();
|
|
355
|
+
if (args.markComplete) {
|
|
356
|
+
const d = markComplete(store, args.runId ?? "");
|
|
357
|
+
emit(args.json, d, d.message, true);
|
|
358
|
+
return 1;
|
|
359
|
+
}
|
|
360
|
+
if (args.observeCommand || args.observeSemantic) {
|
|
361
|
+
if (args.runId === null || args.runId.length === 0) {
|
|
362
|
+
process.stderr.write("pre_pr_run: observe requires --run-id\n");
|
|
363
|
+
return 2;
|
|
364
|
+
}
|
|
365
|
+
if (args.phase === null || args.phase.length === 0) {
|
|
366
|
+
process.stderr.write("pre_pr_run: observe requires --phase\n");
|
|
367
|
+
return 2;
|
|
368
|
+
}
|
|
369
|
+
const phase = asPhaseId(args.phase);
|
|
370
|
+
if (phase === null) {
|
|
371
|
+
process.stderr.write(`pre_pr_run: unknown phase ${args.phase}\n`);
|
|
372
|
+
return 2;
|
|
373
|
+
}
|
|
374
|
+
const rec = store.getById(args.runId);
|
|
375
|
+
if (args.observeCommand) {
|
|
376
|
+
if (args.exitCode === null) {
|
|
377
|
+
process.stderr.write("pre_pr_run: --observe-command requires --exit-code\n");
|
|
378
|
+
return 2;
|
|
379
|
+
}
|
|
380
|
+
if (args.skipReason !== null && args.skipReason.length > 0 && args.exitCode === 0) {
|
|
381
|
+
process.stderr.write("pre_pr_run: skip requires a non-zero --exit-code plus --skip-reason\n");
|
|
382
|
+
return 2;
|
|
383
|
+
}
|
|
384
|
+
const spec = phaseSpec(phase);
|
|
385
|
+
const d = observeCommandPhase(store, args.runId, {
|
|
386
|
+
phaseId: phase,
|
|
387
|
+
command: args.command.length > 0 ? args.command : (spec.command ?? ""),
|
|
388
|
+
exitCode: args.exitCode,
|
|
389
|
+
inputHash: args.inputHash ?? rec?.inputHash ?? "",
|
|
390
|
+
skipReason: args.skipReason,
|
|
391
|
+
});
|
|
392
|
+
emit(args.json, d, d.message, !d.ok);
|
|
393
|
+
return d.ok ? 0 : 1;
|
|
394
|
+
}
|
|
395
|
+
if (args.suppliedContentsHash.length === 0) {
|
|
396
|
+
process.stderr.write("pre_pr_run: --observe-semantic requires --supplied-contents-hash\n");
|
|
397
|
+
return 2;
|
|
398
|
+
}
|
|
399
|
+
if (args.reviewedFiles.length === 0) {
|
|
400
|
+
process.stderr.write("pre_pr_run: --observe-semantic requires --reviewed-file\n");
|
|
401
|
+
return 2;
|
|
402
|
+
}
|
|
403
|
+
const observed = computeControllerObservedHash({ reviewedFiles: args.reviewedFiles });
|
|
404
|
+
if (observed === null) {
|
|
405
|
+
process.stderr.write("pre_pr_run: --observe-semantic could not hash reviewed-file contents\n");
|
|
406
|
+
return 2;
|
|
407
|
+
}
|
|
408
|
+
if (args.controllerObservedHash.length > 0 && args.controllerObservedHash !== observed) {
|
|
409
|
+
process.stderr.write("pre_pr_run: --controller-observed-hash does not match independently hashed reviewed-file contents\n");
|
|
410
|
+
return 1;
|
|
411
|
+
}
|
|
412
|
+
if (args.suppliedContentsHash !== observed) {
|
|
413
|
+
process.stderr.write("pre_pr_run: --supplied-contents-hash does not match independently hashed reviewed-file contents\n");
|
|
414
|
+
return 1;
|
|
415
|
+
}
|
|
416
|
+
const d = submitReviewerReport(store, args.runId, {
|
|
417
|
+
phaseId: phase,
|
|
418
|
+
reviewedFileManifest: args.reviewedFiles,
|
|
419
|
+
suppliedContentsHash: args.suppliedContentsHash,
|
|
420
|
+
criteriaDigest: args.criteriaDigest ?? rec?.criteria.digest ?? "",
|
|
421
|
+
reviewerReportRef: args.reviewerReportRef,
|
|
422
|
+
controllerObservedHash: observed,
|
|
423
|
+
});
|
|
424
|
+
emit(args.json, d, d.message, !d.ok);
|
|
425
|
+
return d.ok ? 0 : 1;
|
|
426
|
+
}
|
|
427
|
+
if (args.evaluate) {
|
|
428
|
+
if (args.repo.length === 0 || args.baseSha.length === 0 || args.headSha.length === 0) {
|
|
429
|
+
process.stderr.write("pre_pr_run: --evaluate requires --repo --base-sha --head-sha\n");
|
|
430
|
+
return 2;
|
|
431
|
+
}
|
|
432
|
+
if (args.prBodyHash.length === 0) {
|
|
433
|
+
process.stderr.write("pre_pr_run: --evaluate requires --pr-body-hash\n");
|
|
434
|
+
return 2;
|
|
435
|
+
}
|
|
436
|
+
const record = resolveRecordFromStore(store, {
|
|
437
|
+
id: args.runId,
|
|
438
|
+
prNodeId: args.prNodeId,
|
|
439
|
+
});
|
|
440
|
+
const headSource = args.approvedRevision.length > 0
|
|
441
|
+
? args.approvedRevision
|
|
442
|
+
: (record?.criteria.sourceRevisionSha ?? args.headSha);
|
|
443
|
+
const liveApproved = digestApprovedCriteria({
|
|
444
|
+
sourceRevisionSha: headSource,
|
|
445
|
+
scopePaths: args.scope,
|
|
446
|
+
acceptanceText: args.acceptance,
|
|
447
|
+
generation: args.generation,
|
|
448
|
+
});
|
|
449
|
+
const d = evaluateLivePrePrCheck({
|
|
450
|
+
store,
|
|
451
|
+
liveBinding: {
|
|
452
|
+
repo: args.repo,
|
|
453
|
+
baseSha: args.baseSha,
|
|
454
|
+
headSha: args.headSha,
|
|
455
|
+
prNodeId: args.prNodeId,
|
|
456
|
+
prBodyHash: args.prBodyHash,
|
|
457
|
+
},
|
|
458
|
+
presentedRunId: args.runId,
|
|
459
|
+
approvedCriteria: liveApproved,
|
|
460
|
+
currentGeneration: args.generation,
|
|
461
|
+
headCriteria: liveApproved,
|
|
462
|
+
});
|
|
463
|
+
emit(args.json, d, d.message, !d.ok);
|
|
464
|
+
return d.ok ? 0 : 1;
|
|
465
|
+
}
|
|
466
|
+
if (args.complete) {
|
|
467
|
+
if (args.runId === null || args.runId.length === 0) {
|
|
468
|
+
process.stderr.write("pre_pr_run: --complete requires --run-id\n");
|
|
469
|
+
return 2;
|
|
470
|
+
}
|
|
471
|
+
const d = completeRun(store, controllerPublisher(), args.runId);
|
|
472
|
+
emit(args.json, d, d.message, !d.ok);
|
|
473
|
+
return d.ok ? 0 : 1;
|
|
474
|
+
}
|
|
475
|
+
if (args.repo.length === 0 ||
|
|
476
|
+
args.baseSha.length === 0 ||
|
|
477
|
+
args.headSha.length === 0 ||
|
|
478
|
+
args.treeHash.length === 0 ||
|
|
479
|
+
args.prBodyHash.length === 0 ||
|
|
480
|
+
args.approvedRevision.length === 0) {
|
|
481
|
+
process.stderr.write("pre_pr_run: start requires --repo --base-sha --head-sha --tree-hash --pr-body-hash --approved-revision\n");
|
|
482
|
+
return 2;
|
|
483
|
+
}
|
|
484
|
+
const criteria = digestApprovedCriteria({
|
|
485
|
+
sourceRevisionSha: args.approvedRevision,
|
|
486
|
+
scopePaths: args.scope,
|
|
487
|
+
acceptanceText: args.acceptance,
|
|
488
|
+
generation: args.generation,
|
|
489
|
+
});
|
|
490
|
+
const started = startControllerRun(store, {
|
|
491
|
+
repo: args.repo,
|
|
492
|
+
baseSha: args.baseSha,
|
|
493
|
+
headSha: args.headSha,
|
|
494
|
+
treeHash: args.treeHash,
|
|
495
|
+
prBodyHash: args.prBodyHash,
|
|
496
|
+
prNodeId: args.prNodeId,
|
|
497
|
+
criteria,
|
|
498
|
+
skillVersion: args.skillVersion,
|
|
499
|
+
policyVersion: args.policyVersion,
|
|
500
|
+
approvedRevisionSha: args.approvedRevision,
|
|
501
|
+
runId: args.runId ?? undefined,
|
|
502
|
+
});
|
|
503
|
+
if (!started.ok) {
|
|
504
|
+
emit(args.json, started.decision, started.decision.message, true);
|
|
505
|
+
return 1;
|
|
506
|
+
}
|
|
507
|
+
const payload = { ok: started.ok, runId: started.runId, hint: "run id is a lookup hint only" };
|
|
508
|
+
emit(args.json, payload, `started ${started.runId} (lookup hint only; private store is authoritative)`, false);
|
|
509
|
+
return 0;
|
|
510
|
+
}
|
|
511
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
512
|
+
process.exit(run(process.argv.slice(2)));
|
|
513
|
+
}
|
|
514
|
+
//# sourceMappingURL=pre-pr-run.js.map
|
package/dist/vbrief-preflight.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
2
3
|
import { fileURLToPath } from "node:url";
|
|
3
4
|
import { emitJson, evaluate, PREFLIGHT_USAGE_HINT } from "@deftai/directive-core/preflight";
|
|
4
5
|
import { scanWorkClaimForBriefPath } from "@deftai/directive-core/scm";
|
|
6
|
+
import { refuseIfWslOwnershipBlocked } from "./wsl-ownership-guard.js";
|
|
5
7
|
const HELP_TEXT = `usage: xbrief:preflight [--vbrief-path PATH | PATH] [--json] [--help]
|
|
6
8
|
|
|
7
9
|
Implementation-intent gate (#810): exits 0 only when the xBRIEF is in
|
|
@@ -100,6 +102,10 @@ export function run(argv, scan = scanWorkClaimForBriefPath) {
|
|
|
100
102
|
return 2;
|
|
101
103
|
}
|
|
102
104
|
const vbriefPath = args.vbriefPath;
|
|
105
|
+
// #1617: gate the brief's tree, not process.cwd() (cross-directory callers).
|
|
106
|
+
const blocked = refuseIfWslOwnershipBlocked(dirname(resolve(vbriefPath)));
|
|
107
|
+
if (blocked !== null)
|
|
108
|
+
return blocked;
|
|
103
109
|
const result = evaluate(vbriefPath);
|
|
104
110
|
let scanLines = [];
|
|
105
111
|
try {
|
|
@@ -6,9 +6,12 @@ interface ParsedArgs {
|
|
|
6
6
|
issue: number | null;
|
|
7
7
|
quiet: boolean;
|
|
8
8
|
skipGh: boolean;
|
|
9
|
+
/** Opt-in discovery when --issue is set; unscoped defaults to on (#3495). */
|
|
10
|
+
discover: boolean | null;
|
|
11
|
+
enforce: boolean;
|
|
9
12
|
error?: string;
|
|
10
13
|
}
|
|
11
|
-
/** Parse verify-completed-tracked CLI args (#3264 / #3476). */
|
|
14
|
+
/** Parse verify-completed-tracked CLI args (#3264 / #3476 / #3495). */
|
|
12
15
|
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
13
16
|
/** Run the gate and return the process exit code. */
|
|
14
17
|
export declare function run(argv: string[]): number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { COMPLETED_TRACKED_PROGRESS_MIN_BLOBS, COMPLETED_TRACKED_PROGRESS_THRESHOLD_MS, evaluateCompletedTracked, shouldAnnounceProgress, shouldAnnounceUpFrontCount, } from "@deftai/directive-core/lifecycle";
|
|
4
|
+
import { COMPLETED_TRACKED_PROGRESS_MIN_BLOBS, COMPLETED_TRACKED_PROGRESS_THRESHOLD_MS, evaluateCompletedTracked, evaluateShippedClosedDiscovery, shouldAnnounceProgress, shouldAnnounceUpFrontCount, } from "@deftai/directive-core/lifecycle";
|
|
5
5
|
function parseIssueNumber(raw) {
|
|
6
6
|
const trimmed = raw.startsWith("#") ? raw.slice(1) : raw;
|
|
7
7
|
if (!/^\d+$/.test(trimmed)) {
|
|
@@ -10,7 +10,7 @@ function parseIssueNumber(raw) {
|
|
|
10
10
|
const value = Number(trimmed);
|
|
11
11
|
return Number.isInteger(value) && value > 0 ? value : null;
|
|
12
12
|
}
|
|
13
|
-
/** Parse verify-completed-tracked CLI args (#3264 / #3476). */
|
|
13
|
+
/** Parse verify-completed-tracked CLI args (#3264 / #3476 / #3495). */
|
|
14
14
|
export function parseArgs(argv) {
|
|
15
15
|
const parsed = {
|
|
16
16
|
projectRoot: ".",
|
|
@@ -19,6 +19,8 @@ export function parseArgs(argv) {
|
|
|
19
19
|
issue: null,
|
|
20
20
|
quiet: false,
|
|
21
21
|
skipGh: false,
|
|
22
|
+
discover: null,
|
|
23
|
+
enforce: false,
|
|
22
24
|
};
|
|
23
25
|
for (let i = 0; i < argv.length; i += 1) {
|
|
24
26
|
const arg = argv[i];
|
|
@@ -28,6 +30,15 @@ export function parseArgs(argv) {
|
|
|
28
30
|
else if (arg === "--skip-gh") {
|
|
29
31
|
parsed.skipGh = true;
|
|
30
32
|
}
|
|
33
|
+
else if (arg === "--discover") {
|
|
34
|
+
parsed.discover = true;
|
|
35
|
+
}
|
|
36
|
+
else if (arg === "--no-discover") {
|
|
37
|
+
parsed.discover = false;
|
|
38
|
+
}
|
|
39
|
+
else if (arg === "--enforce") {
|
|
40
|
+
parsed.enforce = true;
|
|
41
|
+
}
|
|
31
42
|
else if (arg === "--project-root") {
|
|
32
43
|
const value = argv[i + 1];
|
|
33
44
|
if (value === undefined) {
|
|
@@ -87,6 +98,16 @@ export function parseArgs(argv) {
|
|
|
87
98
|
}
|
|
88
99
|
return parsed;
|
|
89
100
|
}
|
|
101
|
+
function shouldRunDiscovery(args) {
|
|
102
|
+
if (args.discover === false) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (args.discover === true || args.enforce) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
// Warn-first default: unscoped corpus scans discover; --issue N stays land-only.
|
|
109
|
+
return args.issue === null;
|
|
110
|
+
}
|
|
90
111
|
/** Run the gate and return the process exit code. */
|
|
91
112
|
export function run(argv) {
|
|
92
113
|
const args = parseArgs(argv);
|
|
@@ -97,7 +118,7 @@ export function run(argv) {
|
|
|
97
118
|
const projectRoot = resolve(args.projectRoot);
|
|
98
119
|
const thresholdMs = resolveProgressThresholdMs();
|
|
99
120
|
let announced = false;
|
|
100
|
-
const
|
|
121
|
+
const land = evaluateCompletedTracked(projectRoot, {
|
|
101
122
|
quiet: args.quiet,
|
|
102
123
|
repo: args.repo,
|
|
103
124
|
tip: args.tip,
|
|
@@ -117,15 +138,45 @@ export function run(argv) {
|
|
|
117
138
|
writeProgress(event);
|
|
118
139
|
},
|
|
119
140
|
});
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
141
|
+
let discoveryCode = 0;
|
|
142
|
+
let discoveryMessage = "";
|
|
143
|
+
let discoveryStream = "none";
|
|
144
|
+
if (shouldRunDiscovery(args)) {
|
|
145
|
+
const discovery = evaluateShippedClosedDiscovery(projectRoot, {
|
|
146
|
+
quiet: args.quiet,
|
|
147
|
+
repo: args.repo,
|
|
148
|
+
tip: args.tip,
|
|
149
|
+
skipGh: args.skipGh,
|
|
150
|
+
enforce: args.enforce,
|
|
151
|
+
});
|
|
152
|
+
discoveryCode = discovery.code;
|
|
153
|
+
discoveryMessage = discovery.message;
|
|
154
|
+
discoveryStream = discovery.stream;
|
|
155
|
+
}
|
|
156
|
+
// Scoped land fail-closed must not be softened by discovery warn (#3495 Reject).
|
|
157
|
+
if (land.message.length > 0) {
|
|
158
|
+
if (land.stream === "stdout") {
|
|
159
|
+
process.stdout.write(`${land.message}\n`);
|
|
123
160
|
}
|
|
124
|
-
else if (
|
|
125
|
-
process.stderr.write(`${
|
|
161
|
+
else if (land.stream === "stderr") {
|
|
162
|
+
process.stderr.write(`${land.message}\n`);
|
|
126
163
|
}
|
|
127
164
|
}
|
|
128
|
-
|
|
165
|
+
if (discoveryMessage.length > 0) {
|
|
166
|
+
if (discoveryStream === "stdout") {
|
|
167
|
+
process.stdout.write(`${discoveryMessage}\n`);
|
|
168
|
+
}
|
|
169
|
+
else if (discoveryStream === "stderr") {
|
|
170
|
+
process.stderr.write(`${discoveryMessage}\n`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (land.code === 2 || discoveryCode === 2) {
|
|
174
|
+
return 2;
|
|
175
|
+
}
|
|
176
|
+
if (land.code === 1 || discoveryCode === 1) {
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
return 0;
|
|
129
180
|
}
|
|
130
181
|
function resolveProgressThresholdMs() {
|
|
131
182
|
const raw = process.env.DEFT_COMPLETED_TRACKED_PROGRESS_MS;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface ParsedArgs {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
quiet: boolean;
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Parse verify-consumer-header-placeholder CLI args. */
|
|
8
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
9
|
+
/** Run the gate and return the process exit code. */
|
|
10
|
+
export declare function run(argv: string[]): number;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=verify-consumer-header-placeholder.d.ts.map
|