@deftai/directive-core 0.96.0 → 0.98.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 +291 -0
- package/dist/cache/archive.js +10 -4
- package/dist/check/cached-orchestrator.d.ts +5 -0
- package/dist/check/cached-orchestrator.js +18 -1
- package/dist/check/gate-lists.d.ts +20 -0
- package/dist/check/gate-lists.js +54 -9
- package/dist/check/index.d.ts +1 -1
- package/dist/check/index.js +1 -1
- package/dist/check/orchestrator.d.ts +4 -0
- package/dist/check/orchestrator.js +4 -0
- package/dist/consumer-check-contract/evaluate.d.ts +124 -0
- package/dist/consumer-check-contract/evaluate.js +699 -0
- package/dist/consumer-check-contract/index.d.ts +5 -0
- package/dist/consumer-check-contract/index.js +5 -0
- package/dist/delivery-attempt/disk-begin.d.ts +51 -0
- package/dist/delivery-attempt/disk-begin.js +68 -0
- package/dist/delivery-attempt/evaluate.d.ts +26 -0
- package/dist/delivery-attempt/evaluate.js +443 -0
- package/dist/delivery-attempt/fingerprint.d.ts +32 -0
- package/dist/delivery-attempt/fingerprint.js +100 -0
- package/dist/delivery-attempt/handoff.d.ts +25 -0
- package/dist/delivery-attempt/handoff.js +102 -0
- package/dist/delivery-attempt/index.d.ts +17 -0
- package/dist/delivery-attempt/index.js +17 -0
- package/dist/delivery-attempt/ledger.d.ts +169 -0
- package/dist/delivery-attempt/ledger.js +758 -0
- package/dist/delivery-attempt/material-delta.d.ts +38 -0
- package/dist/delivery-attempt/material-delta.js +126 -0
- package/dist/delivery-attempt/types.d.ts +210 -0
- package/dist/delivery-attempt/types.js +77 -0
- package/dist/doctor/checks.d.ts +7 -0
- package/dist/doctor/checks.js +83 -0
- package/dist/doctor/index.d.ts +1 -0
- package/dist/doctor/index.js +1 -0
- package/dist/doctor/main.js +12 -0
- package/dist/doctor/openclaw-soft-rebind.d.ts +26 -0
- package/dist/doctor/openclaw-soft-rebind.js +164 -0
- package/dist/hooks/dispatcher.d.ts +7 -1
- package/dist/hooks/dispatcher.js +117 -13
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/init-deposit/gitignore.js +7 -0
- package/dist/init-deposit/hygiene.d.ts +70 -1
- package/dist/init-deposit/hygiene.js +582 -8
- package/dist/init-deposit/init-deposit.js +5 -0
- package/dist/init-deposit/refresh.js +3 -0
- package/dist/init-deposit/scaffold.js +277 -4
- package/dist/init-deposit/skill-discovery-deposit.js +15 -0
- package/dist/policy/check-resume.d.ts +72 -0
- package/dist/policy/check-resume.js +253 -0
- package/dist/policy/coverage-check-resume-presets.d.ts +46 -0
- package/dist/policy/coverage-check-resume-presets.js +228 -0
- package/dist/policy/coverage-debt.d.ts +76 -0
- package/dist/policy/coverage-debt.js +262 -0
- package/dist/policy/index.d.ts +3 -0
- package/dist/policy/index.js +50 -21
- package/dist/pr-merge-readiness/ci-gate.d.ts +29 -1
- package/dist/pr-merge-readiness/ci-gate.js +191 -24
- package/dist/pr-merge-readiness/compute.js +10 -1
- package/dist/pr-merge-readiness/index.d.ts +2 -1
- package/dist/pr-merge-readiness/index.js +2 -1
- package/dist/pr-merge-readiness/output.js +14 -0
- package/dist/pr-merge-readiness/platform-status.d.ts +29 -0
- package/dist/pr-merge-readiness/platform-status.js +49 -0
- package/dist/pr-watch/constants.d.ts +10 -0
- package/dist/pr-watch/constants.js +12 -1
- package/dist/pr-watch/main.js +16 -1
- package/dist/pr-watch/probe.js +13 -1
- package/dist/pr-watch/types.d.ts +3 -2
- package/dist/pr-watch/watch.js +14 -7
- package/dist/release/auto-hatch.d.ts +114 -0
- package/dist/release/auto-hatch.js +301 -0
- package/dist/release/coverage-debt-ledger.d.ts +22 -0
- package/dist/release/coverage-debt-ledger.js +157 -0
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.js +3 -0
- package/dist/release/pipeline.js +164 -12
- package/dist/release/suite-stamp.d.ts +44 -0
- package/dist/release/suite-stamp.js +133 -0
- package/dist/release/types.d.ts +19 -0
- package/dist/scope-provenance/digest.d.ts +67 -0
- package/dist/scope-provenance/digest.js +188 -0
- package/dist/scope-provenance/evaluate.d.ts +82 -0
- package/dist/scope-provenance/evaluate.js +528 -0
- package/dist/scope-provenance/index.d.ts +6 -0
- package/dist/scope-provenance/index.js +6 -0
- package/dist/session/compact-ritual.d.ts +96 -0
- package/dist/session/compact-ritual.js +237 -0
- package/dist/session/compact-ritual.spec.d.ts +2 -0
- package/dist/session/compact-ritual.spec.js +21 -0
- package/dist/session/coverage-check-resume-nudge.d.ts +34 -0
- package/dist/session/coverage-check-resume-nudge.js +66 -0
- package/dist/session/index.d.ts +3 -0
- package/dist/session/index.js +3 -0
- package/dist/session/openclaw-soft-rebind-deposit.d.ts +46 -0
- package/dist/session/openclaw-soft-rebind-deposit.js +165 -0
- package/dist/session/session-start.js +21 -0
- package/dist/test-boundary/evaluate.d.ts +54 -0
- package/dist/test-boundary/evaluate.js +368 -0
- package/dist/test-boundary/index.d.ts +6 -0
- package/dist/test-boundary/index.js +6 -0
- package/dist/test-boundary/policy.d.ts +52 -0
- package/dist/test-boundary/policy.js +182 -0
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +15 -1
- package/dist/triage/classify/label-mirror.d.ts +31 -1
- package/dist/triage/classify/label-mirror.js +78 -6
- package/dist/triage/help/registry-data.d.ts +6 -6
- package/dist/triage/help/registry-data.js +12 -3
- package/dist/vbrief-activate/activate.js +22 -6
- package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
- package/dist/vbrief-validate/plan-hooks.js +54 -0
- package/dist/xbrief/styles.js +33 -17
- package/package.json +7 -3
|
@@ -501,18 +501,287 @@ export function mergeCoreGuardWorkflowRefresh(existing, desired) {
|
|
|
501
501
|
return desired;
|
|
502
502
|
return desired.slice(0, idx) + existingCheckout + desired.slice(idx + desiredCheckout.length);
|
|
503
503
|
}
|
|
504
|
+
/**
|
|
505
|
+
* Embedded python3 content check for package.json / lockfiles when co-travelling
|
|
506
|
+
* with .deft/core/** (#3193). Mirrors TS `isUpgradePinPathContentAllowed`.
|
|
507
|
+
* Uses a single-quoted heredoc so GHA does not interpolate shell variables
|
|
508
|
+
* inside the Python source (BASE/HEAD are argv).
|
|
509
|
+
*/
|
|
510
|
+
function coreGuardPinContentPython() {
|
|
511
|
+
// Keep this compact: deposited into every consumer workflow on init/update.
|
|
512
|
+
return [
|
|
513
|
+
' python3 - "$BASE_SHA" "$HEAD_SHA" <<\'PY\'',
|
|
514
|
+
"import json, re, subprocess, sys",
|
|
515
|
+
"base_sha, head_sha = sys.argv[1], sys.argv[2]",
|
|
516
|
+
"def git_show(sha, path):",
|
|
517
|
+
" try:",
|
|
518
|
+
" return subprocess.check_output(['git','show',f'{sha}:{path}'], text=True, stderr=subprocess.DEVNULL)",
|
|
519
|
+
" except subprocess.CalledProcessError:",
|
|
520
|
+
" return ''",
|
|
521
|
+
"def changed_names():",
|
|
522
|
+
" out = subprocess.check_output(['git','diff','--name-only',base_sha,head_sha], text=True)",
|
|
523
|
+
" return [p for p in out.splitlines() if p]",
|
|
524
|
+
"def is_dir_key(n):",
|
|
525
|
+
" return n.startswith('@deftai/directive')",
|
|
526
|
+
"DEP_FIELDS = ('dependencies','devDependencies','optionalDependencies','peerDependencies')",
|
|
527
|
+
"def deep_eq(a,b):",
|
|
528
|
+
" if a is b: return True",
|
|
529
|
+
" if type(a) is not type(b): return False",
|
|
530
|
+
" if isinstance(a, dict):",
|
|
531
|
+
" if set(a) != set(b): return False",
|
|
532
|
+
" return all(deep_eq(a[k], b[k]) for k in a)",
|
|
533
|
+
" if isinstance(a, list):",
|
|
534
|
+
" return len(a)==len(b) and all(deep_eq(x,y) for x,y in zip(a,b))",
|
|
535
|
+
" return a == b",
|
|
536
|
+
"def strip_pins(obj):",
|
|
537
|
+
" if not isinstance(obj, dict): return obj",
|
|
538
|
+
" out = {}",
|
|
539
|
+
" for k,v in obj.items():",
|
|
540
|
+
" if k in DEP_FIELDS and isinstance(v, dict):",
|
|
541
|
+
" out[k] = {dk:dv for dk,dv in v.items() if not is_dir_key(dk)}",
|
|
542
|
+
" else:",
|
|
543
|
+
" out[k] = v",
|
|
544
|
+
" return out",
|
|
545
|
+
"def pkg_pin_only(base, head):",
|
|
546
|
+
" try:",
|
|
547
|
+
" b, h = json.loads(base or '{}'), json.loads(head or '{}')",
|
|
548
|
+
" except json.JSONDecodeError:",
|
|
549
|
+
" return False",
|
|
550
|
+
" return deep_eq(strip_pins(b), strip_pins(h))",
|
|
551
|
+
"def collect_deps(pkg):",
|
|
552
|
+
" out = {}",
|
|
553
|
+
" if not isinstance(pkg, dict): return out",
|
|
554
|
+
" for f in DEP_FIELDS:",
|
|
555
|
+
" block = pkg.get(f)",
|
|
556
|
+
" if isinstance(block, dict):",
|
|
557
|
+
" for k,v in block.items():",
|
|
558
|
+
" if isinstance(v, str): out[k]=v",
|
|
559
|
+
" elif isinstance(v, dict) and isinstance(v.get('version'), str): out[k]=v['version']",
|
|
560
|
+
" elif v is not None: out[k]=str(v)",
|
|
561
|
+
" return out",
|
|
562
|
+
"def only_dir_diff(bd, hd):",
|
|
563
|
+
" keys = set(bd)|set(hd)",
|
|
564
|
+
" for k in keys:",
|
|
565
|
+
" if is_dir_key(k): continue",
|
|
566
|
+
" if bd.get(k) != hd.get(k): return False",
|
|
567
|
+
" return True",
|
|
568
|
+
"def npm_root_deps(lock):",
|
|
569
|
+
" pkgs = lock.get('packages') if isinstance(lock, dict) else None",
|
|
570
|
+
" if isinstance(pkgs, dict) and isinstance(pkgs.get(''), dict):",
|
|
571
|
+
" return collect_deps(pkgs[''])",
|
|
572
|
+
" deps = lock.get('dependencies') if isinstance(lock, dict) else None",
|
|
573
|
+
" out = {}",
|
|
574
|
+
" if isinstance(deps, dict):",
|
|
575
|
+
" for k,v in deps.items():",
|
|
576
|
+
" if isinstance(v, dict): out[k]=str(v.get('version', v))",
|
|
577
|
+
" elif isinstance(v, str): out[k]=v",
|
|
578
|
+
" return out",
|
|
579
|
+
"def npm_lock_ok(base, head):",
|
|
580
|
+
" try:",
|
|
581
|
+
" b, h = json.loads(base or '{}'), json.loads(head or '{}')",
|
|
582
|
+
" except json.JSONDecodeError:",
|
|
583
|
+
" return False",
|
|
584
|
+
" br, hr = npm_root_deps(b), npm_root_deps(h)",
|
|
585
|
+
" if not only_dir_diff(br, hr): return False",
|
|
586
|
+
" bp, hp = b.get('packages') or {}, h.get('packages') or {}",
|
|
587
|
+
" if not isinstance(bp, dict): bp = {}",
|
|
588
|
+
" if not isinstance(hp, dict): hp = {}",
|
|
589
|
+
" all_keys = set(bp)|set(hp)",
|
|
590
|
+
" for key in all_keys:",
|
|
591
|
+
" if key == '': continue",
|
|
592
|
+
" if 'node_modules/@deftai/directive' in key or '/@deftai/directive/' in key: continue",
|
|
593
|
+
" if not deep_eq(bp.get(key), hp.get(key)): return False",
|
|
594
|
+
" if not bp and not hp:",
|
|
595
|
+
" def strip_dir(v):",
|
|
596
|
+
" if not isinstance(v, dict): return v",
|
|
597
|
+
" out = {}",
|
|
598
|
+
" for k, val in v.items():",
|
|
599
|
+
" if is_dir_key(k): continue",
|
|
600
|
+
" if isinstance(val, dict):",
|
|
601
|
+
" nxt = dict(val)",
|
|
602
|
+
" if 'dependencies' in val: nxt['dependencies'] = strip_dir(val['dependencies'])",
|
|
603
|
+
" out[k] = nxt",
|
|
604
|
+
" else: out[k] = val",
|
|
605
|
+
" return out",
|
|
606
|
+
" if not deep_eq(strip_dir(b.get('dependencies')), strip_dir(h.get('dependencies'))): return False",
|
|
607
|
+
" return True",
|
|
608
|
+
"def pnpm_root_deps(raw):",
|
|
609
|
+
" out = {}",
|
|
610
|
+
" in_imp = in_root = in_dep = False",
|
|
611
|
+
" cur = None",
|
|
612
|
+
" def unq(s):",
|
|
613
|
+
" s=s.strip()",
|
|
614
|
+
' if len(s)>=2 and s[0]==s[-1] and s[0] in "\'\\"": return s[1:-1]',
|
|
615
|
+
" return s",
|
|
616
|
+
" for line in raw.splitlines():",
|
|
617
|
+
" if re.match(r'^importers:\\s*$', line):",
|
|
618
|
+
" in_imp, in_root, in_dep, cur = True, False, False, None",
|
|
619
|
+
" continue",
|
|
620
|
+
" if not in_imp: continue",
|
|
621
|
+
" if re.match(r'^[^\\s#]', line) and not line.startswith('importers'):",
|
|
622
|
+
" break",
|
|
623
|
+
' if re.match(r"^ {2}(?:\\.|\'\\.\'|\\"\\.\\"):\\s*$", line):',
|
|
624
|
+
" in_root, in_dep, cur = True, False, None",
|
|
625
|
+
" continue",
|
|
626
|
+
" if in_root and re.match(r'^ {2}\\S', line) and not re.match(r'^ {2}\\.', line):",
|
|
627
|
+
" in_root, in_dep, cur = False, False, None",
|
|
628
|
+
" continue",
|
|
629
|
+
" if not in_root: continue",
|
|
630
|
+
" if re.match(r'^ {4}(?:dependencies|devDependencies|optionalDependencies|peerDependencies):\\s*$', line):",
|
|
631
|
+
" in_dep, cur = True, None",
|
|
632
|
+
" continue",
|
|
633
|
+
" if in_dep and re.match(r'^ {4}\\S', line):",
|
|
634
|
+
" in_dep, cur = False, None",
|
|
635
|
+
" continue",
|
|
636
|
+
" if not in_dep: continue",
|
|
637
|
+
" m = re.match(r'^ {6}(.+?):\\s*$', line)",
|
|
638
|
+
" if m:",
|
|
639
|
+
" cur = unq(m.group(1)); continue",
|
|
640
|
+
" if cur:",
|
|
641
|
+
" vm = re.match(r'^ {8}version:\\s*(.+?)\\s*$', line)",
|
|
642
|
+
" if vm:",
|
|
643
|
+
" out[cur] = unq(vm.group(1)); cur = None",
|
|
644
|
+
" return out",
|
|
645
|
+
"def pnpm_packages_by_name(raw):",
|
|
646
|
+
" blocks, out = {}, {}",
|
|
647
|
+
" in_pkg, cur, buf = False, None, []",
|
|
648
|
+
" def flush():",
|
|
649
|
+
" nonlocal cur, buf",
|
|
650
|
+
" if cur is None: return",
|
|
651
|
+
" blocks.setdefault(cur, []).append('\\n'.join(buf))",
|
|
652
|
+
" cur, buf = None, []",
|
|
653
|
+
" def unq(s):",
|
|
654
|
+
" s=s.strip()",
|
|
655
|
+
' if len(s)>=2 and s[0]==s[-1] and s[0] in "\'\\"": return s[1:-1]',
|
|
656
|
+
" return s",
|
|
657
|
+
" for line in raw.splitlines():",
|
|
658
|
+
" if re.match(r'^packages:\\s*$', line):",
|
|
659
|
+
" flush(); in_pkg = True; continue",
|
|
660
|
+
" if not in_pkg: continue",
|
|
661
|
+
" if re.match(r'^[^\\s#]', line) and not line.startswith('packages'):",
|
|
662
|
+
" flush(); break",
|
|
663
|
+
" m = re.match(r'^ {2}(.+?):\\s*$', line)",
|
|
664
|
+
" if m:",
|
|
665
|
+
" flush()",
|
|
666
|
+
" key = unq(m.group(1))",
|
|
667
|
+
" at = key.find('@', 1) if key.startswith('@') else key.find('@')",
|
|
668
|
+
" cur = key[:at] if at > 0 else key",
|
|
669
|
+
" buf = [line]; continue",
|
|
670
|
+
" if cur is not None: buf.append(line)",
|
|
671
|
+
" flush()",
|
|
672
|
+
" for name, blist in blocks.items():",
|
|
673
|
+
" out[name] = '\\n---\\n'.join(sorted(blist))",
|
|
674
|
+
" return out",
|
|
675
|
+
"def pnpm_section_by_name(raw, section):",
|
|
676
|
+
" blocks, out = {}, {}",
|
|
677
|
+
" in_sec, cur, buf = False, None, []",
|
|
678
|
+
" sec_re = re.compile(r'^' + re.escape(section) + r':\\s*$')",
|
|
679
|
+
" def flush():",
|
|
680
|
+
" nonlocal cur, buf",
|
|
681
|
+
" if cur is None: return",
|
|
682
|
+
" blocks.setdefault(cur, []).append('\\n'.join(buf))",
|
|
683
|
+
" cur, buf = None, []",
|
|
684
|
+
" def unq(s):",
|
|
685
|
+
" s=s.strip()",
|
|
686
|
+
' if len(s)>=2 and s[0]==s[-1] and s[0] in "\'\\"": return s[1:-1]',
|
|
687
|
+
" return s",
|
|
688
|
+
" for line in raw.splitlines():",
|
|
689
|
+
" if sec_re.match(line):",
|
|
690
|
+
" flush(); in_sec = True; continue",
|
|
691
|
+
" if not in_sec: continue",
|
|
692
|
+
" if re.match(r'^[^\\s#]', line) and not line.startswith(section):",
|
|
693
|
+
" flush(); break",
|
|
694
|
+
" m = re.match(r'^ {2}(.+?):\\s*$', line)",
|
|
695
|
+
" if m:",
|
|
696
|
+
" flush()",
|
|
697
|
+
" key = unq(m.group(1))",
|
|
698
|
+
" at = key.find('@', 1) if key.startswith('@') else key.find('@')",
|
|
699
|
+
" cur = key[:at] if at > 0 else key",
|
|
700
|
+
" buf = [line]; continue",
|
|
701
|
+
" if cur is not None: buf.append(line)",
|
|
702
|
+
" flush()",
|
|
703
|
+
" for name, blist in blocks.items():",
|
|
704
|
+
" out[name] = '\\n---\\n'.join(sorted(blist))",
|
|
705
|
+
" return out",
|
|
706
|
+
"def pnpm_ok(base, head):",
|
|
707
|
+
" br, hr = pnpm_root_deps(base), pnpm_root_deps(head)",
|
|
708
|
+
" if not only_dir_diff(br, hr): return False",
|
|
709
|
+
" for section in ('packages', 'snapshots'):",
|
|
710
|
+
" bp, hp = pnpm_section_by_name(base, section), pnpm_section_by_name(head, section)",
|
|
711
|
+
" for name in set(bp)|set(hp):",
|
|
712
|
+
" if is_dir_key(name): continue",
|
|
713
|
+
" if bp.get(name) != hp.get(name): return False",
|
|
714
|
+
" return True",
|
|
715
|
+
"def yarn_blocks(raw):",
|
|
716
|
+
" blocks = {}",
|
|
717
|
+
" names, buf = [], []",
|
|
718
|
+
" def flush():",
|
|
719
|
+
" nonlocal names, buf",
|
|
720
|
+
" if not names: return",
|
|
721
|
+
" body = '\\n'.join(buf)",
|
|
722
|
+
" for n in names: blocks[n] = body",
|
|
723
|
+
" names, buf = [], []",
|
|
724
|
+
" for line in raw.splitlines():",
|
|
725
|
+
" if line == '' or line.startswith('#'):",
|
|
726
|
+
" if names and line == '': flush()",
|
|
727
|
+
" continue",
|
|
728
|
+
" if not re.match(r'^\\s', line) and line.endswith(':'):",
|
|
729
|
+
" flush()",
|
|
730
|
+
" header = line[:-1]",
|
|
731
|
+
" names = []",
|
|
732
|
+
" for part in header.split(','):",
|
|
733
|
+
" p = part.strip()",
|
|
734
|
+
' if len(p)>=2 and p[0]==p[-1] and p[0] in "\'\\"": p = p[1:-1]',
|
|
735
|
+
" at = p.find('@', 1) if p.startswith('@') else p.find('@')",
|
|
736
|
+
" names.append(p[:at] if at > 0 else p)",
|
|
737
|
+
" buf = [line]",
|
|
738
|
+
" continue",
|
|
739
|
+
" if names: buf.append(line)",
|
|
740
|
+
" flush()",
|
|
741
|
+
" return blocks",
|
|
742
|
+
"def yarn_ok(base, head):",
|
|
743
|
+
" bb, hb = yarn_blocks(base), yarn_blocks(head)",
|
|
744
|
+
" for name in set(bb)|set(hb):",
|
|
745
|
+
" if is_dir_key(name): continue",
|
|
746
|
+
" if bb.get(name) != hb.get(name): return False",
|
|
747
|
+
" return True",
|
|
748
|
+
"paths = changed_names()",
|
|
749
|
+
"core = [p for p in paths if p == '.deft/core' or p.startswith('.deft/core/')]",
|
|
750
|
+
"if not core:",
|
|
751
|
+
" sys.exit(0)",
|
|
752
|
+
"checks = {",
|
|
753
|
+
" 'package.json': pkg_pin_only,",
|
|
754
|
+
" 'package-lock.json': npm_lock_ok,",
|
|
755
|
+
" 'pnpm-lock.yaml': pnpm_ok,",
|
|
756
|
+
" 'yarn.lock': yarn_ok,",
|
|
757
|
+
"}",
|
|
758
|
+
"bad = []",
|
|
759
|
+
"for path, fn in checks.items():",
|
|
760
|
+
" if path not in paths: continue",
|
|
761
|
+
" if not fn(git_show(base_sha, path), git_show(head_sha, path)):",
|
|
762
|
+
" bad.append(path)",
|
|
763
|
+
"if bad:",
|
|
764
|
+
" print('::error title=deft-core guard (#3193)::package/lock co-travel with .deft/core/** must be @deftai/directive* pin-only + lock follow-through. Rejected: ' + ', '.join(bad))",
|
|
765
|
+
" print('--- content-rejected pin/lock paths ---')",
|
|
766
|
+
" print('\\n'.join(bad))",
|
|
767
|
+
" sys.exit(1)",
|
|
768
|
+
"print('OK: package/lock content is Directive pin unit (#3193).')",
|
|
769
|
+
"PY",
|
|
770
|
+
].join("\n");
|
|
771
|
+
}
|
|
504
772
|
function coreGuardWorkflowContent() {
|
|
505
773
|
// Fail closed before emitting guard ERE if allowlist violates #1430 denylist (#3030).
|
|
506
774
|
assertInstallerAllowlistHonors1430();
|
|
507
775
|
const baseSha = githubActionsExpr("github.event.pull_request.base.sha");
|
|
508
776
|
const headSha = githubActionsExpr("github.event.pull_request.head.sha");
|
|
509
777
|
return ("name: deft-core-guard\n\n" +
|
|
510
|
-
"# Deft framework guard (#1430 / #3127): a single PR should not mix changes to the\n" +
|
|
778
|
+
"# Deft framework guard (#1430 / #3127 / #3193): a single PR should not mix changes to the\n" +
|
|
511
779
|
"# vendored framework payload (.deft/core/**) with true application/product files.\n" +
|
|
512
780
|
"# One upgrade PR MAY include deposit + installer-managed paths + package.json pin/lock\n" +
|
|
513
|
-
"# + .deft/GENERATION.json
|
|
514
|
-
"#
|
|
515
|
-
"#
|
|
781
|
+
"# + .deft/GENERATION.json when package.json is @deftai/directive* dependency-key pin-only\n" +
|
|
782
|
+
"# and lockfiles are pin follow-through (#3193). Framework updates come from `deft update`\n" +
|
|
783
|
+
"# / install and should land without product feature work so reviewers treat the payload\n" +
|
|
784
|
+
"# as machine-managed. Delete this file if you do not want the guard.\n" +
|
|
516
785
|
"on:\n" +
|
|
517
786
|
" pull_request:\n\n" +
|
|
518
787
|
"permissions:\n" +
|
|
@@ -543,6 +812,10 @@ function coreGuardWorkflowContent() {
|
|
|
543
812
|
' echo "--- non-framework changes ---"; printf \'%s\\n\' "$app"\n' +
|
|
544
813
|
" exit 1\n" +
|
|
545
814
|
" fi\n" +
|
|
815
|
+
" # Content-aware pin unit (#3193): path allowlist is not enough for package/lock.\n" +
|
|
816
|
+
' if [ -n "$core" ]; then\n' +
|
|
817
|
+
`${coreGuardPinContentPython()}\n` +
|
|
818
|
+
" fi\n" +
|
|
546
819
|
' echo "OK: no mixed framework + app changes."\n');
|
|
547
820
|
}
|
|
548
821
|
export function ensureGitattributes(projectDir, io) {
|
|
@@ -108,6 +108,21 @@ description: >-
|
|
|
108
108
|
---
|
|
109
109
|
|
|
110
110
|
Read and follow: .deft/core/skills/deft-directive-interview/SKILL.md
|
|
111
|
+
`,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
dir: "deft-directive-portfolio-priority",
|
|
115
|
+
content: `---
|
|
116
|
+
name: deft-directive-portfolio-priority
|
|
117
|
+
description: >-
|
|
118
|
+
Pre-promotion portfolio prioritization: cluster competing open RFCs/issues
|
|
119
|
+
from the local github-issue cache, emit a propose-not-apply priority brief
|
|
120
|
+
(conflict matrix, shortlist, park), with epistemic citation gates, then hand
|
|
121
|
+
off for operator dispose. Use when the operator asks for portfolio priority,
|
|
122
|
+
a priority brief, competing RFCs, or to cluster open issues.
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
Read and follow: .deft/core/skills/deft-directive-portfolio-priority/SKILL.md
|
|
111
126
|
`,
|
|
112
127
|
},
|
|
113
128
|
{
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check-resume / local suite-stamp policy (#3189).
|
|
3
|
+
*
|
|
4
|
+
* Consent / SoT surface for consumer expansion of local suite-stamp resume
|
|
5
|
+
* (#3188). Unset is fail-closed (localStamp off). CI never trusts a local
|
|
6
|
+
* stamp in v1 — `ciTrustsLocalStamp` is fixed false.
|
|
7
|
+
*/
|
|
8
|
+
/** Canonical registered policy field name. */
|
|
9
|
+
export declare const FIELD_CHECK_RESUME = "plan.policy.checkResume";
|
|
10
|
+
/** Short alias for `policy:show --field=checkResume`. */
|
|
11
|
+
export declare const FIELD_CHECK_RESUME_CLI_ALIAS = "checkResume";
|
|
12
|
+
export declare const CHECK_RESUME_STATUSES: readonly ["unset", "decided"];
|
|
13
|
+
export type CheckResumeStatus = (typeof CHECK_RESUME_STATUSES)[number];
|
|
14
|
+
export declare const CHECK_RESUME_LOCAL_STAMP: readonly ["off", "on"];
|
|
15
|
+
export type CheckResumeLocalStamp = (typeof CHECK_RESUME_LOCAL_STAMP)[number];
|
|
16
|
+
/** Fail-closed when unset / missing. */
|
|
17
|
+
export declare const DEFAULT_CHECK_RESUME_LOCAL_STAMP: CheckResumeLocalStamp;
|
|
18
|
+
/**
|
|
19
|
+
* FIXED false in v1 (#3189 non-goal). Separate RFC required to ever allow CI
|
|
20
|
+
* to trust a laptop suite stamp.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1: false;
|
|
23
|
+
export interface CheckResumeConfig {
|
|
24
|
+
readonly status: CheckResumeStatus;
|
|
25
|
+
readonly localStamp: CheckResumeLocalStamp;
|
|
26
|
+
/** Always false in v1; present for schema visibility. */
|
|
27
|
+
readonly ciTrustsLocalStamp: false;
|
|
28
|
+
/** Present when decided via dismiss-with-reason. */
|
|
29
|
+
readonly dismissReason: string | null;
|
|
30
|
+
}
|
|
31
|
+
export type CheckResumeSource = "typed" | "default" | "default-on-error";
|
|
32
|
+
export interface CheckResumeResolved extends CheckResumeConfig {
|
|
33
|
+
readonly source: CheckResumeSource;
|
|
34
|
+
readonly error: string | null;
|
|
35
|
+
}
|
|
36
|
+
/** Validate a `plan.policy.checkResume` payload. */
|
|
37
|
+
export declare function validateCheckResume(value: unknown): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a typed block. Missing/invalid → localStamp off, status unset.
|
|
40
|
+
* `ciTrustsLocalStamp` is always forced false regardless of input truthiness
|
|
41
|
+
* (malformed non-false is validation error → fail-closed default).
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveCheckResumeFromTypedBlock(raw: unknown): CheckResumeResolved;
|
|
44
|
+
/** Resolve `plan.policy.checkResume` from PROJECT-DEFINITION (#3189). */
|
|
45
|
+
export declare function resolveCheckResume(projectRoot: string): CheckResumeResolved;
|
|
46
|
+
/** Local suite stamp resume only when decided localStamp=on. */
|
|
47
|
+
export declare function isLocalStampResumeAllowed(policy: CheckResumeResolved): boolean;
|
|
48
|
+
/** Always false in v1 — CI must re-run suite, never trust a laptop stamp. */
|
|
49
|
+
export declare function isCiTrustsLocalStampAllowed(_policy?: CheckResumeResolved): false;
|
|
50
|
+
export declare function formatCheckResumeStatusLine(policy: CheckResumeResolved): string;
|
|
51
|
+
export interface CheckResumePolicyField {
|
|
52
|
+
readonly name: typeof FIELD_CHECK_RESUME;
|
|
53
|
+
readonly current: CheckResumeConfig;
|
|
54
|
+
readonly default: CheckResumeConfig;
|
|
55
|
+
readonly source: string;
|
|
56
|
+
}
|
|
57
|
+
/** Inspector row for `policy:show --field=checkResume`. */
|
|
58
|
+
export declare function inspectCheckResume(data: Record<string, unknown> | null, projectRoot?: string): CheckResumePolicyField;
|
|
59
|
+
export interface WriteCheckResumeOptions {
|
|
60
|
+
readonly localStamp: CheckResumeLocalStamp;
|
|
61
|
+
readonly dismissReason?: string | null;
|
|
62
|
+
readonly actor?: string;
|
|
63
|
+
readonly note?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface WriteCheckResumeResult {
|
|
66
|
+
readonly exitCode: 0 | 2;
|
|
67
|
+
readonly stdout: string;
|
|
68
|
+
readonly changed: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** Persist decided checkResume (preset or dismiss-with-reason). */
|
|
71
|
+
export declare function writeCheckResume(projectRoot: string, options: WriteCheckResumeOptions): WriteCheckResumeResult;
|
|
72
|
+
//# sourceMappingURL=check-resume.d.ts.map
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check-resume / local suite-stamp policy (#3189).
|
|
3
|
+
*
|
|
4
|
+
* Consent / SoT surface for consumer expansion of local suite-stamp resume
|
|
5
|
+
* (#3188). Unset is fail-closed (localStamp off). CI never trusts a local
|
|
6
|
+
* stamp in v1 — `ciTrustsLocalStamp` is fixed false.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
10
|
+
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
|
|
11
|
+
import { policyColonInvocation } from "./policy-invocation.js";
|
|
12
|
+
import { appendAuditLog, loadProjectDefinition, projectDefinitionPath } from "./resolve.js";
|
|
13
|
+
/** Canonical registered policy field name. */
|
|
14
|
+
export const FIELD_CHECK_RESUME = "plan.policy.checkResume";
|
|
15
|
+
/** Short alias for `policy:show --field=checkResume`. */
|
|
16
|
+
export const FIELD_CHECK_RESUME_CLI_ALIAS = "checkResume";
|
|
17
|
+
export const CHECK_RESUME_STATUSES = ["unset", "decided"];
|
|
18
|
+
export const CHECK_RESUME_LOCAL_STAMP = ["off", "on"];
|
|
19
|
+
/** Fail-closed when unset / missing. */
|
|
20
|
+
export const DEFAULT_CHECK_RESUME_LOCAL_STAMP = "off";
|
|
21
|
+
/**
|
|
22
|
+
* FIXED false in v1 (#3189 non-goal). Separate RFC required to ever allow CI
|
|
23
|
+
* to trust a laptop suite stamp.
|
|
24
|
+
*/
|
|
25
|
+
export const CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1 = false;
|
|
26
|
+
function defaultResolved(source, error = null) {
|
|
27
|
+
return {
|
|
28
|
+
status: "unset",
|
|
29
|
+
localStamp: DEFAULT_CHECK_RESUME_LOCAL_STAMP,
|
|
30
|
+
ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
|
|
31
|
+
dismissReason: null,
|
|
32
|
+
source,
|
|
33
|
+
error,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function isStatus(value) {
|
|
37
|
+
return typeof value === "string" && CHECK_RESUME_STATUSES.includes(value);
|
|
38
|
+
}
|
|
39
|
+
function isLocalStamp(value) {
|
|
40
|
+
return (typeof value === "string" && CHECK_RESUME_LOCAL_STAMP.includes(value));
|
|
41
|
+
}
|
|
42
|
+
/** Validate a `plan.policy.checkResume` payload. */
|
|
43
|
+
export function validateCheckResume(value) {
|
|
44
|
+
if (value === null || value === undefined) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
48
|
+
return [`${FIELD_CHECK_RESUME} must be an object; got ${typeof value}`];
|
|
49
|
+
}
|
|
50
|
+
const rec = value;
|
|
51
|
+
const errors = [];
|
|
52
|
+
if ("status" in rec && !isStatus(rec.status)) {
|
|
53
|
+
errors.push(`${FIELD_CHECK_RESUME}.status must be one of ${CHECK_RESUME_STATUSES.join("|")}`);
|
|
54
|
+
}
|
|
55
|
+
if ("localStamp" in rec && !isLocalStamp(rec.localStamp)) {
|
|
56
|
+
errors.push(`${FIELD_CHECK_RESUME}.localStamp must be one of ${CHECK_RESUME_LOCAL_STAMP.join("|")}`);
|
|
57
|
+
}
|
|
58
|
+
if ("ciTrustsLocalStamp" in rec && rec.ciTrustsLocalStamp !== false) {
|
|
59
|
+
errors.push(`${FIELD_CHECK_RESUME}.ciTrustsLocalStamp must be false in v1 (CI never trusts local stamps)`);
|
|
60
|
+
}
|
|
61
|
+
if ("dismissReason" in rec &&
|
|
62
|
+
rec.dismissReason !== null &&
|
|
63
|
+
typeof rec.dismissReason !== "string") {
|
|
64
|
+
errors.push(`${FIELD_CHECK_RESUME}.dismissReason must be a string or null`);
|
|
65
|
+
}
|
|
66
|
+
return errors;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve a typed block. Missing/invalid → localStamp off, status unset.
|
|
70
|
+
* `ciTrustsLocalStamp` is always forced false regardless of input truthiness
|
|
71
|
+
* (malformed non-false is validation error → fail-closed default).
|
|
72
|
+
*/
|
|
73
|
+
export function resolveCheckResumeFromTypedBlock(raw) {
|
|
74
|
+
const errors = validateCheckResume(raw);
|
|
75
|
+
if (errors.length > 0) {
|
|
76
|
+
return defaultResolved("default-on-error", errors[0] ?? "invalid checkResume block");
|
|
77
|
+
}
|
|
78
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
79
|
+
return defaultResolved("default");
|
|
80
|
+
}
|
|
81
|
+
const block = raw;
|
|
82
|
+
const status = isStatus(block.status) ? block.status : "unset";
|
|
83
|
+
const localStampRaw = isLocalStamp(block.localStamp)
|
|
84
|
+
? block.localStamp
|
|
85
|
+
: DEFAULT_CHECK_RESUME_LOCAL_STAMP;
|
|
86
|
+
// Fail-closed: local stamp only when decided.
|
|
87
|
+
const localStamp = status === "decided" ? localStampRaw : DEFAULT_CHECK_RESUME_LOCAL_STAMP;
|
|
88
|
+
const dismissReason = typeof block.dismissReason === "string" && block.dismissReason.trim().length > 0
|
|
89
|
+
? block.dismissReason.trim()
|
|
90
|
+
: null;
|
|
91
|
+
return {
|
|
92
|
+
status,
|
|
93
|
+
localStamp,
|
|
94
|
+
ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
|
|
95
|
+
dismissReason: status === "decided" ? dismissReason : null,
|
|
96
|
+
source: "typed",
|
|
97
|
+
error: null,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** Resolve `plan.policy.checkResume` from PROJECT-DEFINITION (#3189). */
|
|
101
|
+
export function resolveCheckResume(projectRoot) {
|
|
102
|
+
const [data, err] = loadProjectDefinition(projectRoot);
|
|
103
|
+
if (data === null) {
|
|
104
|
+
return defaultResolved("default-on-error", err);
|
|
105
|
+
}
|
|
106
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
107
|
+
if (typeof policyBlock !== "object" ||
|
|
108
|
+
policyBlock === null ||
|
|
109
|
+
Array.isArray(policyBlock) ||
|
|
110
|
+
!("checkResume" in policyBlock)) {
|
|
111
|
+
return defaultResolved("default");
|
|
112
|
+
}
|
|
113
|
+
return resolveCheckResumeFromTypedBlock(policyBlock.checkResume);
|
|
114
|
+
}
|
|
115
|
+
/** Local suite stamp resume only when decided localStamp=on. */
|
|
116
|
+
export function isLocalStampResumeAllowed(policy) {
|
|
117
|
+
return policy.status === "decided" && policy.localStamp === "on";
|
|
118
|
+
}
|
|
119
|
+
/** Always false in v1 — CI must re-run suite, never trust a laptop stamp. */
|
|
120
|
+
export function isCiTrustsLocalStampAllowed(_policy) {
|
|
121
|
+
return CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1;
|
|
122
|
+
}
|
|
123
|
+
export function formatCheckResumeStatusLine(policy) {
|
|
124
|
+
const dismiss = policy.dismissReason !== null ? ` dismissReason=${JSON.stringify(policy.dismissReason)}` : "";
|
|
125
|
+
return (`[deft policy] checkResume status=${policy.status} localStamp=${policy.localStamp} ` +
|
|
126
|
+
`ciTrustsLocalStamp=${String(policy.ciTrustsLocalStamp)}${dismiss} (source=${policy.source}).`);
|
|
127
|
+
}
|
|
128
|
+
function fieldFromResolved(resolved) {
|
|
129
|
+
return {
|
|
130
|
+
name: FIELD_CHECK_RESUME,
|
|
131
|
+
current: {
|
|
132
|
+
status: resolved.status,
|
|
133
|
+
localStamp: resolved.localStamp,
|
|
134
|
+
ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
|
|
135
|
+
dismissReason: resolved.dismissReason,
|
|
136
|
+
},
|
|
137
|
+
default: {
|
|
138
|
+
status: "unset",
|
|
139
|
+
localStamp: DEFAULT_CHECK_RESUME_LOCAL_STAMP,
|
|
140
|
+
ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
|
|
141
|
+
dismissReason: null,
|
|
142
|
+
},
|
|
143
|
+
source: resolved.source,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** Inspector row for `policy:show --field=checkResume`. */
|
|
147
|
+
export function inspectCheckResume(data, projectRoot) {
|
|
148
|
+
if (data === null) {
|
|
149
|
+
return fieldFromResolved(defaultResolved("default"));
|
|
150
|
+
}
|
|
151
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
152
|
+
if (typeof policyBlock !== "object" ||
|
|
153
|
+
policyBlock === null ||
|
|
154
|
+
Array.isArray(policyBlock) ||
|
|
155
|
+
!("checkResume" in policyBlock)) {
|
|
156
|
+
if (projectRoot !== undefined) {
|
|
157
|
+
return fieldFromResolved(resolveCheckResume(projectRoot));
|
|
158
|
+
}
|
|
159
|
+
return fieldFromResolved(defaultResolved("default"));
|
|
160
|
+
}
|
|
161
|
+
return fieldFromResolved(resolveCheckResumeFromTypedBlock(policyBlock.checkResume));
|
|
162
|
+
}
|
|
163
|
+
/** Persist decided checkResume (preset or dismiss-with-reason). */
|
|
164
|
+
export function writeCheckResume(projectRoot, options) {
|
|
165
|
+
const path = projectDefinitionPath(projectRoot);
|
|
166
|
+
try {
|
|
167
|
+
const { changed } = projectDefinitionMutationLock(projectRoot, () => {
|
|
168
|
+
const parsed = JSON.parse(readFileSync(path, { encoding: "utf8" }));
|
|
169
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
170
|
+
throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
171
|
+
}
|
|
172
|
+
const data = parsed;
|
|
173
|
+
if (typeof data.plan !== "object" || data.plan === null || Array.isArray(data.plan)) {
|
|
174
|
+
if (data.plan === undefined) {
|
|
175
|
+
data.plan = {};
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
throw new Error("PROJECT-DEFINITION 'plan' is not an object");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const plan = data.plan;
|
|
182
|
+
migrateLegacyPolicyKey(plan);
|
|
183
|
+
const existingPolicy = plan[PLAN_POLICY_KEY];
|
|
184
|
+
if (typeof existingPolicy !== "object" ||
|
|
185
|
+
existingPolicy === null ||
|
|
186
|
+
Array.isArray(existingPolicy)) {
|
|
187
|
+
if (existingPolicy === undefined) {
|
|
188
|
+
plan[PLAN_POLICY_KEY] = {};
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
throw new Error("plan.policy is not an object");
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const policyBlock = plan[PLAN_POLICY_KEY];
|
|
195
|
+
const previous = policyBlock.checkResume;
|
|
196
|
+
const dismissReason = typeof options.dismissReason === "string" && options.dismissReason.trim().length > 0
|
|
197
|
+
? options.dismissReason.trim()
|
|
198
|
+
: null;
|
|
199
|
+
const nextBlock = {
|
|
200
|
+
status: "decided",
|
|
201
|
+
localStamp: options.localStamp,
|
|
202
|
+
ciTrustsLocalStamp: CHECK_RESUME_CI_TRUSTS_LOCAL_STAMP_V1,
|
|
203
|
+
dismissReason,
|
|
204
|
+
};
|
|
205
|
+
const previousNormalized = resolveCheckResumeFromTypedBlock(previous);
|
|
206
|
+
const changedFlag = previousNormalized.status !== nextBlock.status ||
|
|
207
|
+
previousNormalized.localStamp !== nextBlock.localStamp ||
|
|
208
|
+
previousNormalized.dismissReason !== nextBlock.dismissReason;
|
|
209
|
+
policyBlock.checkResume = nextBlock;
|
|
210
|
+
if (changedFlag) {
|
|
211
|
+
atomicWriteProjectDefinition(path, data);
|
|
212
|
+
}
|
|
213
|
+
const actor = options.actor ?? policyColonInvocation("set-check-resume");
|
|
214
|
+
const note = options.note ?? "";
|
|
215
|
+
const parts = [
|
|
216
|
+
`actor=${actor}`,
|
|
217
|
+
"checkResume.status=decided",
|
|
218
|
+
`localStamp=${nextBlock.localStamp}`,
|
|
219
|
+
"ciTrustsLocalStamp=false",
|
|
220
|
+
`dismissReason=${JSON.stringify(nextBlock.dismissReason)}`,
|
|
221
|
+
`previous=${JSON.stringify(previous ?? null)}`,
|
|
222
|
+
];
|
|
223
|
+
if (note) {
|
|
224
|
+
parts.push(`note=${note.replace(/\n/g, " ").replace(/\r/g, " ")}`);
|
|
225
|
+
}
|
|
226
|
+
appendAuditLog(projectRoot, parts.join(" "));
|
|
227
|
+
return { changed: changedFlag };
|
|
228
|
+
});
|
|
229
|
+
const resolved = resolveCheckResume(projectRoot);
|
|
230
|
+
const lines = [
|
|
231
|
+
`\u2713 ${FIELD_CHECK_RESUME} status=decided localStamp=${resolved.localStamp}.`,
|
|
232
|
+
changed
|
|
233
|
+
? " audit: meta/policy-changes.log updated."
|
|
234
|
+
: " no-op: value already matched (audit entry still appended for trail).",
|
|
235
|
+
formatCheckResumeStatusLine(resolved),
|
|
236
|
+
];
|
|
237
|
+
return { exitCode: 0, stdout: `${lines.join("\n")}\n`, changed };
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
241
|
+
if (message.includes("PROJECT-DEFINITION not found") ||
|
|
242
|
+
message.includes("ENOENT") ||
|
|
243
|
+
message.includes("no such file")) {
|
|
244
|
+
return {
|
|
245
|
+
exitCode: 2,
|
|
246
|
+
stdout: `\u274c PROJECT-DEFINITION not found under ${projectRoot}\n`,
|
|
247
|
+
changed: false,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return { exitCode: 2, stdout: `\u274c Config error: ${message}\n`, changed: false };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=check-resume.js.map
|