@deftai/directive-core 0.100.0 → 0.102.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/classify.js +193 -27
- package/dist/authz/decompose-apply.d.ts +14 -0
- package/dist/authz/decompose-apply.js +93 -18
- package/dist/check/cached-orchestrator.d.ts +30 -5
- package/dist/check/cached-orchestrator.js +297 -10
- package/dist/check/cli-native-gates.d.ts +43 -0
- package/dist/check/cli-native-gates.js +84 -0
- package/dist/check/gate-lists.d.ts +10 -4
- package/dist/check/gate-lists.js +19 -5
- package/dist/check/index.d.ts +4 -2
- package/dist/check/index.js +3 -1
- package/dist/check/named-cause.d.ts +45 -0
- package/dist/check/named-cause.js +131 -0
- package/dist/check/orchestrator.js +6 -1
- package/dist/doctor/checks.d.ts +5 -6
- package/dist/doctor/checks.js +13 -56
- package/dist/doctor/help.d.ts +5 -0
- package/dist/doctor/help.js +31 -0
- package/dist/doctor/index.d.ts +2 -0
- package/dist/doctor/index.js +2 -0
- package/dist/doctor/main.js +58 -0
- package/dist/doctor/session-coda.d.ts +82 -0
- package/dist/doctor/session-coda.js +151 -0
- package/dist/doctor/types.d.ts +16 -0
- package/dist/eval/later-graduation.d.ts +39 -0
- package/dist/eval/later-graduation.js +91 -0
- package/dist/eval/report.d.ts +6 -0
- package/dist/eval/report.js +27 -8
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/init-deposit/gitignore.js +2 -0
- package/dist/intake/issue-ingest.js +49 -0
- package/dist/policy/ac-pass-banking.d.ts +58 -0
- package/dist/policy/ac-pass-banking.js +150 -0
- package/dist/policy/ceremony-dial-escalation.d.ts +52 -0
- package/dist/policy/ceremony-dial-escalation.js +92 -0
- package/dist/policy/ceremony-dial.d.ts +32 -1
- package/dist/policy/ceremony-dial.js +128 -1
- package/dist/policy/check-resume.d.ts +9 -38
- package/dist/policy/check-resume.js +18 -156
- package/dist/policy/coverage-debt.d.ts +18 -37
- package/dist/policy/coverage-debt.js +38 -147
- package/dist/policy/index.d.ts +2 -1
- package/dist/policy/index.js +16 -2
- package/dist/preflight-cache/evaluate.d.ts +3 -0
- package/dist/preflight-cache/evaluate.js +3 -0
- package/dist/product-first-done-gate/acceptance.d.ts +25 -0
- package/dist/product-first-done-gate/acceptance.js +269 -0
- package/dist/product-first-done-gate/check-mode.d.ts +45 -0
- package/dist/product-first-done-gate/check-mode.js +148 -0
- package/dist/product-first-done-gate/empty-resolution.d.ts +26 -0
- package/dist/product-first-done-gate/empty-resolution.js +38 -0
- package/dist/product-first-done-gate/evaluate.d.ts +76 -0
- package/dist/product-first-done-gate/evaluate.js +411 -0
- package/dist/product-first-done-gate/index.d.ts +12 -0
- package/dist/product-first-done-gate/index.js +12 -0
- package/dist/product-first-done-gate/types.d.ts +67 -0
- package/dist/product-first-done-gate/types.js +54 -0
- package/dist/run-summary/emit.d.ts +66 -0
- package/dist/run-summary/emit.js +217 -0
- package/dist/run-summary/index.d.ts +5 -0
- package/dist/run-summary/index.js +5 -0
- package/dist/run-summary/path.d.ts +25 -0
- package/dist/run-summary/path.js +78 -0
- package/dist/run-summary/share.d.ts +25 -0
- package/dist/run-summary/share.js +106 -0
- package/dist/run-summary/types.d.ts +134 -0
- package/dist/run-summary/types.js +28 -0
- package/dist/scope/acceptance-activate-gate.d.ts +23 -0
- package/dist/scope/acceptance-activate-gate.js +70 -0
- package/dist/scope/acceptance-evidence.d.ts +34 -0
- package/dist/scope/acceptance-evidence.js +84 -10
- package/dist/scope/decompose.d.ts +5 -0
- package/dist/scope/decompose.js +11 -2
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/transition.js +5 -0
- package/dist/session/ac-pass-banking.d.ts +221 -0
- package/dist/session/ac-pass-banking.js +761 -0
- package/dist/session/deposit-sha.d.ts +49 -0
- package/dist/session/deposit-sha.js +121 -0
- package/dist/session/effort-budget.d.ts +6 -0
- package/dist/session/effort-budget.js +21 -3
- package/dist/session/index.d.ts +5 -1
- package/dist/session/index.js +5 -1
- package/dist/session/orientation-compression.d.ts +127 -0
- package/dist/session/orientation-compression.js +435 -0
- package/dist/session/orientation-state.d.ts +29 -0
- package/dist/session/orientation-state.js +85 -0
- package/dist/session/session-start.d.ts +31 -0
- package/dist/session/session-start.js +219 -22
- package/dist/session/toolchain-preflight.d.ts +79 -0
- package/dist/session/toolchain-preflight.js +211 -0
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +8 -2
- package/dist/triage/bootstrap/index.js +15 -3
- package/dist/triage/classify/index.d.ts +1 -0
- package/dist/triage/classify/index.js +2 -0
- package/dist/triage/classify/label-mirror.js +16 -0
- package/dist/triage/classify/mirror-discovery-tip.d.ts +95 -0
- package/dist/triage/classify/mirror-discovery-tip.js +234 -0
- package/dist/triage/reconcile/reconcile.js +28 -6
- package/dist/triage/summary/index.js +12 -4
- package/dist/triage/welcome/default-mode.js +16 -0
- package/dist/ts-check-lane/run-lane.js +21 -4
- package/dist/vbrief-validate/conformance.js +7 -0
- package/dist/verify-ac/clauses.d.ts +51 -0
- package/dist/verify-ac/clauses.js +490 -0
- package/dist/verify-ac/evaluate.d.ts +55 -0
- package/dist/verify-ac/evaluate.js +145 -0
- package/dist/verify-ac/flag.d.ts +35 -0
- package/dist/verify-ac/flag.js +104 -0
- package/dist/verify-ac/index.d.ts +10 -0
- package/dist/verify-ac/index.js +10 -0
- package/dist/vitest-runner/coverage-debt-teardown.js +16 -4
- package/package.json +15 -3
- package/dist/policy/coverage-check-resume-presets.d.ts +0 -46
- package/dist/policy/coverage-check-resume-presets.js +0 -228
- package/dist/session/coverage-check-resume-nudge.d.ts +0 -34
- package/dist/session/coverage-check-resume-nudge.js +0 -66
package/dist/authz/classify.js
CHANGED
|
@@ -204,11 +204,52 @@ const DOWNLOADER_DECODER_BINS = new Set([
|
|
|
204
204
|
"fetch",
|
|
205
205
|
"socat",
|
|
206
206
|
"lftp",
|
|
207
|
+
// #3288 residual after #3245: crypto / alt-download / pipe / archive bins.
|
|
208
|
+
"gpg",
|
|
209
|
+
"age",
|
|
210
|
+
"zstd",
|
|
211
|
+
"unzstd",
|
|
212
|
+
"sftp",
|
|
213
|
+
"wget2",
|
|
214
|
+
"http",
|
|
215
|
+
"https",
|
|
216
|
+
"yt-dlp",
|
|
217
|
+
"ytdlp",
|
|
218
|
+
"aria2",
|
|
219
|
+
"mbuffer",
|
|
220
|
+
"cpio",
|
|
221
|
+
// #3311 residual after #3288: versioned / alternate write bins.
|
|
222
|
+
"gpg2",
|
|
223
|
+
"gpg1",
|
|
224
|
+
"rage",
|
|
225
|
+
"xh",
|
|
226
|
+
"httpie",
|
|
227
|
+
"wcurl",
|
|
228
|
+
"curlie",
|
|
229
|
+
"uudecode",
|
|
230
|
+
"iconv",
|
|
231
|
+
"gtar",
|
|
232
|
+
"star",
|
|
233
|
+
"gnutar",
|
|
234
|
+
"pax",
|
|
235
|
+
// #3336 residual after #3311: further write bins (oras/ncat/patch peers).
|
|
236
|
+
"oras",
|
|
237
|
+
"lwp-download",
|
|
238
|
+
"ncat",
|
|
239
|
+
"patch",
|
|
240
|
+
"base32",
|
|
241
|
+
"lrzip",
|
|
242
|
+
"unar",
|
|
243
|
+
"cabextract",
|
|
244
|
+
"ditto",
|
|
245
|
+
"dpkg-deb",
|
|
246
|
+
"hg",
|
|
247
|
+
"msguniq",
|
|
207
248
|
]);
|
|
208
249
|
/**
|
|
209
250
|
* Archive extractors / alt writers that can plant via pathish operands without
|
|
210
|
-
* shell redirects (#3245). Used for pathish authz/kill scans (prefer deny)
|
|
211
|
-
* write-shape residual under UAT — not bare curl-class URL fetches.
|
|
251
|
+
* shell redirects (#3245 / #3288 / #3311 / #3336). Used for pathish authz/kill scans (prefer deny)
|
|
252
|
+
* and write-shape residual under UAT — not bare curl-class URL fetches.
|
|
212
253
|
*/
|
|
213
254
|
const ARCHIVE_ALT_WRITE_BINS = new Set([
|
|
214
255
|
"tar",
|
|
@@ -222,7 +263,106 @@ const ARCHIVE_ALT_WRITE_BINS = new Set([
|
|
|
222
263
|
"fetch",
|
|
223
264
|
"socat",
|
|
224
265
|
"lftp",
|
|
266
|
+
// #3288 residual after #3245.
|
|
267
|
+
"gpg",
|
|
268
|
+
"age",
|
|
269
|
+
"zstd",
|
|
270
|
+
"unzstd",
|
|
271
|
+
"sftp",
|
|
272
|
+
"wget2",
|
|
273
|
+
"http",
|
|
274
|
+
"https",
|
|
275
|
+
"yt-dlp",
|
|
276
|
+
"ytdlp",
|
|
277
|
+
"aria2",
|
|
278
|
+
"mbuffer",
|
|
279
|
+
"cpio",
|
|
280
|
+
// #3311 residual after #3288.
|
|
281
|
+
"gpg2",
|
|
282
|
+
"gpg1",
|
|
283
|
+
"rage",
|
|
284
|
+
"xh",
|
|
285
|
+
"httpie",
|
|
286
|
+
"wcurl",
|
|
287
|
+
"curlie",
|
|
288
|
+
"uudecode",
|
|
289
|
+
"iconv",
|
|
290
|
+
"gtar",
|
|
291
|
+
"star",
|
|
292
|
+
"gnutar",
|
|
293
|
+
"pax",
|
|
294
|
+
// #3336 residual after #3311.
|
|
295
|
+
"oras",
|
|
296
|
+
"lwp-download",
|
|
297
|
+
"ncat",
|
|
298
|
+
"patch",
|
|
299
|
+
"base32",
|
|
300
|
+
"lrzip",
|
|
301
|
+
"unar",
|
|
302
|
+
"cabextract",
|
|
303
|
+
"ditto",
|
|
304
|
+
"dpkg-deb",
|
|
305
|
+
"hg",
|
|
306
|
+
"msguniq",
|
|
307
|
+
]);
|
|
308
|
+
/**
|
|
309
|
+
* Bins whose pathish operands are scanned for authz/kill destinations (#3213 / #3245 / #3288 / #3311 / #3336).
|
|
310
|
+
* Prefer a Set over a long `||` chain so coverage counts one membership check, not N branches.
|
|
311
|
+
*/
|
|
312
|
+
const PROTECTED_POSITIONAL_BINS = new Set([
|
|
313
|
+
"scp",
|
|
314
|
+
"certutil",
|
|
315
|
+
"rclone",
|
|
316
|
+
"tar",
|
|
317
|
+
"bsdtar",
|
|
318
|
+
"unzip",
|
|
319
|
+
"7z",
|
|
320
|
+
"7za",
|
|
321
|
+
"7zr",
|
|
322
|
+
"socat",
|
|
323
|
+
"lftp",
|
|
324
|
+
// #3288 residual.
|
|
325
|
+
"sftp",
|
|
326
|
+
"cpio",
|
|
327
|
+
"gpg",
|
|
328
|
+
"age",
|
|
329
|
+
"zstd",
|
|
330
|
+
"unzstd",
|
|
331
|
+
"mbuffer",
|
|
332
|
+
// #3311 residual: versioned crypto + archive/decoder peers.
|
|
333
|
+
"gpg2",
|
|
334
|
+
"gpg1",
|
|
335
|
+
"rage",
|
|
336
|
+
"uudecode",
|
|
337
|
+
"iconv",
|
|
338
|
+
"gtar",
|
|
339
|
+
"star",
|
|
340
|
+
"gnutar",
|
|
341
|
+
"pax",
|
|
342
|
+
// #3336 residual: positional dest writers (ditto/dpkg-deb/hg/lwp-download).
|
|
343
|
+
"oras",
|
|
344
|
+
"lwp-download",
|
|
345
|
+
"ncat",
|
|
346
|
+
"patch",
|
|
347
|
+
"base32",
|
|
348
|
+
"lrzip",
|
|
349
|
+
"unar",
|
|
350
|
+
"cabextract",
|
|
351
|
+
"ditto",
|
|
352
|
+
"dpkg-deb",
|
|
353
|
+
"hg",
|
|
354
|
+
"msguniq",
|
|
225
355
|
]);
|
|
356
|
+
/** wget family (directory-prefix dest flags). */
|
|
357
|
+
const WGET_FAMILY_BINS = new Set(["wget", "wget2"]);
|
|
358
|
+
/** aria2 family (dir dest flags). */
|
|
359
|
+
const ARIA2_FAMILY_BINS = new Set(["aria2c", "aria2"]);
|
|
360
|
+
/** 7z family (attached -oDIR only). */
|
|
361
|
+
const SEVEN_Z_FAMILY_BINS = new Set(["7z", "7za", "7zr"]);
|
|
362
|
+
/** tar family (chdir -C / --directory). Includes GNU/Schily aliases (#3311). */
|
|
363
|
+
const TAR_FAMILY_BINS = new Set(["tar", "bsdtar", "gtar", "star", "gnutar"]);
|
|
364
|
+
/** xh / httpie family (download-dir dest flags) (#3311). */
|
|
365
|
+
const XH_FAMILY_BINS = new Set(["xh", "httpie"]);
|
|
226
366
|
/**
|
|
227
367
|
* File destination flags for downloaders/decoders (#3206).
|
|
228
368
|
* normalizeToken lowercases, so wget `-O` and curl `-o` share `-o`.
|
|
@@ -233,8 +373,13 @@ const DOWNLOADER_FILE_DEST_FLAGS = new Set([
|
|
|
233
373
|
"-o",
|
|
234
374
|
"--output",
|
|
235
375
|
"--output-document",
|
|
376
|
+
"--output-file",
|
|
236
377
|
"-out",
|
|
237
378
|
"--out",
|
|
379
|
+
"--outfile",
|
|
380
|
+
// unar single-dash long form; GNU/others use --output-directory.
|
|
381
|
+
"-output-directory",
|
|
382
|
+
"--output-directory",
|
|
238
383
|
]);
|
|
239
384
|
/** Directory destination flags (curl --output-dir / wget -P / aria2c -d); bin-scoped below. */
|
|
240
385
|
const CURL_DIR_DEST_FLAGS = new Set(["--output-dir"]);
|
|
@@ -246,8 +391,18 @@ const ARIA2C_DIR_DEST_FLAGS = new Set(["-d", "--dir"]);
|
|
|
246
391
|
*/
|
|
247
392
|
const TAR_DIR_DEST_FLAGS_EXACT = new Set(["-C"]);
|
|
248
393
|
const TAR_DIR_DEST_FLAGS_LOWER = new Set(["--directory"]);
|
|
249
|
-
/** unzip extract directory (#3245). */
|
|
394
|
+
/** unzip / cabextract extract directory (#3245 / #3336). */
|
|
250
395
|
const UNZIP_DIR_DEST_FLAGS = new Set(["-d"]);
|
|
396
|
+
const UNZIP_DIR_DEST_BINS = new Set(["unzip", "cabextract"]);
|
|
397
|
+
/** xh / httpie download directory (#3311). */
|
|
398
|
+
const XH_DIR_DEST_FLAGS = new Set(["--download-dir"]);
|
|
399
|
+
/**
|
|
400
|
+
* cpio chdir before extract/create (#3288).
|
|
401
|
+
* Case-sensitive short form: POSIX cpio uses capital `-D`; lower `-d` is a create option bit.
|
|
402
|
+
* Long form `--directory` is accepted lowercased.
|
|
403
|
+
*/
|
|
404
|
+
const CPIO_DIR_DEST_FLAGS_EXACT = new Set(["-D"]);
|
|
405
|
+
const CPIO_DIR_DEST_FLAGS_LOWER = new Set(["--directory"]);
|
|
251
406
|
/**
|
|
252
407
|
* Symlink / hard-link plant bins (#3213). Absent from prior killWriteBins →
|
|
253
408
|
* `ln -sf … .deft-directive-disable` classified empty → UAT fail-open.
|
|
@@ -285,18 +440,27 @@ function isDownloaderDestFlag(flag, bin, rawFlag) {
|
|
|
285
440
|
if (bin === "scp")
|
|
286
441
|
return false;
|
|
287
442
|
// 7z family: only attached `-oDIR` (parsed in attached-short branch), not separate `-o PATH`.
|
|
288
|
-
if (bin
|
|
443
|
+
if (SEVEN_Z_FAMILY_BINS.has(bin)) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
// #3288: cpio `-o` is copy-out (create archive), not a file dest; dest is `-D` / `--directory`.
|
|
447
|
+
if (bin === "cpio") {
|
|
448
|
+
if (rawFlag !== undefined && CPIO_DIR_DEST_FLAGS_EXACT.has(rawFlag))
|
|
449
|
+
return true;
|
|
450
|
+
// Long form is case-insensitive via normalizeToken (`flag` already lowercased).
|
|
451
|
+
if (CPIO_DIR_DEST_FLAGS_LOWER.has(flag))
|
|
452
|
+
return true;
|
|
289
453
|
return false;
|
|
290
454
|
}
|
|
291
455
|
if (DOWNLOADER_FILE_DEST_FLAGS.has(flag))
|
|
292
456
|
return true;
|
|
293
457
|
if (bin === "curl" && CURL_DIR_DEST_FLAGS.has(flag))
|
|
294
458
|
return true;
|
|
295
|
-
if (bin
|
|
459
|
+
if (WGET_FAMILY_BINS.has(bin) && WGET_DIR_DEST_FLAGS.has(flag))
|
|
296
460
|
return true;
|
|
297
|
-
if (bin
|
|
461
|
+
if (ARIA2_FAMILY_BINS.has(bin) && ARIA2C_DIR_DEST_FLAGS.has(flag))
|
|
298
462
|
return true;
|
|
299
|
-
if (bin
|
|
463
|
+
if (TAR_FAMILY_BINS.has(bin)) {
|
|
300
464
|
if (rawFlag !== undefined && TAR_DIR_DEST_FLAGS_EXACT.has(rawFlag))
|
|
301
465
|
return true;
|
|
302
466
|
if (TAR_DIR_DEST_FLAGS_LOWER.has(flag))
|
|
@@ -306,7 +470,9 @@ function isDownloaderDestFlag(flag, bin, rawFlag) {
|
|
|
306
470
|
return true;
|
|
307
471
|
return false;
|
|
308
472
|
}
|
|
309
|
-
if (bin
|
|
473
|
+
if (UNZIP_DIR_DEST_BINS.has(bin) && UNZIP_DIR_DEST_FLAGS.has(flag))
|
|
474
|
+
return true;
|
|
475
|
+
if (XH_FAMILY_BINS.has(bin) && XH_DIR_DEST_FLAGS.has(flag))
|
|
310
476
|
return true;
|
|
311
477
|
return false;
|
|
312
478
|
}
|
|
@@ -386,17 +552,7 @@ function downloaderDecoderDestinations(tokens) {
|
|
|
386
552
|
// Segment breaks (`;`/`\n`/glued ops) prevent following-command overwrite.
|
|
387
553
|
let lastPositionalPath = null;
|
|
388
554
|
const protectedPathish = [];
|
|
389
|
-
const collectsProtectedPositionals = bin
|
|
390
|
-
bin === "certutil" ||
|
|
391
|
-
bin === "rclone" ||
|
|
392
|
-
bin === "tar" ||
|
|
393
|
-
bin === "bsdtar" ||
|
|
394
|
-
bin === "unzip" ||
|
|
395
|
-
bin === "7z" ||
|
|
396
|
-
bin === "7za" ||
|
|
397
|
-
bin === "7zr" ||
|
|
398
|
-
bin === "socat" ||
|
|
399
|
-
bin === "lftp";
|
|
555
|
+
const collectsProtectedPositionals = PROTECTED_POSITIONAL_BINS.has(bin);
|
|
400
556
|
while (i < tokens.length) {
|
|
401
557
|
const raw = tokens[i];
|
|
402
558
|
const n = normalizeToken(raw);
|
|
@@ -463,31 +619,41 @@ function downloaderDecoderDestinations(tokens) {
|
|
|
463
619
|
i++;
|
|
464
620
|
continue;
|
|
465
621
|
}
|
|
466
|
-
|
|
622
|
+
// cpio `-o` is copy-out create mode, not a file dest (#3288).
|
|
623
|
+
if (bin !== "cpio" && n.startsWith("-o") && !n.startsWith("-out") && n.length > 2) {
|
|
467
624
|
dests.push(pathishToken(raw.slice(2)));
|
|
468
625
|
i++;
|
|
469
626
|
continue;
|
|
470
627
|
}
|
|
471
|
-
// wget attached -Pdir
|
|
472
|
-
if (
|
|
628
|
+
// wget / wget2 attached -Pdir
|
|
629
|
+
if (WGET_FAMILY_BINS.has(bin) &&
|
|
630
|
+
n.startsWith("-p") &&
|
|
631
|
+
n.length > 2 &&
|
|
632
|
+
!n.startsWith("-proxy")) {
|
|
473
633
|
dests.push(pathishToken(raw.slice(2)));
|
|
474
634
|
i++;
|
|
475
635
|
continue;
|
|
476
636
|
}
|
|
477
|
-
// aria2c attached -dDIR (#3213)
|
|
478
|
-
if (bin
|
|
637
|
+
// aria2c / aria2 attached -dDIR (#3213 / #3288)
|
|
638
|
+
if (ARIA2_FAMILY_BINS.has(bin) && n.startsWith("-d") && n.length > 2) {
|
|
479
639
|
dests.push(pathishToken(raw.slice(2)));
|
|
480
640
|
i++;
|
|
481
641
|
continue;
|
|
482
642
|
}
|
|
483
|
-
// unzip attached -dDIR (#3245)
|
|
484
|
-
if (bin
|
|
643
|
+
// unzip / cabextract attached -dDIR (#3245 / #3336)
|
|
644
|
+
if (UNZIP_DIR_DEST_BINS.has(bin) && n.startsWith("-d") && n.length > 2) {
|
|
485
645
|
dests.push(pathishToken(raw.slice(2)));
|
|
486
646
|
i++;
|
|
487
647
|
continue;
|
|
488
648
|
}
|
|
489
649
|
// tar attached -CDIR (rare; capital C required)
|
|
490
|
-
if ((bin
|
|
650
|
+
if (TAR_FAMILY_BINS.has(bin) && raw.startsWith("-C") && raw.length > 2) {
|
|
651
|
+
dests.push(pathishToken(raw.slice(2)));
|
|
652
|
+
i++;
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
// cpio attached -DDIR (#3288; capital D required)
|
|
656
|
+
if (bin === "cpio" && raw.startsWith("-D") && raw.length > 2) {
|
|
491
657
|
dests.push(pathishToken(raw.slice(2)));
|
|
492
658
|
i++;
|
|
493
659
|
continue;
|
|
@@ -34,6 +34,20 @@ export interface EvaluateDecomposeStructuralApplyInput {
|
|
|
34
34
|
export declare function sha256Hex(data: Buffer | string): string;
|
|
35
35
|
/** SHA-256 hex of file bytes at path (exact on-disk digest). */
|
|
36
36
|
export declare function sha256FileHex(path: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Exact operator CLI mint command for a structural decompose apply grant (#3291).
|
|
39
|
+
* Paths are project-relative POSIX when inside projectRoot; otherwise as provided.
|
|
40
|
+
* Parent / draft / repo tokens are shell-quoted when they contain whitespace or
|
|
41
|
+
* shell metacharacters. When POSIX and PowerShell apostrophe escapes differ,
|
|
42
|
+
* both forms are emitted on separate labeled lines (`bash:` / `pwsh:`) so each
|
|
43
|
+
* is independently copy-pasteable. Paths containing newline or NUL are refused
|
|
44
|
+
* (no rewritten fake path) so the operator renames before minting. Does not
|
|
45
|
+
* include agent/CI/TTY gates — those remain on the CLI multi-factor path.
|
|
46
|
+
*/
|
|
47
|
+
export declare function formatDecomposeStructuralMintCommand(parentPath: string, draftPath: string, options?: {
|
|
48
|
+
readonly projectRoot?: string;
|
|
49
|
+
readonly repo?: string | null;
|
|
50
|
+
}): string;
|
|
37
51
|
/**
|
|
38
52
|
* Normalize a path to project-relative POSIX form for grant binding compare.
|
|
39
53
|
* Returns null when the path escapes the project root.
|
|
@@ -23,6 +23,69 @@ export function sha256Hex(data) {
|
|
|
23
23
|
export function sha256FileHex(path) {
|
|
24
24
|
return sha256Hex(readFileSync(path));
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Quote a CLI token for operator copy-paste when it has whitespace or shell
|
|
28
|
+
* metacharacters (Greptile #3291 / PR #3300). Safe bare tokens stay unquoted
|
|
29
|
+
* so deny-reason mint hints match the common path shape.
|
|
30
|
+
*
|
|
31
|
+
* Single quotes keep `$()`, backticks, and `!` inert. Apostrophe escape differs
|
|
32
|
+
* by shell: POSIX uses `'\''`; PowerShell uses `''`. Callers that need both
|
|
33
|
+
* pass dialect `"posix"` or `"pwsh"`. Does not rewrite path bytes (no newline
|
|
34
|
+
* flattening) — callers must refuse control chars before quoting.
|
|
35
|
+
*/
|
|
36
|
+
function shellQuoteCliArg(value, dialect) {
|
|
37
|
+
// Allowlist: alnum + common path/repo chars without whitespace or shell meta.
|
|
38
|
+
if (value.length > 0 && /^[A-Za-z0-9_./:@+=,-]+$/.test(value)) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
if (dialect === "pwsh") {
|
|
42
|
+
// PowerShell single-quoted string: only ' is special, doubled as ''.
|
|
43
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
44
|
+
}
|
|
45
|
+
// POSIX single-quote; only ' needs escaping as '\''
|
|
46
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
47
|
+
}
|
|
48
|
+
function mintCommandWithDialect(parent, draft, repo, dialect) {
|
|
49
|
+
return (`deft authz:grant -- --parent ${shellQuoteCliArg(parent, dialect)} --draft ${shellQuoteCliArg(draft, dialect)}` +
|
|
50
|
+
(repo ? ` --repo ${shellQuoteCliArg(repo, dialect)}` : "") +
|
|
51
|
+
" --confirm");
|
|
52
|
+
}
|
|
53
|
+
/** True when a path/repo token cannot be represented as an exact shell mint arg. */
|
|
54
|
+
function hasUnsafeMintControlChars(value) {
|
|
55
|
+
return /[\r\n\0]/.test(value);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Exact operator CLI mint command for a structural decompose apply grant (#3291).
|
|
59
|
+
* Paths are project-relative POSIX when inside projectRoot; otherwise as provided.
|
|
60
|
+
* Parent / draft / repo tokens are shell-quoted when they contain whitespace or
|
|
61
|
+
* shell metacharacters. When POSIX and PowerShell apostrophe escapes differ,
|
|
62
|
+
* both forms are emitted on separate labeled lines (`bash:` / `pwsh:`) so each
|
|
63
|
+
* is independently copy-pasteable. Paths containing newline or NUL are refused
|
|
64
|
+
* (no rewritten fake path) so the operator renames before minting. Does not
|
|
65
|
+
* include agent/CI/TTY gates — those remain on the CLI multi-factor path.
|
|
66
|
+
*/
|
|
67
|
+
export function formatDecomposeStructuralMintCommand(parentPath, draftPath, options) {
|
|
68
|
+
let parent = parentPath.replace(/\\/g, "/");
|
|
69
|
+
let draft = draftPath.replace(/\\/g, "/");
|
|
70
|
+
const root = options?.projectRoot;
|
|
71
|
+
if (root) {
|
|
72
|
+
parent = toProjectRelativePosix(root, parentPath) ?? parent;
|
|
73
|
+
draft = toProjectRelativePosix(root, draftPath) ?? draft;
|
|
74
|
+
}
|
|
75
|
+
const repo = (options?.repo ?? "").trim();
|
|
76
|
+
if (hasUnsafeMintControlChars(parent) ||
|
|
77
|
+
hasUnsafeMintControlChars(draft) ||
|
|
78
|
+
hasUnsafeMintControlChars(repo)) {
|
|
79
|
+
return ("rename parent/draft/repo to remove newline or NUL characters, then run: " +
|
|
80
|
+
"deft authz:grant -- --parent <parent.xbrief.json> --draft <draft.json> [--repo owner/name] --confirm");
|
|
81
|
+
}
|
|
82
|
+
const posix = mintCommandWithDialect(parent, draft, repo, "posix");
|
|
83
|
+
const pwsh = mintCommandWithDialect(parent, draft, repo, "pwsh");
|
|
84
|
+
// Labels on their own lines so the executable command is independently pasteable.
|
|
85
|
+
if (posix === pwsh)
|
|
86
|
+
return posix;
|
|
87
|
+
return `bash:\n${posix}\npwsh:\n${pwsh}`;
|
|
88
|
+
}
|
|
26
89
|
/**
|
|
27
90
|
* Normalize a path to project-relative POSIX form for grant binding compare.
|
|
28
91
|
* Returns null when the path escapes the project root.
|
|
@@ -56,7 +119,7 @@ function normalizeDigest(raw) {
|
|
|
56
119
|
function pathsEqual(a, b) {
|
|
57
120
|
return a.replace(/\\/g, "/") === b.replace(/\\/g, "/");
|
|
58
121
|
}
|
|
59
|
-
function grantValidity(grant, now) {
|
|
122
|
+
function grantValidity(grant, now, mintHint) {
|
|
60
123
|
if (!isHumanOriginGrant(grant)) {
|
|
61
124
|
const kind = grant.origin.kind;
|
|
62
125
|
if (isRejectedOriginKind(kind)) {
|
|
@@ -65,14 +128,14 @@ function grantValidity(grant, now) {
|
|
|
65
128
|
code: "authz-grant-origin-reject",
|
|
66
129
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} origin.kind=${kind} is ` +
|
|
67
130
|
"agent/self-authored and cannot approve a decomposition draft. " +
|
|
68
|
-
|
|
131
|
+
`Human action required: \`${mintHint}\`.`,
|
|
69
132
|
};
|
|
70
133
|
}
|
|
71
134
|
return {
|
|
72
135
|
ok: false,
|
|
73
136
|
code: "authz-grant-origin-reject",
|
|
74
137
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} lacks human-origin provenance. ` +
|
|
75
|
-
|
|
138
|
+
`Human action required: \`${mintHint}\`.`,
|
|
76
139
|
};
|
|
77
140
|
}
|
|
78
141
|
if (grant.semantics.revokedAt !== null) {
|
|
@@ -86,7 +149,8 @@ function grantValidity(grant, now) {
|
|
|
86
149
|
return {
|
|
87
150
|
ok: false,
|
|
88
151
|
code: "authz-grant-single-use-spent",
|
|
89
|
-
reason: `Directive denied scope:decompose apply: single-use grant ${grant.id} already spent at ${grant.semantics.usedAt}
|
|
152
|
+
reason: `Directive denied scope:decompose apply: single-use grant ${grant.id} already spent at ${grant.semantics.usedAt}. ` +
|
|
153
|
+
`Human action required: \`${mintHint}\`.`,
|
|
90
154
|
};
|
|
91
155
|
}
|
|
92
156
|
if (grant.semantics.expiresAt !== null) {
|
|
@@ -96,7 +160,7 @@ function grantValidity(grant, now) {
|
|
|
96
160
|
ok: false,
|
|
97
161
|
code: "authz-grant-expired",
|
|
98
162
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} expired at ${grant.semantics.expiresAt}. ` +
|
|
99
|
-
|
|
163
|
+
`Human action required: \`${mintHint}\`.`,
|
|
100
164
|
};
|
|
101
165
|
}
|
|
102
166
|
}
|
|
@@ -116,11 +180,16 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
116
180
|
const targetRel = toProjectRelativePosix(input.projectRoot, input.draftPath);
|
|
117
181
|
const projectAbs = resolve(input.projectRoot);
|
|
118
182
|
const grants = input.grants ?? listGrants(input.projectRoot);
|
|
183
|
+
const mintHint = formatDecomposeStructuralMintCommand(input.parentPath, input.draftPath, {
|
|
184
|
+
projectRoot: input.projectRoot,
|
|
185
|
+
repo: input.repo,
|
|
186
|
+
});
|
|
119
187
|
if (parentRel === null || targetRel === null) {
|
|
120
188
|
return {
|
|
121
189
|
allowed: false,
|
|
122
190
|
code: "authz-grant-scope-deny",
|
|
123
|
-
reason: "Directive denied scope:decompose apply: parent or draft path is outside the project root."
|
|
191
|
+
reason: "Directive denied scope:decompose apply: parent or draft path is outside the project root. " +
|
|
192
|
+
`Human action required: \`${mintHint}\` with paths inside the project root.`,
|
|
124
193
|
humanApprovalRef: null,
|
|
125
194
|
draftDigest: digest,
|
|
126
195
|
};
|
|
@@ -128,7 +197,7 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
128
197
|
let lastReject = null;
|
|
129
198
|
for (const grant of grants) {
|
|
130
199
|
if (!evidenceSatisfiesImplementationApproval({ grant })) {
|
|
131
|
-
const validity = grantValidity(grant, now);
|
|
200
|
+
const validity = grantValidity(grant, now, mintHint);
|
|
132
201
|
if (!validity.ok) {
|
|
133
202
|
lastReject = { code: validity.code, reason: validity.reason, grantId: grant.id };
|
|
134
203
|
continue;
|
|
@@ -136,12 +205,12 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
136
205
|
lastReject = {
|
|
137
206
|
code: "authz-grant-origin-reject",
|
|
138
207
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} does not satisfy ` +
|
|
139
|
-
|
|
208
|
+
`human-origin implementation approval. Human action required: \`${mintHint}\`.`,
|
|
140
209
|
grantId: grant.id,
|
|
141
210
|
};
|
|
142
211
|
continue;
|
|
143
212
|
}
|
|
144
|
-
const validity = grantValidity(grant, now);
|
|
213
|
+
const validity = grantValidity(grant, now, mintHint);
|
|
145
214
|
if (!validity.ok) {
|
|
146
215
|
lastReject = { code: validity.code, reason: validity.reason, grantId: grant.id };
|
|
147
216
|
continue;
|
|
@@ -150,7 +219,7 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
150
219
|
lastReject = {
|
|
151
220
|
code: "authz-grant-scope-deny",
|
|
152
221
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} does not include operation ` +
|
|
153
|
-
`'${SCOPE_DECOMPOSE_APPLY_STRUCTURAL}'. Human action required:
|
|
222
|
+
`'${SCOPE_DECOMPOSE_APPLY_STRUCTURAL}'. Human action required: \`${mintHint}\`.`,
|
|
154
223
|
grantId: grant.id,
|
|
155
224
|
};
|
|
156
225
|
continue;
|
|
@@ -165,7 +234,7 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
165
234
|
code: "authz-grant-binding-incomplete",
|
|
166
235
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} is missing required ` +
|
|
167
236
|
"structural bindings (contentDigest, parentPath, targetPath). " +
|
|
168
|
-
|
|
237
|
+
`Human action required: \`${mintHint}\`.`,
|
|
169
238
|
grantId: grant.id,
|
|
170
239
|
};
|
|
171
240
|
continue;
|
|
@@ -174,7 +243,8 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
174
243
|
lastReject = {
|
|
175
244
|
code: "authz-grant-binding-incomplete",
|
|
176
245
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} does not bind project ` +
|
|
177
|
-
"identity (repo and/or worktree).
|
|
246
|
+
"identity (repo and/or worktree). " +
|
|
247
|
+
`Human action required: \`${mintHint}\`.`,
|
|
178
248
|
grantId: grant.id,
|
|
179
249
|
};
|
|
180
250
|
continue;
|
|
@@ -184,7 +254,8 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
184
254
|
code: "authz-grant-digest-mismatch",
|
|
185
255
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} is bound to draft digest ` +
|
|
186
256
|
`${boundDigest.slice(0, 12)}… but the draft bytes hash to ${digest.slice(0, 12)}…. ` +
|
|
187
|
-
"Any content change after approval invalidates the grant.
|
|
257
|
+
"Any content change after approval invalidates the grant. " +
|
|
258
|
+
`Human action required: \`${mintHint}\`.`,
|
|
188
259
|
grantId: grant.id,
|
|
189
260
|
};
|
|
190
261
|
continue;
|
|
@@ -193,7 +264,8 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
193
264
|
lastReject = {
|
|
194
265
|
code: "authz-grant-parent-mismatch",
|
|
195
266
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} is bound to parent ` +
|
|
196
|
-
`'${boundParent}' but apply targets '${parentRel}'
|
|
267
|
+
`'${boundParent}' but apply targets '${parentRel}'. ` +
|
|
268
|
+
`Human action required: \`${mintHint}\`.`,
|
|
197
269
|
grantId: grant.id,
|
|
198
270
|
};
|
|
199
271
|
continue;
|
|
@@ -202,7 +274,8 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
202
274
|
lastReject = {
|
|
203
275
|
code: "authz-grant-target-mismatch",
|
|
204
276
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} is bound to draft path ` +
|
|
205
|
-
`'${boundTarget}' but apply uses '${targetRel}'
|
|
277
|
+
`'${boundTarget}' but apply uses '${targetRel}'. ` +
|
|
278
|
+
`Human action required: \`${mintHint}\`.`,
|
|
206
279
|
grantId: grant.id,
|
|
207
280
|
};
|
|
208
281
|
continue;
|
|
@@ -214,7 +287,8 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
214
287
|
lastReject = {
|
|
215
288
|
code: "authz-grant-project-mismatch",
|
|
216
289
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} is bound to repo ` +
|
|
217
|
-
`'${boundRepo}' which does not match the apply context
|
|
290
|
+
`'${boundRepo}' which does not match the apply context. ` +
|
|
291
|
+
`Human action required: \`${mintHint}\`.`,
|
|
218
292
|
grantId: grant.id,
|
|
219
293
|
};
|
|
220
294
|
continue;
|
|
@@ -228,7 +302,8 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
228
302
|
lastReject = {
|
|
229
303
|
code: "authz-grant-project-mismatch",
|
|
230
304
|
reason: `Directive denied scope:decompose apply: grant ${grant.id} is bound to worktree ` +
|
|
231
|
-
`'${boundWorktree}' which does not match project root '${projectAbs}'
|
|
305
|
+
`'${boundWorktree}' which does not match project root '${projectAbs}'. ` +
|
|
306
|
+
`Human action required: \`${mintHint}\`.`,
|
|
232
307
|
grantId: grant.id,
|
|
233
308
|
};
|
|
234
309
|
continue;
|
|
@@ -257,7 +332,7 @@ export function evaluateDecomposeStructuralApply(input) {
|
|
|
257
332
|
code: "authz-grant-missing",
|
|
258
333
|
reason: "Directive denied scope:decompose apply: no human-origin grant covers " +
|
|
259
334
|
`'${SCOPE_DECOMPOSE_APPLY_STRUCTURAL}' for this draft digest. ` +
|
|
260
|
-
|
|
335
|
+
`Human action required: \`${mintHint}\` ` +
|
|
261
336
|
"(self-authored lifecycle/dispatch tokens do not count). --check remains ungated.",
|
|
262
337
|
humanApprovalRef: null,
|
|
263
338
|
draftDigest: digest,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TaskRunResult } from "../cache/task-cache/types.js";
|
|
2
|
+
import { type ToolchainPreflightResult } from "../session/toolchain-preflight.js";
|
|
2
3
|
import { type CheckOrchestratorSeams } from "./context.js";
|
|
3
4
|
export interface CachedCheckOptions extends CheckOrchestratorSeams {
|
|
4
5
|
readonly onGateStart?: (gateId: string) => void;
|
|
@@ -6,16 +7,40 @@ export interface CachedCheckOptions extends CheckOrchestratorSeams {
|
|
|
6
7
|
readonly gateSpawnFn?: (gateId: string, taskBin: string, taskArgs: string[], opts: {
|
|
7
8
|
cwd: string;
|
|
8
9
|
env?: NodeJS.ProcessEnv;
|
|
9
|
-
}) => Pick<TaskRunResult, "exitCode" | "stdout" | "stderr"
|
|
10
|
+
}) => Pick<TaskRunResult, "exitCode" | "stdout" | "stderr"> & {
|
|
11
|
+
spawnError?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* #3282: inject preflight (tests). When omitted, runs live toolchain preflight.
|
|
15
|
+
* Pass `null` to skip preflight entirely (legacy test paths).
|
|
16
|
+
*/
|
|
17
|
+
readonly preflight?: ToolchainPreflightResult | null;
|
|
18
|
+
/** #3282: session id for run-summary lines (default: new UUID). */
|
|
19
|
+
readonly sessionId?: string;
|
|
20
|
+
/** #3282: disable run-summary emission (tests). */
|
|
21
|
+
readonly emitRunSummary?: boolean;
|
|
22
|
+
/** PATH lookup seam (tests). */
|
|
23
|
+
readonly which?: (name: string) => string | null;
|
|
24
|
+
/** Override global CLI binary when dispatching CLI-native gates (#3335). */
|
|
25
|
+
readonly cliBin?: string | null;
|
|
10
26
|
}
|
|
11
27
|
/**
|
|
12
28
|
* Run check gates sequentially with content-hash caching (#1713).
|
|
13
29
|
* Falls back to fail-open execution for undeclared / non-cacheable gates.
|
|
14
30
|
*
|
|
15
|
-
* Gate order
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
31
|
+
* Gate order (#3284 / #3188):
|
|
32
|
+
* 1. Product AC (`verify:ac`) first — fail-fast; never skippable when commands exist
|
|
33
|
+
* 2. Hygiene preflight (may be advisory under pressure / degraded)
|
|
34
|
+
* 3. Suite last (`ts:check-lane`)
|
|
35
|
+
*
|
|
36
|
+
* Modes (env / ceremony dial / hard budget — see resolveProductFirstCheckMode):
|
|
37
|
+
* - full: AC hard → hygiene hard → suite
|
|
38
|
+
* - pressure: AC hard → hygiene advisory → suite
|
|
39
|
+
* - rapid: AC only (ceremony dial rapid/minimal positive content)
|
|
40
|
+
*
|
|
41
|
+
* #3282: toolchain preflight enables degraded skip report when go-task/pnpm
|
|
42
|
+
* are missing; gate failures print named cause + remedy; run-summary JSONL
|
|
43
|
+
* is appended when DEFT_RUN_SUMMARY_PATH is set (fail-open).
|
|
19
44
|
*/
|
|
20
45
|
export declare function dispatchCachedTaskCheck(frameworkRoot: string, projectRoot: string, options?: CachedCheckOptions): number;
|
|
21
46
|
//# sourceMappingURL=cached-orchestrator.d.ts.map
|