@mstar-harness/cli 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/mstar-harness.js +482 -400
  2. package/package.json +4 -4
@@ -2344,13 +2344,24 @@ var require_commander = __commonJS((exports) => {
2344
2344
  });
2345
2345
 
2346
2346
  // src/index.ts
2347
- import { execFileSync as execFileSync6 } from "child_process";
2347
+ import { execFileSync as execFileSync7 } from "child_process";
2348
2348
  import fs7 from "fs";
2349
2349
  import path11 from "path";
2350
2350
 
2351
2351
  // ../../node_modules/@inquirer/core/dist/lib/key.js
2352
- var isUpKey = (key, keybindings = []) => key.name === "up" || keybindings.includes("vim") && key.name === "k" || keybindings.includes("emacs") && key.ctrl && key.name === "p";
2353
- var isDownKey = (key, keybindings = []) => key.name === "down" || keybindings.includes("vim") && key.name === "j" || keybindings.includes("emacs") && key.ctrl && key.name === "n";
2352
+ var keybindings = ["emacs", "vim"];
2353
+ var keybindingLookup = new Set(keybindings);
2354
+ function isKeybinding(value) {
2355
+ return keybindingLookup.has(value);
2356
+ }
2357
+ function getDefaultKeybindings() {
2358
+ const env = process.env["INQUIRER_KEYBINDINGS"];
2359
+ if (!env)
2360
+ return [];
2361
+ return Array.from(new Set(env.toLowerCase().split(/[\s,]+/).filter(isKeybinding)));
2362
+ }
2363
+ var isUpKey = (key, keybindings2 = []) => key.name === "up" || keybindings2.includes("vim") && key.name === "k" || keybindings2.includes("emacs") && key.ctrl && key.name === "p";
2364
+ var isDownKey = (key, keybindings2 = []) => key.name === "down" || keybindings2.includes("vim") && key.name === "j" || keybindings2.includes("emacs") && key.ctrl && key.name === "n";
2354
2365
  var isBackspaceKey = (key) => key.name === "backspace";
2355
2366
  var isNumberKey = (key) => "1234567890".includes(key.name);
2356
2367
  var isEnterKey = (key) => key.name === "enter" || key.name === "return";
@@ -2535,267 +2546,267 @@ function isUnicodeSupported() {
2535
2546
  var common = {
2536
2547
  circleQuestionMark: "(?)",
2537
2548
  questionMarkPrefix: "(?)",
2538
- square: "",
2539
- squareDarkShade: "",
2540
- squareMediumShade: "",
2541
- squareLightShade: "",
2542
- squareTop: "",
2543
- squareBottom: "",
2544
- squareLeft: "",
2545
- squareRight: "",
2546
- squareCenter: "",
2547
- bullet: "",
2548
- dot: "",
2549
- ellipsis: "",
2550
- pointerSmall: "",
2551
- triangleUp: "",
2552
- triangleUpSmall: "",
2553
- triangleDown: "",
2554
- triangleDownSmall: "",
2555
- triangleLeftSmall: "",
2556
- triangleRightSmall: "",
2557
- home: "",
2558
- heart: "",
2559
- musicNote: "",
2560
- musicNoteBeamed: "",
2561
- arrowUp: "",
2562
- arrowDown: "",
2563
- arrowLeft: "",
2564
- arrowRight: "",
2565
- arrowLeftRight: "",
2566
- arrowUpDown: "",
2567
- almostEqual: "",
2568
- notEqual: "",
2569
- lessOrEqual: "",
2570
- greaterOrEqual: "",
2571
- identical: "",
2572
- infinity: "",
2573
- subscriptZero: "",
2574
- subscriptOne: "",
2575
- subscriptTwo: "",
2576
- subscriptThree: "",
2577
- subscriptFour: "",
2578
- subscriptFive: "",
2579
- subscriptSix: "",
2580
- subscriptSeven: "",
2581
- subscriptEight: "",
2582
- subscriptNine: "",
2583
- oneHalf: "½",
2584
- oneThird: "",
2585
- oneQuarter: "¼",
2586
- oneFifth: "",
2587
- oneSixth: "",
2588
- oneEighth: "",
2589
- twoThirds: "",
2590
- twoFifths: "",
2591
- threeQuarters: "¾",
2592
- threeFifths: "",
2593
- threeEighths: "",
2594
- fourFifths: "",
2595
- fiveSixths: "",
2596
- fiveEighths: "",
2597
- sevenEighths: "",
2598
- line: "",
2599
- lineBold: "",
2600
- lineDouble: "",
2601
- lineDashed0: "",
2602
- lineDashed1: "",
2603
- lineDashed2: "",
2604
- lineDashed3: "",
2605
- lineDashed4: "",
2606
- lineDashed5: "",
2607
- lineDashed6: "",
2608
- lineDashed7: "",
2609
- lineDashed8: "",
2610
- lineDashed9: "",
2611
- lineDashed10: "",
2612
- lineDashed11: "",
2613
- lineDashed12: "",
2614
- lineDashed13: "",
2615
- lineDashed14: "",
2616
- lineDashed15: "",
2617
- lineVertical: "",
2618
- lineVerticalBold: "",
2619
- lineVerticalDouble: "",
2620
- lineVerticalDashed0: "",
2621
- lineVerticalDashed1: "",
2622
- lineVerticalDashed2: "",
2623
- lineVerticalDashed3: "",
2624
- lineVerticalDashed4: "",
2625
- lineVerticalDashed5: "",
2626
- lineVerticalDashed6: "",
2627
- lineVerticalDashed7: "",
2628
- lineVerticalDashed8: "",
2629
- lineVerticalDashed9: "",
2630
- lineVerticalDashed10: "",
2631
- lineVerticalDashed11: "",
2632
- lineDownLeft: "",
2633
- lineDownLeftArc: "",
2634
- lineDownBoldLeftBold: "",
2635
- lineDownBoldLeft: "",
2636
- lineDownLeftBold: "",
2637
- lineDownDoubleLeftDouble: "",
2638
- lineDownDoubleLeft: "",
2639
- lineDownLeftDouble: "",
2640
- lineDownRight: "",
2641
- lineDownRightArc: "",
2642
- lineDownBoldRightBold: "",
2643
- lineDownBoldRight: "",
2644
- lineDownRightBold: "",
2645
- lineDownDoubleRightDouble: "",
2646
- lineDownDoubleRight: "",
2647
- lineDownRightDouble: "",
2648
- lineUpLeft: "",
2649
- lineUpLeftArc: "",
2650
- lineUpBoldLeftBold: "",
2651
- lineUpBoldLeft: "",
2652
- lineUpLeftBold: "",
2653
- lineUpDoubleLeftDouble: "",
2654
- lineUpDoubleLeft: "",
2655
- lineUpLeftDouble: "",
2656
- lineUpRight: "",
2657
- lineUpRightArc: "",
2658
- lineUpBoldRightBold: "",
2659
- lineUpBoldRight: "",
2660
- lineUpRightBold: "",
2661
- lineUpDoubleRightDouble: "",
2662
- lineUpDoubleRight: "",
2663
- lineUpRightDouble: "",
2664
- lineUpDownLeft: "",
2665
- lineUpBoldDownBoldLeftBold: "",
2666
- lineUpBoldDownBoldLeft: "",
2667
- lineUpDownLeftBold: "",
2668
- lineUpBoldDownLeftBold: "",
2669
- lineUpDownBoldLeftBold: "",
2670
- lineUpDownBoldLeft: "",
2671
- lineUpBoldDownLeft: "",
2672
- lineUpDoubleDownDoubleLeftDouble: "",
2673
- lineUpDoubleDownDoubleLeft: "",
2674
- lineUpDownLeftDouble: "",
2675
- lineUpDownRight: "",
2676
- lineUpBoldDownBoldRightBold: "",
2677
- lineUpBoldDownBoldRight: "",
2678
- lineUpDownRightBold: "",
2679
- lineUpBoldDownRightBold: "",
2680
- lineUpDownBoldRightBold: "",
2681
- lineUpDownBoldRight: "",
2682
- lineUpBoldDownRight: "",
2683
- lineUpDoubleDownDoubleRightDouble: "",
2684
- lineUpDoubleDownDoubleRight: "",
2685
- lineUpDownRightDouble: "",
2686
- lineDownLeftRight: "",
2687
- lineDownBoldLeftBoldRightBold: "",
2688
- lineDownLeftBoldRightBold: "",
2689
- lineDownBoldLeftRight: "",
2690
- lineDownBoldLeftBoldRight: "",
2691
- lineDownBoldLeftRightBold: "",
2692
- lineDownLeftRightBold: "",
2693
- lineDownLeftBoldRight: "",
2694
- lineDownDoubleLeftDoubleRightDouble: "",
2695
- lineDownDoubleLeftRight: "",
2696
- lineDownLeftDoubleRightDouble: "",
2697
- lineUpLeftRight: "",
2698
- lineUpBoldLeftBoldRightBold: "",
2699
- lineUpLeftBoldRightBold: "",
2700
- lineUpBoldLeftRight: "",
2701
- lineUpBoldLeftBoldRight: "",
2702
- lineUpBoldLeftRightBold: "",
2703
- lineUpLeftRightBold: "",
2704
- lineUpLeftBoldRight: "",
2705
- lineUpDoubleLeftDoubleRightDouble: "",
2706
- lineUpDoubleLeftRight: "",
2707
- lineUpLeftDoubleRightDouble: "",
2708
- lineUpDownLeftRight: "",
2709
- lineUpBoldDownBoldLeftBoldRightBold: "",
2710
- lineUpDownBoldLeftBoldRightBold: "",
2711
- lineUpBoldDownLeftBoldRightBold: "",
2712
- lineUpBoldDownBoldLeftRightBold: "",
2713
- lineUpBoldDownBoldLeftBoldRight: "",
2714
- lineUpBoldDownLeftRight: "",
2715
- lineUpDownBoldLeftRight: "",
2716
- lineUpDownLeftBoldRight: "",
2717
- lineUpDownLeftRightBold: "",
2718
- lineUpBoldDownBoldLeftRight: "",
2719
- lineUpDownLeftBoldRightBold: "",
2720
- lineUpBoldDownLeftBoldRight: "",
2721
- lineUpBoldDownLeftRightBold: "",
2722
- lineUpDownBoldLeftBoldRight: "",
2723
- lineUpDownBoldLeftRightBold: "",
2724
- lineUpDoubleDownDoubleLeftDoubleRightDouble: "",
2725
- lineUpDoubleDownDoubleLeftRight: "",
2726
- lineUpDownLeftDoubleRightDouble: "",
2727
- lineCross: "",
2728
- lineBackslash: "",
2729
- lineSlash: ""
2549
+ square: "\u2588",
2550
+ squareDarkShade: "\u2593",
2551
+ squareMediumShade: "\u2592",
2552
+ squareLightShade: "\u2591",
2553
+ squareTop: "\u2580",
2554
+ squareBottom: "\u2584",
2555
+ squareLeft: "\u258c",
2556
+ squareRight: "\u2590",
2557
+ squareCenter: "\u25a0",
2558
+ bullet: "\u25cf",
2559
+ dot: "\u2024",
2560
+ ellipsis: "\u2026",
2561
+ pointerSmall: "\u203a",
2562
+ triangleUp: "\u25b2",
2563
+ triangleUpSmall: "\u25b4",
2564
+ triangleDown: "\u25bc",
2565
+ triangleDownSmall: "\u25be",
2566
+ triangleLeftSmall: "\u25c2",
2567
+ triangleRightSmall: "\u25b8",
2568
+ home: "\u2302",
2569
+ heart: "\u2665",
2570
+ musicNote: "\u266a",
2571
+ musicNoteBeamed: "\u266b",
2572
+ arrowUp: "\u2191",
2573
+ arrowDown: "\u2193",
2574
+ arrowLeft: "\u2190",
2575
+ arrowRight: "\u2192",
2576
+ arrowLeftRight: "\u2194",
2577
+ arrowUpDown: "\u2195",
2578
+ almostEqual: "\u2248",
2579
+ notEqual: "\u2260",
2580
+ lessOrEqual: "\u2264",
2581
+ greaterOrEqual: "\u2265",
2582
+ identical: "\u2261",
2583
+ infinity: "\u221e",
2584
+ subscriptZero: "\u2080",
2585
+ subscriptOne: "\u2081",
2586
+ subscriptTwo: "\u2082",
2587
+ subscriptThree: "\u2083",
2588
+ subscriptFour: "\u2084",
2589
+ subscriptFive: "\u2085",
2590
+ subscriptSix: "\u2086",
2591
+ subscriptSeven: "\u2087",
2592
+ subscriptEight: "\u2088",
2593
+ subscriptNine: "\u2089",
2594
+ oneHalf: "\u00bd",
2595
+ oneThird: "\u2153",
2596
+ oneQuarter: "\u00bc",
2597
+ oneFifth: "\u2155",
2598
+ oneSixth: "\u2159",
2599
+ oneEighth: "\u215b",
2600
+ twoThirds: "\u2154",
2601
+ twoFifths: "\u2156",
2602
+ threeQuarters: "\u00be",
2603
+ threeFifths: "\u2157",
2604
+ threeEighths: "\u215c",
2605
+ fourFifths: "\u2158",
2606
+ fiveSixths: "\u215a",
2607
+ fiveEighths: "\u215d",
2608
+ sevenEighths: "\u215e",
2609
+ line: "\u2500",
2610
+ lineBold: "\u2501",
2611
+ lineDouble: "\u2550",
2612
+ lineDashed0: "\u2504",
2613
+ lineDashed1: "\u2505",
2614
+ lineDashed2: "\u2508",
2615
+ lineDashed3: "\u2509",
2616
+ lineDashed4: "\u254c",
2617
+ lineDashed5: "\u254d",
2618
+ lineDashed6: "\u2574",
2619
+ lineDashed7: "\u2576",
2620
+ lineDashed8: "\u2578",
2621
+ lineDashed9: "\u257a",
2622
+ lineDashed10: "\u257c",
2623
+ lineDashed11: "\u257e",
2624
+ lineDashed12: "\u2212",
2625
+ lineDashed13: "\u2013",
2626
+ lineDashed14: "\u2010",
2627
+ lineDashed15: "\u2043",
2628
+ lineVertical: "\u2502",
2629
+ lineVerticalBold: "\u2503",
2630
+ lineVerticalDouble: "\u2551",
2631
+ lineVerticalDashed0: "\u2506",
2632
+ lineVerticalDashed1: "\u2507",
2633
+ lineVerticalDashed2: "\u250a",
2634
+ lineVerticalDashed3: "\u250b",
2635
+ lineVerticalDashed4: "\u254e",
2636
+ lineVerticalDashed5: "\u254f",
2637
+ lineVerticalDashed6: "\u2575",
2638
+ lineVerticalDashed7: "\u2577",
2639
+ lineVerticalDashed8: "\u2579",
2640
+ lineVerticalDashed9: "\u257b",
2641
+ lineVerticalDashed10: "\u257d",
2642
+ lineVerticalDashed11: "\u257f",
2643
+ lineDownLeft: "\u2510",
2644
+ lineDownLeftArc: "\u256e",
2645
+ lineDownBoldLeftBold: "\u2513",
2646
+ lineDownBoldLeft: "\u2512",
2647
+ lineDownLeftBold: "\u2511",
2648
+ lineDownDoubleLeftDouble: "\u2557",
2649
+ lineDownDoubleLeft: "\u2556",
2650
+ lineDownLeftDouble: "\u2555",
2651
+ lineDownRight: "\u250c",
2652
+ lineDownRightArc: "\u256d",
2653
+ lineDownBoldRightBold: "\u250f",
2654
+ lineDownBoldRight: "\u250e",
2655
+ lineDownRightBold: "\u250d",
2656
+ lineDownDoubleRightDouble: "\u2554",
2657
+ lineDownDoubleRight: "\u2553",
2658
+ lineDownRightDouble: "\u2552",
2659
+ lineUpLeft: "\u2518",
2660
+ lineUpLeftArc: "\u256f",
2661
+ lineUpBoldLeftBold: "\u251b",
2662
+ lineUpBoldLeft: "\u251a",
2663
+ lineUpLeftBold: "\u2519",
2664
+ lineUpDoubleLeftDouble: "\u255d",
2665
+ lineUpDoubleLeft: "\u255c",
2666
+ lineUpLeftDouble: "\u255b",
2667
+ lineUpRight: "\u2514",
2668
+ lineUpRightArc: "\u2570",
2669
+ lineUpBoldRightBold: "\u2517",
2670
+ lineUpBoldRight: "\u2516",
2671
+ lineUpRightBold: "\u2515",
2672
+ lineUpDoubleRightDouble: "\u255a",
2673
+ lineUpDoubleRight: "\u2559",
2674
+ lineUpRightDouble: "\u2558",
2675
+ lineUpDownLeft: "\u2524",
2676
+ lineUpBoldDownBoldLeftBold: "\u252b",
2677
+ lineUpBoldDownBoldLeft: "\u2528",
2678
+ lineUpDownLeftBold: "\u2525",
2679
+ lineUpBoldDownLeftBold: "\u2529",
2680
+ lineUpDownBoldLeftBold: "\u252a",
2681
+ lineUpDownBoldLeft: "\u2527",
2682
+ lineUpBoldDownLeft: "\u2526",
2683
+ lineUpDoubleDownDoubleLeftDouble: "\u2563",
2684
+ lineUpDoubleDownDoubleLeft: "\u2562",
2685
+ lineUpDownLeftDouble: "\u2561",
2686
+ lineUpDownRight: "\u251c",
2687
+ lineUpBoldDownBoldRightBold: "\u2523",
2688
+ lineUpBoldDownBoldRight: "\u2520",
2689
+ lineUpDownRightBold: "\u251d",
2690
+ lineUpBoldDownRightBold: "\u2521",
2691
+ lineUpDownBoldRightBold: "\u2522",
2692
+ lineUpDownBoldRight: "\u251f",
2693
+ lineUpBoldDownRight: "\u251e",
2694
+ lineUpDoubleDownDoubleRightDouble: "\u2560",
2695
+ lineUpDoubleDownDoubleRight: "\u255f",
2696
+ lineUpDownRightDouble: "\u255e",
2697
+ lineDownLeftRight: "\u252c",
2698
+ lineDownBoldLeftBoldRightBold: "\u2533",
2699
+ lineDownLeftBoldRightBold: "\u252f",
2700
+ lineDownBoldLeftRight: "\u2530",
2701
+ lineDownBoldLeftBoldRight: "\u2531",
2702
+ lineDownBoldLeftRightBold: "\u2532",
2703
+ lineDownLeftRightBold: "\u252e",
2704
+ lineDownLeftBoldRight: "\u252d",
2705
+ lineDownDoubleLeftDoubleRightDouble: "\u2566",
2706
+ lineDownDoubleLeftRight: "\u2565",
2707
+ lineDownLeftDoubleRightDouble: "\u2564",
2708
+ lineUpLeftRight: "\u2534",
2709
+ lineUpBoldLeftBoldRightBold: "\u253b",
2710
+ lineUpLeftBoldRightBold: "\u2537",
2711
+ lineUpBoldLeftRight: "\u2538",
2712
+ lineUpBoldLeftBoldRight: "\u2539",
2713
+ lineUpBoldLeftRightBold: "\u253a",
2714
+ lineUpLeftRightBold: "\u2536",
2715
+ lineUpLeftBoldRight: "\u2535",
2716
+ lineUpDoubleLeftDoubleRightDouble: "\u2569",
2717
+ lineUpDoubleLeftRight: "\u2568",
2718
+ lineUpLeftDoubleRightDouble: "\u2567",
2719
+ lineUpDownLeftRight: "\u253c",
2720
+ lineUpBoldDownBoldLeftBoldRightBold: "\u254b",
2721
+ lineUpDownBoldLeftBoldRightBold: "\u2548",
2722
+ lineUpBoldDownLeftBoldRightBold: "\u2547",
2723
+ lineUpBoldDownBoldLeftRightBold: "\u254a",
2724
+ lineUpBoldDownBoldLeftBoldRight: "\u2549",
2725
+ lineUpBoldDownLeftRight: "\u2540",
2726
+ lineUpDownBoldLeftRight: "\u2541",
2727
+ lineUpDownLeftBoldRight: "\u253d",
2728
+ lineUpDownLeftRightBold: "\u253e",
2729
+ lineUpBoldDownBoldLeftRight: "\u2542",
2730
+ lineUpDownLeftBoldRightBold: "\u253f",
2731
+ lineUpBoldDownLeftBoldRight: "\u2543",
2732
+ lineUpBoldDownLeftRightBold: "\u2544",
2733
+ lineUpDownBoldLeftBoldRight: "\u2545",
2734
+ lineUpDownBoldLeftRightBold: "\u2546",
2735
+ lineUpDoubleDownDoubleLeftDoubleRightDouble: "\u256c",
2736
+ lineUpDoubleDownDoubleLeftRight: "\u256b",
2737
+ lineUpDownLeftDoubleRightDouble: "\u256a",
2738
+ lineCross: "\u2573",
2739
+ lineBackslash: "\u2572",
2740
+ lineSlash: "\u2571"
2730
2741
  };
2731
2742
  var specialMainSymbols = {
2732
- tick: "",
2733
- info: "",
2734
- warning: "",
2735
- cross: "",
2736
- squareSmall: "",
2737
- squareSmallFilled: "",
2738
- circle: "",
2739
- circleFilled: "",
2740
- circleDotted: "",
2741
- circleDouble: "",
2742
- circleCircle: "",
2743
- circleCross: "",
2744
- circlePipe: "",
2745
- radioOn: "",
2746
- radioOff: "",
2747
- checkboxOn: "",
2748
- checkboxOff: "",
2749
- checkboxCircleOn: "",
2750
- checkboxCircleOff: "",
2751
- pointer: "",
2752
- triangleUpOutline: "",
2753
- triangleLeft: "",
2754
- triangleRight: "",
2755
- lozenge: "",
2756
- lozengeOutline: "",
2757
- hamburger: "",
2758
- smiley: "",
2759
- mustache: "",
2760
- star: "",
2761
- play: "",
2762
- nodejs: "",
2763
- oneSeventh: "",
2764
- oneNinth: "",
2765
- oneTenth: ""
2743
+ tick: "\u2714",
2744
+ info: "\u2139",
2745
+ warning: "\u26a0",
2746
+ cross: "\u2718",
2747
+ squareSmall: "\u25fb",
2748
+ squareSmallFilled: "\u25fc",
2749
+ circle: "\u25ef",
2750
+ circleFilled: "\u25c9",
2751
+ circleDotted: "\u25cc",
2752
+ circleDouble: "\u25ce",
2753
+ circleCircle: "\u24de",
2754
+ circleCross: "\u24e7",
2755
+ circlePipe: "\u24be",
2756
+ radioOn: "\u25c9",
2757
+ radioOff: "\u25ef",
2758
+ checkboxOn: "\u2612",
2759
+ checkboxOff: "\u2610",
2760
+ checkboxCircleOn: "\u24e7",
2761
+ checkboxCircleOff: "\u24be",
2762
+ pointer: "\u276f",
2763
+ triangleUpOutline: "\u25b3",
2764
+ triangleLeft: "\u25c0",
2765
+ triangleRight: "\u25b6",
2766
+ lozenge: "\u25c6",
2767
+ lozengeOutline: "\u25c7",
2768
+ hamburger: "\u2630",
2769
+ smiley: "\u32e1",
2770
+ mustache: "\u0df4",
2771
+ star: "\u2605",
2772
+ play: "\u25b6",
2773
+ nodejs: "\u2b22",
2774
+ oneSeventh: "\u2150",
2775
+ oneNinth: "\u2151",
2776
+ oneTenth: "\u2152"
2766
2777
  };
2767
2778
  var specialFallbackSymbols = {
2768
- tick: "",
2779
+ tick: "\u221a",
2769
2780
  info: "i",
2770
- warning: "",
2771
- cross: "×",
2772
- squareSmall: "",
2773
- squareSmallFilled: "",
2781
+ warning: "\u203c",
2782
+ cross: "\u00d7",
2783
+ squareSmall: "\u25a1",
2784
+ squareSmallFilled: "\u25a0",
2774
2785
  circle: "( )",
2775
2786
  circleFilled: "(*)",
2776
2787
  circleDotted: "( )",
2777
2788
  circleDouble: "( )",
2778
- circleCircle: "()",
2779
- circleCross: "(×)",
2780
- circlePipe: "()",
2789
+ circleCircle: "(\u25cb)",
2790
+ circleCross: "(\u00d7)",
2791
+ circlePipe: "(\u2502)",
2781
2792
  radioOn: "(*)",
2782
2793
  radioOff: "( )",
2783
- checkboxOn: "[×]",
2794
+ checkboxOn: "[\u00d7]",
2784
2795
  checkboxOff: "[ ]",
2785
- checkboxCircleOn: "(×)",
2796
+ checkboxCircleOn: "(\u00d7)",
2786
2797
  checkboxCircleOff: "( )",
2787
2798
  pointer: ">",
2788
- triangleUpOutline: "",
2789
- triangleLeft: "",
2790
- triangleRight: "",
2791
- lozenge: "",
2792
- lozengeOutline: "",
2793
- hamburger: "",
2794
- smiley: "",
2795
- mustache: "┌─┐",
2796
- star: "",
2797
- play: "",
2798
- nodejs: "",
2799
+ triangleUpOutline: "\u2206",
2800
+ triangleLeft: "\u25c4",
2801
+ triangleRight: "\u25ba",
2802
+ lozenge: "\u2666",
2803
+ lozengeOutline: "\u25ca",
2804
+ hamburger: "\u2261",
2805
+ smiley: "\u263a",
2806
+ mustache: "\u250c\u2500\u2510",
2807
+ star: "\u2736",
2808
+ play: "\u25ba",
2809
+ nodejs: "\u2666",
2799
2810
  oneSeventh: "1/7",
2800
2811
  oneNinth: "1/9",
2801
2812
  oneTenth: "1/10"
@@ -2821,8 +2832,9 @@ var defaultTheme = {
2821
2832
  },
2822
2833
  spinner: {
2823
2834
  interval: 80,
2824
- frames: ["", "", "", "", "", "", "", "", "", ""].map((frame) => styleText("yellow", frame))
2835
+ frames: ["\u280b", "\u2819", "\u2839", "\u2838", "\u283c", "\u2834", "\u2826", "\u2827", "\u2807", "\u280f"].map((frame) => styleText("yellow", frame))
2825
2836
  },
2837
+ keybindings: [],
2826
2838
  style: {
2827
2839
  answer: (text) => styleText("cyan", text),
2828
2840
  message: (text) => styleText("bold", text),
@@ -2833,6 +2845,12 @@ var defaultTheme = {
2833
2845
  key: (text) => styleText("cyan", styleText("bold", `<${text}>`))
2834
2846
  }
2835
2847
  };
2848
+ function getDefaultTheme() {
2849
+ return {
2850
+ ...defaultTheme,
2851
+ keybindings: getDefaultKeybindings()
2852
+ };
2853
+ }
2836
2854
 
2837
2855
  // ../../node_modules/@inquirer/core/dist/lib/make-theme.js
2838
2856
  function isPlainObject(value) {
@@ -2856,7 +2874,7 @@ function deepMerge(...objects) {
2856
2874
  }
2857
2875
  function makeTheme(...themes) {
2858
2876
  const themesToMerge = [
2859
- defaultTheme,
2877
+ getDefaultTheme(),
2860
2878
  ...themes.filter((theme) => theme != null)
2861
2879
  ];
2862
2880
  return deepMerge(...themesToMerge);
@@ -3060,7 +3078,7 @@ var dist_default3 = fastStringWidth;
3060
3078
 
3061
3079
  // ../../node_modules/fast-wrap-ansi/lib/main.js
3062
3080
  var ESC = "\x1B";
3063
- var CSI = "›";
3081
+ var CSI = "\u009b";
3064
3082
  var END_CODE = 39;
3065
3083
  var ANSI_ESCAPE_BELL = "\x07";
3066
3084
  var ANSI_CSI = "[";
@@ -3273,7 +3291,7 @@ function wrapAnsi(string, columns, options) {
3273
3291
  // ../../node_modules/@inquirer/core/dist/lib/utils.js
3274
3292
  function breakLines(content, width) {
3275
3293
  return content.split(`
3276
- `).flatMap((line) => wrapAnsi(line, width, { trim: false, hard: true }).split(`
3294
+ `).flatMap((line) => wrapAnsi(line, width, { trim: false, wordWrap: false }).split(`
3277
3295
  `).map((str) => str.trimEnd())).join(`
3278
3296
  `);
3279
3297
  }
@@ -3638,6 +3656,7 @@ class ScreenManager {
3638
3656
  let output = cursorDown(this.extraLinesUnderPrompt);
3639
3657
  output += clearContent ? eraseLines(this.height) : `
3640
3658
  `;
3659
+ output += cursorLeft;
3641
3660
  output += cursorShow;
3642
3661
  this.write(output);
3643
3662
  this.rl.close();
@@ -3661,7 +3680,7 @@ class PromisePolyfill extends Promise {
3661
3680
  import path from "node:path";
3662
3681
  var nativeSetImmediate = globalThis.setImmediate;
3663
3682
  function getCallSites() {
3664
- const _prepareStackTrace = Error.prepareStackTrace;
3683
+ const savedPrepareStackTrace = Error.prepareStackTrace;
3665
3684
  let result = [];
3666
3685
  try {
3667
3686
  Error.prepareStackTrace = (_, callSites) => {
@@ -3673,7 +3692,7 @@ function getCallSites() {
3673
3692
  } catch {
3674
3693
  return result;
3675
3694
  }
3676
- Error.prepareStackTrace = _prepareStackTrace;
3695
+ Error.prepareStackTrace = savedPrepareStackTrace;
3677
3696
  return result;
3678
3697
  }
3679
3698
  function createPrompt(view) {
@@ -3789,11 +3808,10 @@ var selectTheme = {
3789
3808
  style: {
3790
3809
  disabled: (text) => styleText3("dim", text),
3791
3810
  description: (text) => styleText3("cyan", text),
3792
- keysHelpTip: (keys) => keys.map(([key, action]) => `${styleText3("bold", key)} ${styleText3("dim", action)}`).join(styleText3("dim", " "))
3811
+ keysHelpTip: (keys) => keys.map(([key, action]) => `${styleText3("bold", key)} ${styleText3("dim", action)}`).join(styleText3("dim", " \u2022 "))
3793
3812
  },
3794
3813
  i18n: { disabledError: "This option is disabled and cannot be selected." },
3795
- indexMode: "hidden",
3796
- keybindings: []
3814
+ indexMode: "hidden"
3797
3815
  };
3798
3816
  function isSelectable(item) {
3799
3817
  return !Separator.isSeparator(item) && !item.disabled;
@@ -3830,11 +3848,11 @@ function normalizeChoices(choices) {
3830
3848
  var dist_default4 = createPrompt((config, done) => {
3831
3849
  const { loop = true, pageSize = 7 } = config;
3832
3850
  const theme = makeTheme(selectTheme, config.theme);
3833
- const { keybindings } = theme;
3851
+ const { keybindings: keybindings2 } = theme;
3834
3852
  const [status, setStatus] = useState("idle");
3835
3853
  const prefix = usePrefix({ status, theme });
3836
3854
  const searchTimeoutRef = useRef();
3837
- const searchEnabled = !keybindings.includes("vim");
3855
+ const searchEnabled = !keybindings2.includes("vim");
3838
3856
  const items = useMemo(() => normalizeChoices(config.choices), [config.choices]);
3839
3857
  const bounds = useMemo(() => {
3840
3858
  const first = items.findIndex(isNavigable);
@@ -3867,10 +3885,10 @@ var dist_default4 = createPrompt((config, done) => {
3867
3885
  setStatus("done");
3868
3886
  done(selectedChoice.value);
3869
3887
  }
3870
- } else if (isUpKey(key, keybindings) || isDownKey(key, keybindings)) {
3888
+ } else if (isUpKey(key, keybindings2) || isDownKey(key, keybindings2)) {
3871
3889
  rl.clearLine(0);
3872
- if (loop || isUpKey(key, keybindings) && active !== bounds.first || isDownKey(key, keybindings) && active !== bounds.last) {
3873
- const offset = isUpKey(key, keybindings) ? -1 : 1;
3890
+ if (loop || isUpKey(key, keybindings2) && active !== bounds.first || isDownKey(key, keybindings2) && active !== bounds.last) {
3891
+ const offset = isUpKey(key, keybindings2) ? -1 : 1;
3874
3892
  let next = active;
3875
3893
  do {
3876
3894
  next = (next + offset + items.length) % items.length;
@@ -3915,8 +3933,8 @@ var dist_default4 = createPrompt((config, done) => {
3915
3933
  }, []);
3916
3934
  const message = theme.style.message(config.message, status);
3917
3935
  const helpLine = theme.style.keysHelpTip([
3918
- ["↑↓", "navigate"],
3919
- ["", "select"]
3936
+ ["\u2191\u2193", "navigate"],
3937
+ ["\u23ce", "select"]
3920
3938
  ]);
3921
3939
  let separatorCount = 0;
3922
3940
  const page = usePagination({
@@ -3979,6 +3997,7 @@ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileS
3979
3997
  import { randomUUID } from "node:crypto";
3980
3998
  import { basename, dirname, join, resolve } from "node:path";
3981
3999
  import { mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
4000
+ import { execFileSync } from "node:child_process";
3982
4001
  import { basename as basename2, dirname as dirname2, isAbsolute, join as join2, relative, resolve as resolve2 } from "node:path";
3983
4002
  import { existsSync as existsSync2, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "node:fs";
3984
4003
  import { join as join4, resolve as resolve4 } from "node:path";
@@ -3986,10 +4005,10 @@ import { mkdirSync as mkdirSync3, rmdirSync, statSync as statSync2, unlinkSync a
3986
4005
  import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve as resolve3 } from "node:path";
3987
4006
  import { setTimeout as sleep } from "node:timers/promises";
3988
4007
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
3989
- import { execFileSync } from "node:child_process";
4008
+ import { execFileSync as execFileSync2 } from "node:child_process";
3990
4009
  import { existsSync as existsSync3 } from "node:fs";
3991
4010
  import { isAbsolute as isAbsolute3, resolve as resolve5 } from "node:path";
3992
- import { execFileSync as execFileSync2 } from "node:child_process";
4011
+ import { execFileSync as execFileSync3 } from "node:child_process";
3993
4012
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, realpathSync, statSync as statSync3, writeFileSync as writeFileSync3 } from "node:fs";
3994
4013
  import { basename as basename3, dirname as dirname4, isAbsolute as isAbsolute4, join as join5, resolve as resolve6 } from "node:path";
3995
4014
  import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
@@ -4030,18 +4049,45 @@ function resolveHarnessDir(startDir = process.cwd(), opts = {}) {
4030
4049
  const explicit = opts.harnessDir ?? process.env.MSTAR_HARNESS_DIR;
4031
4050
  if (explicit)
4032
4051
  return resolve2(start, explicit);
4052
+ const boundary = resolve2(start, opts.workspaceRoot ?? defaultWorkspaceRoot(start));
4033
4053
  let dir = start;
4034
4054
  for (;; ) {
4055
+ if (!isAtOrBelow(dir, boundary))
4056
+ return null;
4035
4057
  for (const candidate of [join2(dir, ".mstar"), join2(dir, ".agents"), join2(dir, ".plans"), join2(dir, "plans")]) {
4036
4058
  if (isDirectory(candidate))
4037
4059
  return candidate;
4038
4060
  }
4061
+ if (dir === boundary)
4062
+ return null;
4039
4063
  const parent = dirname2(dir);
4040
4064
  if (parent === dir)
4041
4065
  return null;
4042
4066
  dir = parent;
4043
4067
  }
4044
4068
  }
4069
+ function defaultWorkspaceRoot(startDir) {
4070
+ try {
4071
+ const cdup = execFileSync("git", ["rev-parse", "--show-cdup"], {
4072
+ cwd: startDir,
4073
+ encoding: "utf8",
4074
+ stdio: ["ignore", "pipe", "ignore"]
4075
+ }).trim();
4076
+ if (!cdup)
4077
+ return startDir;
4078
+ let boundary = startDir;
4079
+ for (const segment of cdup.split(/[\\/]/)) {
4080
+ if (segment && segment !== ".")
4081
+ boundary = dirname2(boundary);
4082
+ }
4083
+ return resolve2(boundary);
4084
+ } catch {}
4085
+ return startDir;
4086
+ }
4087
+ function isAtOrBelow(dir, root) {
4088
+ const rel = relative(root, dir);
4089
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
4090
+ }
4045
4091
  function resolveSpecsDir(harnessDir, opts = {}) {
4046
4092
  const harness = resolve2(harnessDir);
4047
4093
  const repoRoot = dirname2(harness);
@@ -4063,7 +4109,7 @@ function resolveSpecsDir(harnessDir, opts = {}) {
4063
4109
  }
4064
4110
  function assertSafePathComponent(value, what) {
4065
4111
  if (value === "" || value === "." || value === ".." || !/^[A-Za-z0-9._-]+$/.test(value)) {
4066
- throw new Error(`${what} must be a single safe path component ([A-Za-z0-9._-]+; not "", ".", "..", or containing "/" or "\\") got ${JSON.stringify(value)}`);
4112
+ throw new Error(`${what} must be a single safe path component ([A-Za-z0-9._-]+; not "", ".", "..", or containing "/" or "\\") \u2014 got ${JSON.stringify(value)}`);
4067
4113
  }
4068
4114
  }
4069
4115
  function resolveSddDir(harnessDir, planId) {
@@ -4080,7 +4126,7 @@ var GITIGNORE_SNIPPET = `# Morning Star harness (.mstar/)
4080
4126
  !.mstar/specs/
4081
4127
  !.mstar/specs/**
4082
4128
  `;
4083
- var GITIGNORE_SNIPPET_AGENTS = `# Morning Star harness (.agents/) legacy
4129
+ var GITIGNORE_SNIPPET_AGENTS = `# Morning Star harness (.agents/) \u2014 legacy
4084
4130
  # Default-ignore everything under .agents/, then re-include the tracked results.
4085
4131
  .agents/**
4086
4132
  !.agents/AGENTS.md
@@ -4125,7 +4171,7 @@ async function withStatusWriteLock(statusPath, fn, opts = {}) {
4125
4171
  const lockDir = join3(dirname3(resolve3(statusPath)), STATUS_WRITE_LOCKDIR);
4126
4172
  const held = heldLockDirs.getStore();
4127
4173
  if (held !== undefined && held.has(lockDir)) {
4128
- throw new Error(`${lockDir} is already held by this process in this async context withStatusWriteLock is not reentrant; a nested acquisition on the same status.json is a bug`);
4174
+ throw new Error(`${lockDir} is already held by this process in this async context \u2014 withStatusWriteLock is not reentrant; a nested acquisition on the same status.json is a bug`);
4129
4175
  }
4130
4176
  const timeoutMs = opts.timeoutMs ?? 30000;
4131
4177
  const pollMs = opts.pollMs ?? 25;
@@ -4141,7 +4187,7 @@ async function withStatusWriteLock(statusPath, fn, opts = {}) {
4141
4187
  if (error.code !== "EEXIST")
4142
4188
  throw error;
4143
4189
  if (Date.now() >= deadline) {
4144
- throw new Error(`${lockDir} already exists another writer holds the status write lock; Blocked (same-host exclusive lock; status-and-residuals.md § Same-host exclusive write lock). ` + `Recovery: remove ${lockDir} if no writer is alive (holder.pid inside names the acquiring process)`);
4190
+ throw new Error(`${lockDir} already exists \u2014 another writer holds the status write lock; Blocked (same-host exclusive lock; status-and-residuals.md \u00a7 Same-host exclusive write lock). ` + `Recovery: remove ${lockDir} if no writer is alive (holder.pid inside names the acquiring process)`);
4145
4191
  }
4146
4192
  await sleep(pollMs);
4147
4193
  }
@@ -4166,7 +4212,7 @@ async function withStatusWriteLock(statusPath, fn, opts = {}) {
4166
4212
  } catch {}
4167
4213
  }
4168
4214
  }
4169
- var BRANCH_FORMS_HINT = '"Working branch: <existing>" | "Working branch: create <new> from <base>" | "Branch policy: direct on <branch> <reason>"';
4215
+ var BRANCH_FORMS_HINT = '"Working branch: <existing>" | "Working branch: create <new> from <base>" | "Branch policy: direct on <branch> \u2014 <reason>"';
4170
4216
  var REQUIRED_FIELDS = [
4171
4217
  { key: "executeAs", label: "Execute as", code: "execute-as" },
4172
4218
  { key: "delegation", label: "Delegation", code: "delegation" },
@@ -4233,7 +4279,7 @@ function parseAssignmentBranchForms(assignmentText) {
4233
4279
  if (fields.branchPolicy !== undefined && fields.branchPolicy !== "") {
4234
4280
  const direct = fields.branchPolicy.match(/^direct\s+on\s+(\S+)/i);
4235
4281
  if (direct) {
4236
- const strict = fields.branchPolicy.match(/^direct\s+on\s+(\S+)(?:\s*(?:[—–]|--|-)\s*(.+))?$/);
4282
+ const strict = fields.branchPolicy.match(/^direct\s+on\s+(\S+)(?:\s*(?:[\u2014\u2013]|--|-)\s*(.+))?$/);
4237
4283
  forms.directOn = { branch: direct[1].trim(), reason: strict ? (strict[2] ?? "").trim() : "" };
4238
4284
  }
4239
4285
  }
@@ -4262,7 +4308,7 @@ function validateAssignmentFields(assignmentText, opts = {}) {
4262
4308
  if (formCount === 0) {
4263
4309
  violations.push(violation2("high", "assignment.field.branch-missing", "writable assignment must contain exactly one branch form", `add exactly one of: ${BRANCH_FORMS_HINT}`));
4264
4310
  } else if (formCount > 1) {
4265
- violations.push(violation2("high", "assignment.field.branch-multiple", `writable assignment contains ${formCount} branch forms (Working branch + Branch policy) exactly one required`, `keep exactly one of: ${BRANCH_FORMS_HINT}`));
4311
+ violations.push(violation2("high", "assignment.field.branch-multiple", `writable assignment contains ${formCount} branch forms (Working branch + Branch policy) \u2014 exactly one required`, `keep exactly one of: ${BRANCH_FORMS_HINT}`));
4266
4312
  } else if (workingPresent) {
4267
4313
  const create = forms.createForm;
4268
4314
  if (create !== undefined && (create.base === undefined || create.base.trim() === "" || create.name.trim() === "")) {
@@ -4271,9 +4317,9 @@ function validateAssignmentFields(assignmentText, opts = {}) {
4271
4317
  } else if (policyPresent) {
4272
4318
  const direct = forms.directOn;
4273
4319
  if (direct === undefined) {
4274
- violations.push(violation2("high", "assignment.field.branch-policy-missing-branch", `unparseable Branch policy: "${fields.branchPolicy}" (expected "direct on <branch> <reason>")`, "start the field with `direct on <branch>`"));
4320
+ violations.push(violation2("high", "assignment.field.branch-policy-missing-branch", `unparseable Branch policy: "${fields.branchPolicy}" (expected "direct on <branch> \u2014 <reason>")`, "start the field with `direct on <branch>`"));
4275
4321
  } else if (direct.reason === "") {
4276
- violations.push(violation2("high", "assignment.field.branch-policy-missing-reason", `Branch policy "direct on ${direct.branch}" is missing the reason`, 'append " <reason>" after the branch name'));
4322
+ violations.push(violation2("high", "assignment.field.branch-policy-missing-reason", `Branch policy "direct on ${direct.branch}" is missing the reason`, 'append "\u2014 <reason>" after the branch name'));
4277
4323
  }
4278
4324
  }
4279
4325
  }
@@ -4284,7 +4330,7 @@ function assertDefaultBranchProtected(branch, opts = {}) {
4284
4330
  const violations = [];
4285
4331
  const normalized = branch.trim();
4286
4332
  if (normalized !== "" && defaultBranches.includes(normalized) && opts.directOnException !== true) {
4287
- violations.push(violation2("high", "dispatch.default-branch.protected", `writable work on default protected branch "${normalized}" requires an explicit direct-on exception`, `add "Branch policy: direct on ${normalized} <reason>" to the Assignment, or use a feature branch`));
4333
+ violations.push(violation2("high", "dispatch.default-branch.protected", `writable work on default protected branch "${normalized}" requires an explicit direct-on exception`, `add "Branch policy: direct on ${normalized} \u2014 <reason>" to the Assignment, or use a feature branch`));
4288
4334
  }
4289
4335
  return { ok: violations.length === 0, violations };
4290
4336
  }
@@ -4301,9 +4347,9 @@ function executionModeToN(executionMode, opts = {}) {
4301
4347
  } else if (mode === "targeted") {
4302
4348
  const seats = [...new Set((opts.seats ?? []).map((s) => s.trim()).filter((s) => s !== ""))];
4303
4349
  if (seats.length === 0) {
4304
- violations.push(violation2("high", "dispatch.execution-mode.missing-seats", 'execution mode "targeted" requires listed reviewer seats', 'add "QC re-review: targeted reviewers: <role-id>, " to the Assignment and pass the seats'));
4350
+ violations.push(violation2("high", "dispatch.execution-mode.missing-seats", 'execution mode "targeted" requires listed reviewer seats', 'add "QC re-review: targeted \u2014 reviewers: <role-id>, \u2026" to the Assignment and pass the seats'));
4305
4351
  } else if (seats.length > 3) {
4306
- violations.push(violation2("high", "dispatch.execution-mode.too-many-seats", `execution mode "targeted" lists ${seats.length} reviewer seats at most 3 (targeted re-review seats are the tri seats, N = 1–3)`, "list at most three reviewer seats for the targeted re-review"));
4352
+ violations.push(violation2("high", "dispatch.execution-mode.too-many-seats", `execution mode "targeted" lists ${seats.length} reviewer seats \u2014 at most 3 (targeted re-review seats are the tri seats, N = 1\u20133)`, "list at most three reviewer seats for the targeted re-review"));
4307
4353
  } else {
4308
4354
  n = seats.length;
4309
4355
  }
@@ -4370,7 +4416,7 @@ function validatePlanRow(row) {
4370
4416
  validateNonEmptyString2(violations, planId, "plan_id", "status.plan-row.missing-plan-id", "status.plan-row.invalid-plan-id");
4371
4417
  }
4372
4418
  if (id !== undefined && planId !== undefined && id !== planId) {
4373
- violations.push(violation3("medium", "status.plan-row.dual-id", "row has both id and plan_id with different values write one canonical key (prefer id)"));
4419
+ violations.push(violation3("medium", "status.plan-row.dual-id", "row has both id and plan_id with different values \u2014 write one canonical key (prefer id)"));
4374
4420
  }
4375
4421
  }
4376
4422
  validateNonEmptyString2(violations, title, "title", "status.plan-row.missing-title", "status.plan-row.invalid-title");
@@ -4378,7 +4424,7 @@ function validatePlanRow(row) {
4378
4424
  if (status === undefined) {
4379
4425
  violations.push(violation3("high", "status.plan-row.missing-status", "missing required field: status"));
4380
4426
  } else if (typeof status !== "string" || !PLAN_STATUSES.includes(status)) {
4381
- violations.push(violation3("medium", "status.plan-row.invalid-status", `status must be one of ${PLAN_STATUSES.join(" | ")} got ${JSON.stringify(status)}`));
4427
+ violations.push(violation3("medium", "status.plan-row.invalid-status", `status must be one of ${PLAN_STATUSES.join(" | ")} \u2014 got ${JSON.stringify(status)}`));
4382
4428
  }
4383
4429
  if (metadata !== undefined && !isPlainObject2(metadata)) {
4384
4430
  violations.push(violation3("medium", "status.plan-row.invalid-metadata", "metadata must be an object"));
@@ -4387,7 +4433,7 @@ function validatePlanRow(row) {
4387
4433
  violations.push(violation3("medium", "status.plan-row.invalid-execution-lease", "execution_lease must be an object"));
4388
4434
  }
4389
4435
  if (status === "Done" && execution_lease !== undefined) {
4390
- violations.push(violation3("medium", "status.plan-row.done-with-lease", 'plan status Done must not carry an execution_lease the Done authority deletes the lease in the same complete-file update as status: "Done" (status-and-residuals.md § Hold, release, and override)', 'delete plans[].execution_lease in the same update that sets status: "Done"'));
4436
+ violations.push(violation3("medium", "status.plan-row.done-with-lease", 'plan status Done must not carry an execution_lease \u2014 the Done authority deletes the lease in the same complete-file update as status: "Done" (status-and-residuals.md \u00a7 Hold, release, and override)', 'delete plans[].execution_lease in the same update that sets status: "Done"'));
4391
4437
  }
4392
4438
  return { ok: violations.length === 0, violations };
4393
4439
  }
@@ -4405,14 +4451,14 @@ function validateResidual(entry) {
4405
4451
  if (severity === undefined) {
4406
4452
  violations.push(violation3("high", "status.residual.missing-severity", "missing required field: severity"));
4407
4453
  } else if (typeof severity !== "string" || !SEVERITY_ORDER.includes(severity) && severity !== "warning") {
4408
- violations.push(violation3("medium", "status.residual.invalid-severity", `severity must be one of ${SEVERITY_ORDER.join(" | ")} got ${JSON.stringify(severity)}`));
4454
+ violations.push(violation3("medium", "status.residual.invalid-severity", `severity must be one of ${SEVERITY_ORDER.join(" | ")} \u2014 got ${JSON.stringify(severity)}`));
4409
4455
  } else if (severity === "warning") {
4410
- violations.push(violation3("low", "status.residual.legacy-warning", `severity "warning" is legacy forbidden on new entries; read paths normalize it to "low"`, `use "low" (normalizeSeverity maps 'warning' 'low')`));
4456
+ violations.push(violation3("low", "status.residual.legacy-warning", `severity "warning" is legacy \u2014 forbidden on new entries; read paths normalize it to "low"`, `use "low" (normalizeSeverity maps 'warning' \u2192 'low')`));
4411
4457
  }
4412
4458
  if (decision === undefined) {
4413
4459
  violations.push(violation3("high", "status.residual.missing-decision", "missing required field: decision"));
4414
4460
  } else if (typeof decision !== "string" || !RESIDUAL_DECISIONS.includes(decision)) {
4415
- violations.push(violation3("medium", "status.residual.invalid-decision", `decision must be one of ${RESIDUAL_DECISIONS.join(" | ")} got ${JSON.stringify(decision)}`));
4461
+ violations.push(violation3("medium", "status.residual.invalid-decision", `decision must be one of ${RESIDUAL_DECISIONS.join(" | ")} \u2014 got ${JSON.stringify(decision)}`));
4416
4462
  }
4417
4463
  if (target === undefined) {
4418
4464
  violations.push(violation3("high", "status.residual.missing-target", "missing required field: target"));
@@ -4432,7 +4478,7 @@ function validateResidual(entry) {
4432
4478
  }
4433
4479
  if (lifecycle !== undefined) {
4434
4480
  if (typeof lifecycle !== "string" || !RESIDUAL_LIFECYCLES.includes(lifecycle)) {
4435
- violations.push(violation3("medium", "status.residual.invalid-lifecycle", `lifecycle must be one of ${RESIDUAL_LIFECYCLES.join(" | ")} got ${JSON.stringify(lifecycle)}`));
4481
+ violations.push(violation3("medium", "status.residual.invalid-lifecycle", `lifecycle must be one of ${RESIDUAL_LIFECYCLES.join(" | ")} \u2014 got ${JSON.stringify(lifecycle)}`));
4436
4482
  } else if (lifecycle !== "open") {
4437
4483
  if (closed_at === undefined) {
4438
4484
  violations.push(violation3("high", "status.residual.closed-missing-closed-at", `lifecycle "${lifecycle}" requires closed_at (YYYY-MM-DD)`, 'set closed_at (e.g. "2026-08-08")'));
@@ -4465,7 +4511,7 @@ function validateStatus(docOrPath) {
4465
4511
  } else if (typeof version !== "number" || !Number.isInteger(version)) {
4466
4512
  violations.push(violation3("high", "status.invalid-version", "version must be an integer"));
4467
4513
  } else if (version !== 1) {
4468
- violations.push(violation3("medium", "status.unsupported-version", `unsupported status.json schema version ${version} expected 1`));
4514
+ violations.push(violation3("medium", "status.unsupported-version", `unsupported status.json schema version ${version} \u2014 expected 1`));
4469
4515
  }
4470
4516
  if (updated_at === undefined) {
4471
4517
  violations.push(violation3("high", "status.missing-updated-at", "missing required field: updated_at"));
@@ -4490,7 +4536,7 @@ function validateStatus(docOrPath) {
4490
4536
  if (!Array.isArray(list)) {
4491
4537
  violations.push(violation3("high", "status.residual.invalid-list", `residual_findings["${planId}"] must be an array`));
4492
4538
  } else if (list.length === 0) {
4493
- violations.push(violation3("low", "status.residual.empty-key", `residual_findings["${planId}"] is empty delete the key (no "plan-id": [])`));
4539
+ violations.push(violation3("low", "status.residual.empty-key", `residual_findings["${planId}"] is empty \u2014 delete the key (no "plan-id": [])`));
4494
4540
  } else {
4495
4541
  for (const entry of list) {
4496
4542
  violations.push(...validateResidual(entry).violations);
@@ -4503,14 +4549,14 @@ function validateStatus(docOrPath) {
4503
4549
  } else if (!isPlainObject2(metadata)) {
4504
4550
  violations.push(violation3("high", "status.invalid-metadata", "metadata must be an object"));
4505
4551
  } else if (Object.prototype.hasOwnProperty.call(metadata, "residual_findings")) {
4506
- violations.push(violation3("medium", "status.dual-write-residuals", "residual_findings must be root-only metadata.residual_findings is legacy read-only; remove it (no dual-write)", "move entries to root residual_findings and delete metadata.residual_findings"));
4552
+ violations.push(violation3("medium", "status.dual-write-residuals", "residual_findings must be root-only \u2014 metadata.residual_findings is legacy read-only; remove it (no dual-write)", "move entries to root residual_findings and delete metadata.residual_findings"));
4507
4553
  }
4508
4554
  return { ok: violations.length === 0, violations };
4509
4555
  }
4510
4556
  async function archiveResiduals(planId, harnessDir) {
4511
4557
  const dir = harnessDir !== undefined ? resolve4(harnessDir) : resolveHarnessDir();
4512
4558
  if (dir === null) {
4513
- throw new Error(`harness dir not found from ${process.cwd()} pass harnessDir or set MSTAR_HARNESS_DIR`);
4559
+ throw new Error(`harness dir not found from ${process.cwd()} \u2014 pass harnessDir or set MSTAR_HARNESS_DIR`);
4514
4560
  }
4515
4561
  assertSafePathComponent(planId, "planId");
4516
4562
  const statusPath = join4(dir, "status.json");
@@ -4565,7 +4611,7 @@ function probeBranch(worktreePath, opts) {
4565
4611
  return { branch: precomputed };
4566
4612
  const timeout = opts.timeoutMs ?? probeTimeoutMs();
4567
4613
  try {
4568
- const stdout = execFileSync(opts.gitPath ?? "git", ["-C", worktreePath, "branch", "--show-current"], {
4614
+ const stdout = execFileSync2(opts.gitPath ?? "git", ["-C", worktreePath, "branch", "--show-current"], {
4569
4615
  encoding: "utf8",
4570
4616
  stdio: ["ignore", "pipe", "pipe"],
4571
4617
  timeout
@@ -4587,16 +4633,16 @@ function l1PreDispatchCheck(input, opts = {}) {
4587
4633
  const violations = [];
4588
4634
  const { controlWorktreePath, leaseWorktreePath, leaseWorkingBranch, planId } = input;
4589
4635
  if (controlWorktreePath.trim() === "") {
4590
- violations.push(violation4("high", "worktree.l1.control-missing", "metadata.control_worktree_path is not recorded the L1 control worktree (integration-branch checkout) must be recorded in status.json before writable dispatch", "record the control worktree path in status.json metadata.control_worktree_path"));
4636
+ violations.push(violation4("high", "worktree.l1.control-missing", "metadata.control_worktree_path is not recorded \u2014 the L1 control worktree (integration-branch checkout) must be recorded in status.json before writable dispatch", "record the control worktree path in status.json metadata.control_worktree_path"));
4591
4637
  }
4592
4638
  if (leaseWorktreePath.trim() === "") {
4593
- violations.push(violation4("high", "worktree.l1.lease-missing", `execution_lease.worktree_path is empty for plan "${planId}" no verified execution_lease to dispatch against`, "claim the execution_lease with an absolute feature worktree path before dispatch"));
4639
+ violations.push(violation4("high", "worktree.l1.lease-missing", `execution_lease.worktree_path is empty for plan "${planId}" \u2014 no verified execution_lease to dispatch against`, "claim the execution_lease with an absolute feature worktree path before dispatch"));
4594
4640
  }
4595
4641
  if (leaseWorkingBranch.trim() === "") {
4596
4642
  violations.push(violation4("high", "worktree.l1.lease-branch-missing", `execution_lease.working_branch is empty for plan "${planId}"`, "record the lease working_branch before dispatch"));
4597
4643
  }
4598
4644
  if (controlWorktreePath !== "" && leaseWorktreePath !== "" && resolve5(controlWorktreePath) === resolve5(leaseWorktreePath)) {
4599
- violations.push(violation4("critical", "worktree.l1.lease-equals-control", `execution_lease.worktree_path "${leaseWorktreePath}" equals metadata.control_worktree_path the feature worktree MUST differ from the control worktree (L1 isolation; product edits never land in the control checkout)`, "use a distinct feature worktree for the plan (git worktree add <path> <branch>) and update the lease"));
4645
+ violations.push(violation4("critical", "worktree.l1.lease-equals-control", `execution_lease.worktree_path "${leaseWorktreePath}" equals metadata.control_worktree_path \u2014 the feature worktree MUST differ from the control worktree (L1 isolation; product edits never land in the control checkout)`, "use a distinct feature worktree for the plan (git worktree add <path> <branch>) and update the lease"));
4600
4646
  }
4601
4647
  if (leaseWorktreePath !== "" && !existsSync3(leaseWorktreePath)) {
4602
4648
  violations.push(violation4("high", "worktree.l1.feature-missing", `feature worktree directory "${leaseWorktreePath}" does not exist for plan "${planId}"`, `create it before dispatch: git worktree add ${leaseWorktreePath} <working-branch>`));
@@ -4615,7 +4661,7 @@ function l2PreDispatchCheck(input, opts = {}) {
4615
4661
  const tracks = input.tracks ?? [];
4616
4662
  const seenPaths = new Set;
4617
4663
  if (tracks.length < 1) {
4618
- violations.push(violation4("high", "worktree.l2.no-tracks", "no parallel writable tracks the L2 pre-dispatch checklist requires at least one track with an absolute worktreePath and Working branch", "pass each track's absolute Worktree path and PM-approved Working branch"));
4664
+ violations.push(violation4("high", "worktree.l2.no-tracks", "no parallel writable tracks \u2014 the L2 pre-dispatch checklist requires at least one track with an absolute worktreePath and Working branch", "pass each track's absolute Worktree path and PM-approved Working branch"));
4619
4665
  }
4620
4666
  tracks.forEach((track, index) => {
4621
4667
  if (track.worktreePath.trim() === "" || track.workingBranch.trim() === "") {
@@ -4623,12 +4669,12 @@ function l2PreDispatchCheck(input, opts = {}) {
4623
4669
  return;
4624
4670
  }
4625
4671
  if (!isAbsolute3(track.worktreePath)) {
4626
- violations.push(violation4("high", "worktree.l2.track-path-relative", `track ${index + 1} worktreePath "${track.worktreePath}" is not an absolute path L2 tracks MUST use absolute worktree checkout paths (consistent with the lease validator's absolute worktree_path enforcement)`, `use an absolute path for track ${index + 1} (e.g. /Users/<you>/worktrees/<branch>)`));
4672
+ violations.push(violation4("high", "worktree.l2.track-path-relative", `track ${index + 1} worktreePath "${track.worktreePath}" is not an absolute path \u2014 L2 tracks MUST use absolute worktree checkout paths (consistent with the lease validator's absolute worktree_path enforcement)`, `use an absolute path for track ${index + 1} (e.g. /Users/<you>/worktrees/<branch>)`));
4627
4673
  return;
4628
4674
  }
4629
4675
  const normalized = resolve5(track.worktreePath);
4630
4676
  if (seenPaths.has(normalized)) {
4631
- violations.push(violation4("high", "worktree.l2.track-path-collision", `duplicate worktreePath "${track.worktreePath}" across parallel tracks L2 parallel-writable isolation requires a distinct absolute Worktree path per track (N parallel invokes isolation)`, "give every parallel track its own git worktree checkout"));
4677
+ violations.push(violation4("high", "worktree.l2.track-path-collision", `duplicate worktreePath "${track.worktreePath}" across parallel tracks \u2014 L2 parallel-writable isolation requires a distinct absolute Worktree path per track (N parallel invokes \u2260 isolation)`, "give every parallel track its own git worktree checkout"));
4632
4678
  return;
4633
4679
  }
4634
4680
  seenPaths.add(normalized);
@@ -4670,7 +4716,7 @@ function isFile(file) {
4670
4716
  var GIT_CAPTURE_MAX_BYTES = 64 * 1024 * 1024;
4671
4717
  function gitOut(cwd, args) {
4672
4718
  try {
4673
- return execFileSync2("git", args, {
4719
+ return execFileSync3("git", args, {
4674
4720
  cwd,
4675
4721
  encoding: "utf8",
4676
4722
  stdio: ["ignore", "pipe", "pipe"],
@@ -4725,7 +4771,7 @@ function sddWorkspace(planId, opts = {}) {
4725
4771
  throw new SddScriptError(`mstar sdd workspace: linked worktree at ${root} has no {HARNESS_DIR}/status.json (default gitignore).
4726
4772
  ` + ` Refusing to create a second SDD tree under the feature checkout.
4727
4773
  ` + ` Re-run with MSTAR_CONTROL_ROOT=<control_worktree_path> or: mstar sdd workspace ${planId} <control_worktree_path>
4728
- ` + ` See mstar-branch-worktree «Harness path SSOT under default gitignore».`, 1);
4774
+ ` + ` See mstar-branch-worktree \u00abHarness path SSOT under default gitignore\u00bb.`, 1);
4729
4775
  }
4730
4776
  const harnessOverride = opts.harnessDir ?? (process.env.MSTAR_HARNESS_DIR || undefined);
4731
4777
  let harnessDir;
@@ -4803,7 +4849,7 @@ function reviewPackage(base, head, outFile, opts = {}) {
4803
4849
  const cwd = opts.cwd ?? process.cwd();
4804
4850
  const verifyRef = (ref, what) => {
4805
4851
  try {
4806
- execFileSync2("git", ["rev-parse", "--verify", "--quiet", ref], { cwd, stdio: ["ignore", "pipe", "pipe"] });
4852
+ execFileSync3("git", ["rev-parse", "--verify", "--quiet", ref], { cwd, stdio: ["ignore", "pipe", "pipe"] });
4807
4853
  } catch {
4808
4854
  throw new SddScriptError(`bad ${what}: ${ref}`, 2);
4809
4855
  }
@@ -4823,7 +4869,7 @@ function reviewPackage(base, head, outFile, opts = {}) {
4823
4869
  const shortHead = gitOut(cwd, ["rev-parse", "--short", head]) ?? head;
4824
4870
  out = join5(sddDir, `review-${shortBase}..${shortHead}.diff`);
4825
4871
  }
4826
- const run = (args) => execFileSync2("git", args, { cwd, maxBuffer: GIT_CAPTURE_MAX_BYTES });
4872
+ const run = (args) => execFileSync3("git", args, { cwd, maxBuffer: GIT_CAPTURE_MAX_BYTES });
4827
4873
  const parts = [
4828
4874
  Buffer.from(`# Review package: ${base}..${head}
4829
4875
 
@@ -4927,7 +4973,7 @@ function validateCompassFrontmatter(doc) {
4927
4973
  return {
4928
4974
  ok: false,
4929
4975
  violations: [
4930
- violation5("medium", "COMPASS_INVALID_FIELD", "Compass frontmatter must be a YAML object with iteration_id / start_date / status / iteration_base_branch / target_branch (template: mstar-iteration §1.3)", "Fix the frontmatter of {ITERATION_DIR}/<iteration-id>/delivery-compass.md")
4976
+ violation5("medium", "COMPASS_INVALID_FIELD", "Compass frontmatter must be a YAML object with iteration_id / start_date / status / iteration_base_branch / target_branch (template: mstar-iteration \u00a71.3)", "Fix the frontmatter of {ITERATION_DIR}/<iteration-id>/delivery-compass.md")
4931
4977
  ]
4932
4978
  };
4933
4979
  }
@@ -4937,17 +4983,17 @@ function validateCompassFrontmatter(doc) {
4937
4983
  ok: false,
4938
4984
  violations: parsed.issues.map((issue) => {
4939
4985
  const field = issue.path.join(".") || "(root)";
4940
- return violation5("medium", "COMPASS_INVALID_FIELD", `Compass frontmatter field '${field}' is invalid: ${issue.message}`, `Fix '${field}' in {ITERATION_DIR}/<iteration-id>/delivery-compass.md frontmatter (template: mstar-iteration §1.3)`);
4986
+ return violation5("medium", "COMPASS_INVALID_FIELD", `Compass frontmatter field '${field}' is invalid: ${issue.message}`, `Fix '${field}' in {ITERATION_DIR}/<iteration-id>/delivery-compass.md frontmatter (template: mstar-iteration \u00a71.3)`);
4941
4987
  })
4942
4988
  };
4943
4989
  }
4944
4990
  const violations = [];
4945
4991
  const { status, end_date } = parsed.data;
4946
4992
  if (status === "completed" && end_date === undefined) {
4947
- violations.push(violation5("high", "COMPASS_END_DATE_REQUIRED", "Compass frontmatter status is 'completed' but end_date is missing end_date is required at iteration-close (mstar-iteration §3.4, template Fields guide)", "Add `end_date: YYYY-MM-DD` to the frontmatter"));
4993
+ violations.push(violation5("high", "COMPASS_END_DATE_REQUIRED", "Compass frontmatter status is 'completed' but end_date is missing \u2014 end_date is required at iteration-close (mstar-iteration \u00a73.4, template Fields guide)", "Add `end_date: YYYY-MM-DD` to the frontmatter"));
4948
4994
  }
4949
4995
  if (status !== "completed" && end_date !== undefined) {
4950
- violations.push(violation5("medium", "COMPASS_END_DATE_NOT_ALLOWED", `Compass frontmatter sets end_date while status is '${status}' end_date is only written at iteration-close (mstar-iteration §3.4)`, "Remove end_date until iteration-close"));
4996
+ violations.push(violation5("medium", "COMPASS_END_DATE_NOT_ALLOWED", `Compass frontmatter sets end_date while status is '${status}' \u2014 end_date is only written at iteration-close (mstar-iteration \u00a73.4)`, "Remove end_date until iteration-close"));
4951
4997
  }
4952
4998
  return { ok: violations.length === 0, violations };
4953
4999
  }
@@ -4971,17 +5017,17 @@ function findPlanRow(statusDoc, planId) {
4971
5017
  function entryPlansAllDone(statusDoc, registered) {
4972
5018
  const violations = [];
4973
5019
  if (registered.length === 0) {
4974
- violations.push(violation5("medium", "COMPASS_NO_PLANS", "Compass frontmatter registers no plans the all-plans-Done transition cannot be verified (mstar-iteration §1.3 / Phase transition gates)", "List the iteration's plan ids in the compass frontmatter `plans`"));
5020
+ violations.push(violation5("medium", "COMPASS_NO_PLANS", "Compass frontmatter registers no plans \u2014 the all-plans-Done transition cannot be verified (mstar-iteration \u00a71.3 / Phase transition gates)", "List the iteration's plan ids in the compass frontmatter `plans`"));
4975
5021
  return violations;
4976
5022
  }
4977
5023
  for (const planId of registered) {
4978
5024
  const row = findPlanRow(statusDoc, planId);
4979
5025
  if (row === null) {
4980
- violations.push(violation5("high", "PLAN_NOT_IN_STATUS", `Plan '${planId}' is registered in the compass frontmatter but has no row in status.json plans[] (mstar-iteration §3.1 entry item 1)`, "Add the plan row to {HARNESS_DIR}/status.json"));
5026
+ violations.push(violation5("high", "PLAN_NOT_IN_STATUS", `Plan '${planId}' is registered in the compass frontmatter but has no row in status.json plans[] (mstar-iteration \u00a73.1 entry item 1)`, "Add the plan row to {HARNESS_DIR}/status.json"));
4981
5027
  continue;
4982
5028
  }
4983
5029
  if (row.status !== PLAN_STATUS_DONE) {
4984
- violations.push(violation5("high", "PLAN_NOT_DONE", `Plan '${planId}' status is ${JSON.stringify(row.status)} in status.json all compass-registered plans must be 'Done' before iteration-close (mstar-iteration §3.1 entry item 1)`));
5030
+ violations.push(violation5("high", "PLAN_NOT_DONE", `Plan '${planId}' status is ${JSON.stringify(row.status)} in status.json \u2014 all compass-registered plans must be 'Done' before iteration-close (mstar-iteration \u00a73.1 entry item 1)`));
4985
5031
  }
4986
5032
  }
4987
5033
  return violations;
@@ -4992,14 +5038,14 @@ function entryResidualsOpen(statusDoc, planId) {
4992
5038
  if (residualRoot === undefined || residualRoot === null)
4993
5039
  return violations;
4994
5040
  if (!isPlainObject3(residualRoot)) {
4995
- violations.push(violation5("medium", "RESIDUAL_MALFORMED", "status.json residual_findings must be a plan-id entries object (mstar-iteration §3.1 entry item 2)"));
5041
+ violations.push(violation5("medium", "RESIDUAL_MALFORMED", "status.json residual_findings must be a plan-id \u2192 entries object (mstar-iteration \u00a73.1 entry item 2)"));
4996
5042
  return violations;
4997
5043
  }
4998
5044
  const entries = residualRoot[planId];
4999
5045
  if (entries === undefined)
5000
5046
  return violations;
5001
5047
  if (!Array.isArray(entries)) {
5002
- violations.push(violation5("medium", "RESIDUAL_MALFORMED", `status.json residual_findings['${planId}'] must be an array of residual entries (mstar-iteration §3.1 entry item 2)`));
5048
+ violations.push(violation5("medium", "RESIDUAL_MALFORMED", `status.json residual_findings['${planId}'] must be an array of residual entries (mstar-iteration \u00a73.1 entry item 2)`));
5003
5049
  return violations;
5004
5050
  }
5005
5051
  const openIds = [];
@@ -5012,7 +5058,7 @@ function entryResidualsOpen(statusDoc, planId) {
5012
5058
  openIds.push(typeof entry.id === "string" ? entry.id : "<unnamed>");
5013
5059
  }
5014
5060
  if (openIds.length > 0) {
5015
- violations.push(violation5("high", "OPEN_RESIDUALS", `Plan '${planId}' has ${openIds.length} open residual finding(s) not exempted as blocker-defers (${openIds.join(", ")}) residuals must be closed/archived before iteration-close; only zero-residual blocker-defers (decision: defer + target) may stay open (mstar-iteration §3.1 entry item 2)`, "Close or archive the open residuals, or convert them into blocker-defers (decision: defer + non-empty target) per mstar-plan-artifacts Findings cleanup modes"));
5061
+ violations.push(violation5("high", "OPEN_RESIDUALS", `Plan '${planId}' has ${openIds.length} open residual finding(s) not exempted as blocker-defers (${openIds.join(", ")}) \u2014 residuals must be closed/archived before iteration-close; only zero-residual blocker-defers (decision: defer + target) may stay open (mstar-iteration \u00a73.1 entry item 2)`, "Close or archive the open residuals, or convert them into blocker-defers (decision: defer + non-empty target) per mstar-plan-artifacts Findings cleanup modes"));
5016
5062
  }
5017
5063
  return violations;
5018
5064
  }
@@ -5022,11 +5068,11 @@ function entryFrontmatterComplete(compassDoc) {
5022
5068
  function exitFrontmatterClosed(compassDoc) {
5023
5069
  const violations = [];
5024
5070
  if (compassDoc.status !== "completed") {
5025
- violations.push(violation5("high", "EXIT_STATUS_NOT_COMPLETED", `Compass frontmatter status must be 'completed' at close exit current: ${JSON.stringify(compassDoc.status)} (mstar-iteration §3.4 / §3.5 exit item 4)`));
5071
+ violations.push(violation5("high", "EXIT_STATUS_NOT_COMPLETED", `Compass frontmatter status must be 'completed' at close exit \u2014 current: ${JSON.stringify(compassDoc.status)} (mstar-iteration \u00a73.4 / \u00a73.5 exit item 4)`));
5026
5072
  }
5027
5073
  const endDate = compassDoc.end_date;
5028
5074
  if (typeof endDate !== "string" || !DATE_RE2.test(endDate)) {
5029
- violations.push(violation5("high", "EXIT_END_DATE_REQUIRED", "Compass frontmatter end_date (YYYY-MM-DD) is required when closing (mstar-iteration §3.4 / §3.5 exit item 4)"));
5075
+ violations.push(violation5("high", "EXIT_END_DATE_REQUIRED", "Compass frontmatter end_date (YYYY-MM-DD) is required when closing (mstar-iteration \u00a73.4 / \u00a73.5 exit item 4)"));
5030
5076
  }
5031
5077
  return violations;
5032
5078
  }
@@ -5034,9 +5080,9 @@ function exitBranchCheck(opts) {
5034
5080
  const violations = [];
5035
5081
  const { currentBranch, specIntegrationBranch } = opts;
5036
5082
  if (currentBranch === undefined || specIntegrationBranch === undefined) {
5037
- violations.push(violation5("medium", "EXIT_BRANCH_UNVERIFIABLE", "Cannot verify the current branch is spec_integration_branch missing currentBranch / specIntegrationBranch probe inputs (mstar-iteration §3.5 exit item 5)"));
5083
+ violations.push(violation5("medium", "EXIT_BRANCH_UNVERIFIABLE", "Cannot verify the current branch is spec_integration_branch \u2014 missing currentBranch / specIntegrationBranch probe inputs (mstar-iteration \u00a73.5 exit item 5)"));
5038
5084
  } else if (currentBranch !== specIntegrationBranch) {
5039
- violations.push(violation5("high", "EXIT_BRANCH_MISMATCH", `Current branch '${currentBranch}' is not the spec_integration_branch '${specIntegrationBranch}' (mstar-iteration §3.5 exit item 5)`));
5085
+ violations.push(violation5("high", "EXIT_BRANCH_MISMATCH", `Current branch '${currentBranch}' is not the spec_integration_branch '${specIntegrationBranch}' (mstar-iteration \u00a73.5 exit item 5)`));
5040
5086
  }
5041
5087
  return violations;
5042
5088
  }
@@ -5045,9 +5091,9 @@ function exitPrBaseCheck(compassDoc, opts) {
5045
5091
  const target = compassDoc.target_branch;
5046
5092
  const { prBaseBranch } = opts;
5047
5093
  if (prBaseBranch === undefined) {
5048
- violations.push(violation5("medium", "EXIT_PR_BASE_UNVERIFIABLE", "Cannot verify the PR base missing prBaseBranch probe input (mstar-iteration §3.5 exit item 6)"));
5094
+ violations.push(violation5("medium", "EXIT_PR_BASE_UNVERIFIABLE", "Cannot verify the PR base \u2014 missing prBaseBranch probe input (mstar-iteration \u00a73.5 exit item 6)"));
5049
5095
  } else if (typeof target !== "string" || prBaseBranch !== target) {
5050
- violations.push(violation5("high", "EXIT_PR_BASE_MISMATCH", `PR base '${prBaseBranch}' must equal the compass target_branch '${String(target)}' not an undocumented branch (mstar-iteration §3.5 exit item 6)`));
5096
+ violations.push(violation5("high", "EXIT_PR_BASE_MISMATCH", `PR base '${prBaseBranch}' must equal the compass target_branch '${String(target)}' \u2014 not an undocumented branch (mstar-iteration \u00a73.5 exit item 6)`));
5051
5097
  }
5052
5098
  return violations;
5053
5099
  }
@@ -5089,10 +5135,10 @@ function evaluatePhaseGate(statusDoc, compassDoc, opts = {}) {
5089
5135
  function pushCadenceProbe(ciRunning, reviewWaveActive) {
5090
5136
  const violations = [];
5091
5137
  if (ciRunning) {
5092
- violations.push(violation5("high", "PUSH_BLOCKED_CI", "CI checks are still queued/in_progress on the current head do not push until the wave completes (mstar-iteration §5.1a push gate 1)", "Wait for CI to settle, then push once with the whole local batch"));
5138
+ violations.push(violation5("high", "PUSH_BLOCKED_CI", "CI checks are still queued/in_progress on the current head \u2014 do not push until the wave completes (mstar-iteration \u00a75.1a push gate 1)", "Wait for CI to settle, then push once with the whole local batch"));
5093
5139
  }
5094
5140
  if (reviewWaveActive) {
5095
- violations.push(violation5("high", "PUSH_BLOCKED_REVIEW_WAVE", "An AI/bot review wave is still running on the current head do not push until it settles (mstar-iteration §5.1a push gate 2)", "Wait for the review wave, then push once"));
5141
+ violations.push(violation5("high", "PUSH_BLOCKED_REVIEW_WAVE", "An AI/bot review wave is still running on the current head \u2014 do not push until it settles (mstar-iteration \u00a75.1a push gate 2)", "Wait for the review wave, then push once"));
5096
5142
  }
5097
5143
  return { ok: violations.length === 0, violations };
5098
5144
  }
@@ -5133,7 +5179,7 @@ function parseCompassFrontmatter(filePath) {
5133
5179
  function parseFlowArray(raw, filePath) {
5134
5180
  const inner = raw.slice(1, -1);
5135
5181
  if (/[[\]]/.test(inner)) {
5136
- throw new Error(`nested flow-style array in ${filePath}: ${JSON.stringify(raw)} only flat scalar items are supported (e.g. [a, b])`);
5182
+ throw new Error(`nested flow-style array in ${filePath}: ${JSON.stringify(raw)} \u2014 only flat scalar items are supported (e.g. [a, b])`);
5137
5183
  }
5138
5184
  let quote = null;
5139
5185
  for (const ch of inner) {
@@ -5143,7 +5189,7 @@ function parseFlowArray(raw, filePath) {
5143
5189
  else if (quote === ch)
5144
5190
  quote = null;
5145
5191
  } else if (ch === "," && quote !== null) {
5146
- throw new Error(`ambiguous flow-style array in ${filePath}: ${JSON.stringify(raw)} quoted item containing comma cannot be split unambiguously (flat scalar items only)`);
5192
+ throw new Error(`ambiguous flow-style array in ${filePath}: ${JSON.stringify(raw)} \u2014 quoted item containing comma cannot be split unambiguously (flat scalar items only)`);
5147
5193
  }
5148
5194
  }
5149
5195
  if (quote !== null) {
@@ -5271,7 +5317,7 @@ function validateDesignTokenFrontmatter(frontmatterText) {
5271
5317
  const violations = [];
5272
5318
  const fm = parseDesignFrontmatter(frontmatterText);
5273
5319
  if (fm === null) {
5274
- violations.push(violation6("medium", "design-md.tokens.missing-frontmatter", "no `---` YAML frontmatter block found DESIGN.md must open with a fenced frontmatter holding the token SSOT (design-md-spec §1.5)", "add a `---` fenced frontmatter with version, name, description, and the colors/typography/spacing/rounded groups"));
5320
+ violations.push(violation6("medium", "design-md.tokens.missing-frontmatter", "no `---` YAML frontmatter block found \u2014 DESIGN.md must open with a fenced frontmatter holding the token SSOT (design-md-spec \u00a71.5)", "add a `---` fenced frontmatter with version, name, description, and the colors/typography/spacing/rounded groups"));
5275
5321
  return { ok: false, violations };
5276
5322
  }
5277
5323
  const groupEntries = (group) => {
@@ -5286,61 +5332,61 @@ function validateDesignTokenFrontmatter(frontmatterText) {
5286
5332
  };
5287
5333
  for (const group of ["colors", "typography", "spacing", "rounded"]) {
5288
5334
  if (!isMap(fm[group])) {
5289
- violations.push(violation6("medium", "design-md.tokens.group-not-map", `token group "${group}" must be a YAML map, not a scalar (design-md-spec §1.5)`, `rewrite \`${group}\` as a nested map`));
5335
+ violations.push(violation6("medium", "design-md.tokens.group-not-map", `token group "${group}" must be a YAML map, not a scalar (design-md-spec \u00a71.5)`, `rewrite \`${group}\` as a nested map`));
5290
5336
  } else if (!groupIsMap(group)) {
5291
- violations.push(violation6("medium", "design-md.tokens.group-not-map", `token group "${group}" must be a YAML map, not a scalar (design-md-spec §1.5)`, `rewrite \`${group}\` as a nested map`));
5337
+ violations.push(violation6("medium", "design-md.tokens.group-not-map", `token group "${group}" must be a YAML map, not a scalar (design-md-spec \u00a71.5)`, `rewrite \`${group}\` as a nested map`));
5292
5338
  } else if (groupEntries(group).length === 0) {
5293
- violations.push(violation6("medium", "design-md.tokens.missing-group", `missing required token group "${group}" colors/typography/spacing/rounded are required by the frontmatter SSOT (design-md-spec §1.5)`, `add an active \`${group}:\` block with concrete token values`));
5339
+ violations.push(violation6("medium", "design-md.tokens.missing-group", `missing required token group "${group}" \u2014 colors/typography/spacing/rounded are required by the frontmatter SSOT (design-md-spec \u00a71.5)`, `add an active \`${group}:\` block with concrete token values`));
5294
5340
  }
5295
5341
  }
5296
5342
  if (!isMap(fm.components) || !groupIsMap("components")) {
5297
- violations.push(violation6("medium", "design-md.tokens.group-not-map", `token group "components" must be a YAML map, not a scalar (design-md-spec §1.5)`, `rewrite \`components\` as a nested map`));
5343
+ violations.push(violation6("medium", "design-md.tokens.group-not-map", `token group "components" must be a YAML map, not a scalar (design-md-spec \u00a71.5)`, `rewrite \`components\` as a nested map`));
5298
5344
  }
5299
- const placeholder = (group, name, value) => violations.push(violation6("low", "design-md.tokens.placeholder", `token "${group}.${name}" uses a "[...]" template value placeholders never count as concrete tokens (completeness-checklist § How to use item 5)`, `replace \`${value}\` with a concrete value`));
5345
+ const placeholder = (group, name, value) => violations.push(violation6("low", "design-md.tokens.placeholder", `token "${group}.${name}" uses a "[...]" template value \u2014 placeholders never count as concrete tokens (completeness-checklist \u00a7 How to use item 5)`, `replace \`${value}\` with a concrete value`));
5300
5346
  for (const [name, value] of groupEntries("colors")) {
5301
5347
  if (typeof value !== "string") {
5302
- violations.push(violation6("medium", "design-md.tokens.color-format", `color "${name}" must be a string value (design-md-spec §2.2)`, "quote the color value"));
5348
+ violations.push(violation6("medium", "design-md.tokens.color-format", `color "${name}" must be a string value (design-md-spec \u00a72.2)`, "quote the color value"));
5303
5349
  continue;
5304
5350
  }
5305
5351
  if (isPlaceholder(value)) {
5306
5352
  placeholder("colors", name, value);
5307
5353
  } else if (!HEX_RE.test(value) && !OKLCH_RE.test(value)) {
5308
- violations.push(violation6("medium", "design-md.tokens.color-format", `color "${name}" = "${value}" is not a hex (\`#rrggbb\`/\`#rrggbbaa\`) or oklch() value (design-md-spec §2.2)`, "use an sRGB hex value, optionally with a `-p3` oklch() twin"));
5354
+ violations.push(violation6("medium", "design-md.tokens.color-format", `color "${name}" = "${value}" is not a hex (\`#rrggbb\`/\`#rrggbbaa\`) or oklch() value (design-md-spec \u00a72.2)`, "use an sRGB hex value, optionally with a `-p3` oklch() twin"));
5309
5355
  }
5310
5356
  }
5311
5357
  for (const [name, value] of groupEntries("typography")) {
5312
5358
  if (!isMap(value)) {
5313
- violations.push(violation6("medium", "design-md.tokens.typography-shape", `typography token "${name}" must be a map of the five properties (design-md-spec §1.5)`, "give it fontFamily/fontSize/fontWeight/lineHeight/letterSpacing"));
5359
+ violations.push(violation6("medium", "design-md.tokens.typography-shape", `typography token "${name}" must be a map of the five properties (design-md-spec \u00a71.5)`, "give it fontFamily/fontSize/fontWeight/lineHeight/letterSpacing"));
5314
5360
  continue;
5315
5361
  }
5316
5362
  const keys = Object.keys(value);
5317
5363
  const missing = TYPOGRAPHY_PROPS.filter((p) => !keys.includes(p));
5318
5364
  const extra = keys.filter((k) => !TYPOGRAPHY_PROPS.includes(k));
5319
5365
  if (missing.length > 0 || extra.length > 0) {
5320
- violations.push(violation6("medium", "design-md.tokens.typography-shape", `typography token "${name}" must have exactly the five properties fontFamily/fontSize/fontWeight/lineHeight/letterSpacing (design-md-spec §1.5)${missing.length > 0 ? ` missing: ${missing.join(", ")}` : ""}${extra.length > 0 ? ` extra: ${extra.join(", ")}` : ""}`, "align the token with the five-property shape"));
5366
+ violations.push(violation6("medium", "design-md.tokens.typography-shape", `typography token "${name}" must have exactly the five properties fontFamily/fontSize/fontWeight/lineHeight/letterSpacing (design-md-spec \u00a71.5)${missing.length > 0 ? ` \u2014 missing: ${missing.join(", ")}` : ""}${extra.length > 0 ? ` \u2014 extra: ${extra.join(", ")}` : ""}`, "align the token with the five-property shape"));
5321
5367
  }
5322
5368
  for (const prop of ["fontFamily", "fontSize"]) {
5323
5369
  const v = value[prop];
5324
5370
  if (typeof v === "string" && isPlaceholder(v))
5325
5371
  placeholder("typography", name, v);
5326
5372
  else if (typeof v !== "string" || v.trim() === "") {
5327
- violations.push(violation6("medium", "design-md.tokens.typography-shape", `typography token "${name}" has an empty \`${prop}\` (design-md-spec §1.5)`, `fill \`${prop}\` with a concrete value`));
5373
+ violations.push(violation6("medium", "design-md.tokens.typography-shape", `typography token "${name}" has an empty \`${prop}\` (design-md-spec \u00a71.5)`, `fill \`${prop}\` with a concrete value`));
5328
5374
  }
5329
5375
  }
5330
5376
  }
5331
5377
  if (groupIsMap("spacing")) {
5332
5378
  const spacing = fm.spacing;
5333
5379
  if (!Object.prototype.hasOwnProperty.call(spacing, "base")) {
5334
- violations.push(violation6("medium", "design-md.tokens.spacing-base", "spacing must declare the base unit as `base` (design-md-spec §2.4)", "add `base: 4px` (or 8px) to the spacing group"));
5380
+ violations.push(violation6("medium", "design-md.tokens.spacing-base", "spacing must declare the base unit as `base` (design-md-spec \u00a72.4)", "add `base: 4px` (or 8px) to the spacing group"));
5335
5381
  }
5336
5382
  for (const [name, value] of Object.entries(spacing)) {
5337
5383
  if (name !== "base" && name !== RAW_GROUP && !/^\d+$/.test(name)) {
5338
- violations.push(violation6("medium", "design-md.tokens.spacing-key", `spacing key "${name}" must be \`base\` or a numeric multiplier (design-md-spec §1.5)`, "use numeric scale-step keys or `base`"));
5384
+ violations.push(violation6("medium", "design-md.tokens.spacing-key", `spacing key "${name}" must be \`base\` or a numeric multiplier (design-md-spec \u00a71.5)`, "use numeric scale-step keys or `base`"));
5339
5385
  }
5340
5386
  if (typeof value === "string" && isPlaceholder(value)) {
5341
5387
  placeholder("spacing", name, value);
5342
5388
  } else if (typeof value !== "string" || !PX_RE.test(value)) {
5343
- violations.push(violation6("medium", "design-md.tokens.spacing-format", `spacing value "${name}" = "${String(value)}" is not a px length (design-md-spec §1.5)`, "use a pixel value like `4px`"));
5389
+ violations.push(violation6("medium", "design-md.tokens.spacing-format", `spacing value "${name}" = "${String(value)}" is not a px length (design-md-spec \u00a71.5)`, "use a pixel value like `4px`"));
5344
5390
  }
5345
5391
  }
5346
5392
  }
@@ -5348,12 +5394,12 @@ function validateDesignTokenFrontmatter(frontmatterText) {
5348
5394
  if (typeof value === "string" && isPlaceholder(value)) {
5349
5395
  placeholder("rounded", name, value);
5350
5396
  } else if (typeof value !== "string" || !PX_RE.test(value)) {
5351
- violations.push(violation6("medium", "design-md.tokens.rounded-format", `rounded value "${name}" = "${String(value)}" is not a px length (design-md-spec §1.5)`, "use a pixel value like `6px`"));
5397
+ violations.push(violation6("medium", "design-md.tokens.rounded-format", `rounded value "${name}" = "${String(value)}" is not a px length (design-md-spec \u00a71.5)`, "use a pixel value like `6px`"));
5352
5398
  }
5353
5399
  }
5354
5400
  for (const [name, value] of groupEntries("components")) {
5355
5401
  if (!isMap(value)) {
5356
- violations.push(violation6("medium", "design-md.tokens.components-shape", `component token "${name}" must be a map of properties (design-md-spec §2.8)`, "give it backgroundColor/textColor/typography/rounded/padding/height"));
5402
+ violations.push(violation6("medium", "design-md.tokens.components-shape", `component token "${name}" must be a map of properties (design-md-spec \u00a72.8)`, "give it backgroundColor/textColor/typography/rounded/padding/height"));
5357
5403
  continue;
5358
5404
  }
5359
5405
  for (const [prop, v] of Object.entries(value)) {
@@ -5369,7 +5415,7 @@ function validateDesignTokenFrontmatter(frontmatterText) {
5369
5415
  const [, refGroup, refKey] = ref;
5370
5416
  const resolves = REF_GROUPS.includes(refGroup) && groupIsMap(refGroup) && Object.prototype.hasOwnProperty.call(fm[refGroup], refKey);
5371
5417
  if (!resolves) {
5372
- violations.push(violation6("medium", "design-md.tokens.ref-unresolved", `component "${name}" references "${v}" which does not resolve to an active token in this frontmatter (design-md-spec §6 {path} refs MUST trace back to a key)`, `add the referenced token or use a literal value`));
5418
+ violations.push(violation6("medium", "design-md.tokens.ref-unresolved", `component "${name}" references "${v}" which does not resolve to an active token in this frontmatter (design-md-spec \u00a76 \u2014 {path} refs MUST trace back to a key)`, `add the referenced token or use a literal value`));
5373
5419
  }
5374
5420
  }
5375
5421
  }
@@ -5381,7 +5427,7 @@ function assertLightDarkParity(lightFm, darkFm) {
5381
5427
  const light = parseDesignFrontmatter(lightFm);
5382
5428
  const dark = parseDesignFrontmatter(darkFm);
5383
5429
  if (light === null || dark === null) {
5384
- violations.push(violation6("medium", "design-md.parity.missing-frontmatter", `light/dark parity needs a YAML frontmatter in both files ${light === null ? "DESIGN.md" : "DESIGN.dark.md"} has none (design-md-spec §4 rules 1–2)`, "add the fenced frontmatter to both theme files"));
5430
+ violations.push(violation6("medium", "design-md.parity.missing-frontmatter", `light/dark parity needs a YAML frontmatter in both files \u2014 ${light === null ? "DESIGN.md" : "DESIGN.dark.md"} has none (design-md-spec \u00a74 rules 1\u20132)`, "add the fenced frontmatter to both theme files"));
5385
5431
  return { ok: false, violations };
5386
5432
  }
5387
5433
  const activeKeys = (fm) => {
@@ -5399,12 +5445,12 @@ function assertLightDarkParity(lightFm, darkFm) {
5399
5445
  const darkKeys = activeKeys(dark);
5400
5446
  for (const key of lightKeys) {
5401
5447
  if (!darkKeys.has(key)) {
5402
- violations.push(violation6("medium", "design-md.parity.missing-dark", `token "${key}" is active in DESIGN.md but missing from DESIGN.dark.md both files must define the same token set (design-md-spec §4 rule 3)`, "add the token to DESIGN.dark.md with a dark-appropriate value"));
5448
+ violations.push(violation6("medium", "design-md.parity.missing-dark", `token "${key}" is active in DESIGN.md but missing from DESIGN.dark.md \u2014 both files must define the same token set (design-md-spec \u00a74 rule 3)`, "add the token to DESIGN.dark.md with a dark-appropriate value"));
5403
5449
  }
5404
5450
  }
5405
5451
  for (const key of darkKeys) {
5406
5452
  if (!lightKeys.has(key)) {
5407
- violations.push(violation6("medium", "design-md.parity.missing-light", `token "${key}" is active in DESIGN.dark.md but missing from DESIGN.md DESIGN.md is the SSOT for token names (design-md-spec §4 rules 3–4)`, "add the token to DESIGN.md, or remove it from the dark file"));
5453
+ violations.push(violation6("medium", "design-md.parity.missing-light", `token "${key}" is active in DESIGN.dark.md but missing from DESIGN.md \u2014 DESIGN.md is the SSOT for token names (design-md-spec \u00a74 rules 3\u20134)`, "add the token to DESIGN.md, or remove it from the dark file"));
5408
5454
  }
5409
5455
  }
5410
5456
  return { ok: violations.length === 0, violations };
@@ -5597,7 +5643,7 @@ function slugify(title) {
5597
5643
  return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
5598
5644
  }
5599
5645
  var escapeCell = (value) => value.replace(/\|/g, "\\|");
5600
- var truncate = (value, max) => value.length > max ? `${value.slice(0, max)}…` : value;
5646
+ var truncate = (value, max) => value.length > max ? `${value.slice(0, max)}\u2026` : value;
5601
5647
  function renderPlanFile(finding, plannedAt) {
5602
5648
  const sections = [
5603
5649
  `# ${finding.title}`,
@@ -5636,14 +5682,14 @@ function renderIndex(params) {
5636
5682
  const { date, repoName, repoShortSha, rows, rejected } = params;
5637
5683
  const findingsRows = rows.map((r) => `| ${r.num} | ${escapeCell(r.title)} | ${r.category} | ${escapeCell(truncate(r.impact, 80))} | ${r.effort} | ${r.risk} | ${r.confidence} | ${escapeCell(truncate(r.evidence, 80))} |`).join(`
5638
5684
  `);
5639
- const directionRows = rows.filter((r) => r.category === "direction").map((r) => `- ${escapeCell(r.title)} ${escapeCell(truncate(r.impact, 120))}`).join(`
5685
+ const directionRows = rows.filter((r) => r.category === "direction").map((r) => `- ${escapeCell(r.title)} \u2014 ${escapeCell(truncate(r.impact, 120))}`).join(`
5640
5686
  `);
5641
5687
  const executionRows = rows.map((r) => `| ${r.num} | ${escapeCell(r.title)} | ${r.priority} | ${r.effort} | ${r.dependsOn} | TODO |`).join(`
5642
5688
  `);
5643
5689
  const rejectedRows = rejected.map((r) => `- ${escapeCell(r.title)}: ${escapeCell(r.reason)}`).join(`
5644
5690
  `);
5645
5691
  const sections = [
5646
- `# Audit Report ${repoName} @ ${repoShortSha} (${date})`,
5692
+ `# Audit Report \u2014 ${repoName} @ ${repoShortSha} (${date})`,
5647
5693
  "",
5648
5694
  "## Findings",
5649
5695
  "",
@@ -5658,6 +5704,7 @@ function renderIndex(params) {
5658
5704
  if (rejectedRows !== "") {
5659
5705
  sections.push("", "## Findings considered and rejected", "", rejectedRows);
5660
5706
  }
5707
+ sections.push("", "## Red-team dispositions", "", "- <finding>: <survived / refuted / hallucination-dropped / uncovered-kept>, <one-line reason>");
5661
5708
  return `${sections.join(`
5662
5709
  `)}
5663
5710
  `;
@@ -5692,14 +5739,14 @@ function scaffoldAuditPlan(outDir, findings, options = {}) {
5692
5739
  return {
5693
5740
  num: file.slice(0, 3),
5694
5741
  title: summary.title,
5695
- category: fields.get("Category") ?? "",
5742
+ category: fields.get("Category") ?? "\u2014",
5696
5743
  impact: "see plan file",
5697
- effort: fields.get("Effort") ?? "",
5698
- risk: fields.get("Risk") ?? "",
5699
- confidence: "",
5700
- evidence: fields.get("Evidence") ?? "",
5701
- priority: fields.get("Priority") ?? "",
5702
- dependsOn: fields.get("Depends on") ?? ""
5744
+ effort: fields.get("Effort") ?? "\u2014",
5745
+ risk: fields.get("Risk") ?? "\u2014",
5746
+ confidence: "\u2014",
5747
+ evidence: fields.get("Evidence") ?? "\u2014",
5748
+ priority: fields.get("Priority") ?? "\u2014",
5749
+ dependsOn: fields.get("Depends on") ?? "\u2014"
5703
5750
  };
5704
5751
  });
5705
5752
  const byNum = new Map(rows.map((r) => [r.num, r]));
@@ -5893,7 +5940,7 @@ function validateSchemaYaml(frontmatterText) {
5893
5940
  const violations = [];
5894
5941
  const doc = parseYamlLite(frontmatterText);
5895
5942
  if (doc === null) {
5896
- violations.push(violation8("medium", "compound.schema.missing-frontmatter", "no `---` YAML frontmatter block found knowledge docs must open with the schema.yaml contract (mstar-compound/references/schema.yaml)", "add the fenced frontmatter with module, date, problem_type, category, severity"));
5943
+ violations.push(violation8("medium", "compound.schema.missing-frontmatter", "no `---` YAML frontmatter block found \u2014 knowledge docs must open with the schema.yaml contract (mstar-compound/references/schema.yaml)", "add the fenced frontmatter with module, date, problem_type, category, severity"));
5897
5944
  return { ok: false, violations };
5898
5945
  }
5899
5946
  const isStr = (v) => typeof v === "string";
@@ -5907,14 +5954,14 @@ function validateSchemaYaml(frontmatterText) {
5907
5954
  }
5908
5955
  const problemType = doc.problem_type;
5909
5956
  if (problemType !== undefined && !isStr(problemType)) {
5910
- violations.push(violation8("medium", "compound.schema.invalid-problem-type", `problem_type "${String(problemType)}" must be a string one of the schema.yaml enum values (bug: build_error…config_error; knowledge: developer_experience…testing_pattern)`, "pick the narrowest applicable problem_type from schema.yaml"));
5957
+ violations.push(violation8("medium", "compound.schema.invalid-problem-type", `problem_type "${String(problemType)}" must be a string \u2014 one of the schema.yaml enum values (bug: build_error\u2026config_error; knowledge: developer_experience\u2026testing_pattern)`, "pick the narrowest applicable problem_type from schema.yaml"));
5911
5958
  }
5912
5959
  const problemTypeValid = isStr(problemType) && KNOWLEDGE_PROBLEM_TYPES.includes(problemType);
5913
5960
  if (isStr(problemType) && !problemTypeValid) {
5914
- violations.push(violation8("medium", "compound.schema.invalid-problem-type", `problem_type "${problemType}" is not one of the schema.yaml enum values (bug: build_error…config_error; knowledge: developer_experience…testing_pattern)`, "pick the narrowest applicable problem_type from schema.yaml"));
5961
+ violations.push(violation8("medium", "compound.schema.invalid-problem-type", `problem_type "${problemType}" is not one of the schema.yaml enum values (bug: build_error\u2026config_error; knowledge: developer_experience\u2026testing_pattern)`, "pick the narrowest applicable problem_type from schema.yaml"));
5915
5962
  }
5916
5963
  if (doc.severity !== undefined && !isStr(doc.severity)) {
5917
- violations.push(violation8("medium", "compound.schema.invalid-severity", `severity "${String(doc.severity)}" must be a string critical | high | medium | low (schema.yaml required_fields.severity)`, "use one of the four severity values"));
5964
+ violations.push(violation8("medium", "compound.schema.invalid-severity", `severity "${String(doc.severity)}" must be a string \u2014 critical | high | medium | low (schema.yaml required_fields.severity)`, "use one of the four severity values"));
5918
5965
  }
5919
5966
  if (isStr(doc.severity) && !KNOWLEDGE_SEVERITIES.includes(doc.severity)) {
5920
5967
  violations.push(violation8("medium", "compound.schema.invalid-severity", `severity "${doc.severity}" must be critical | high | medium | low (schema.yaml required_fields.severity)`, "use one of the four severity values"));
@@ -5922,7 +5969,7 @@ function validateSchemaYaml(frontmatterText) {
5922
5969
  if (problemTypeValid && isStr(doc.category)) {
5923
5970
  const expected = KNOWLEDGE_CATEGORY_MAP[problemType];
5924
5971
  if (doc.category !== expected) {
5925
- violations.push(violation8("medium", "compound.schema.category-mismatch", `category "${doc.category}" does not match problem_type "${problemType}" category-mapping.md rule 1 maps it to "${expected}"`, `set \`category: ${expected}\` (the directory name under {KNOWLEDGE_DIR})`));
5972
+ violations.push(violation8("medium", "compound.schema.category-mismatch", `category "${doc.category}" does not match problem_type "${problemType}" \u2014 category-mapping.md rule 1 maps it to "${expected}"`, `set \`category: ${expected}\` (the directory name under {KNOWLEDGE_DIR})`));
5926
5973
  }
5927
5974
  }
5928
5975
  if (problemTypeValid) {
@@ -5953,7 +6000,7 @@ function validateSchemaYaml(frontmatterText) {
5953
6000
  if (!Array.isArray(doc.tags)) {
5954
6001
  violations.push(violation8("low", "compound.schema.invalid-tags", "optional `tags` must be a YAML list (schema.yaml optional_fields.tags)", "list lowercase, hyphen-separated keywords"));
5955
6002
  } else if (doc.tags.length > 8) {
5956
- violations.push(violation8("low", "compound.schema.tags-too-many", `tags has ${doc.tags.length} entries max 8 (schema.yaml optional_fields.tags.max_items)`, "trim the tag list to at most 8 keywords"));
6003
+ violations.push(violation8("low", "compound.schema.tags-too-many", `tags has ${doc.tags.length} entries \u2014 max 8 (schema.yaml optional_fields.tags.max_items)`, "trim the tag list to at most 8 keywords"));
5957
6004
  }
5958
6005
  }
5959
6006
  if (doc.last_updated !== undefined && (!isStr(doc.last_updated) || !DATE_RE3.test(doc.last_updated))) {
@@ -6001,7 +6048,7 @@ function assertIndexRows(knowledgeDir) {
6001
6048
  const violations = [];
6002
6049
  const readmePath = join8(knowledgeDir, "README.md");
6003
6050
  if (!existsSync5(readmePath)) {
6004
- violations.push(violation8("medium", "compound.index.missing-readme", `missing ${readmePath} the knowledge index is required (mstar-compound Phase 6: every doc gets a README.md row)`, "create knowledge/README.md with a Document / Source Plan / Description / Status table"));
6051
+ violations.push(violation8("medium", "compound.index.missing-readme", `missing ${readmePath} \u2014 the knowledge index is required (mstar-compound Phase 6: every doc gets a README.md row)`, "create knowledge/README.md with a Document / Source Plan / Description / Status table"));
6005
6052
  return { ok: false, violations };
6006
6053
  }
6007
6054
  const docs = collectKnowledgeDocs(knowledgeDir);
@@ -6040,7 +6087,7 @@ function scopeGuard(path2, allowedRoots) {
6040
6087
  return {
6041
6088
  ok: false,
6042
6089
  violations: [
6043
- violation8("medium", "compound.scope.outside", `path "${path2}" is outside the compound-refresh scope (allowed: ${allowedRoots.join(", ")}) compound-refresh operates only on {HARNESS_DIR}/knowledge/**, {HARNESS_DIR}/knowledge/README.md, <repo-root>/CONCEPTS.md, {HARNESS_DIR}/status.json (mstar-compound-refresh SKILL.md § 产物与操作路径)`, "point the operation at one of the allowed paths")
6090
+ violation8("medium", "compound.scope.outside", `path "${path2}" is outside the compound-refresh scope (allowed: ${allowedRoots.join(", ")}) \u2014 compound-refresh operates only on {HARNESS_DIR}/knowledge/**, {HARNESS_DIR}/knowledge/README.md, <repo-root>/CONCEPTS.md, {HARNESS_DIR}/status.json (mstar-compound-refresh SKILL.md \u00a7 \u4ea7\u7269\u4e0e\u64cd\u4f5c\u8def\u5f84)`, "point the operation at one of the allowed paths")
6044
6091
  ]
6045
6092
  };
6046
6093
  }
@@ -6087,16 +6134,36 @@ function findTemporaryMarkers(fileText) {
6087
6134
  }
6088
6135
  markers.push({ line: i + 1, text, removalPath });
6089
6136
  if (removalPath === null) {
6090
- violations.push(violation9("medium", "lint.temporary.no-removal-path", `temporary marker at line ${i + 1} records no removal path (plan/status artifact reference) record one before claiming the task complete (mstar-coding-behavior § Simplification markers)`, 'add a plan/status reference to the marker, e.g. "removal tracked in status.json" or "plan 20260808-slice2 removes this"'));
6137
+ violations.push(violation9("medium", "lint.temporary.no-removal-path", `temporary marker at line ${i + 1} records no removal path (plan/status artifact reference) \u2014 record one before claiming the task complete (mstar-coding-behavior \u00a7 Simplification markers)`, 'add a plan/status reference to the marker, e.g. "removal tracked in status.json" or "plan 20260808-slice2 removes this"'));
6091
6138
  }
6092
6139
  }
6093
6140
  return { ok: violations.length === 0, violations, markers };
6094
6141
  }
6142
+ var TASK_ARTIFACT_RE = /\btask-\d+(?:-(?:brief|report|fix-report|diff)|\.diff)\b/g;
6143
+ var SDD_DEEPLINK_RE = /\.(?:mstar|agents)\/sdd\/([^\s/<>{}\[\]"'\*\?]+)/g;
6144
+ function findEphemeralCitations(skillText) {
6145
+ const citations = [];
6146
+ const lines = skillText.split(/\r?\n/);
6147
+ for (let i = 0;i < lines.length; i++) {
6148
+ const found = [];
6149
+ for (const m of lines[i].matchAll(TASK_ARTIFACT_RE)) {
6150
+ found.push({ index: m.index, match: m[0], kind: "task-artifact" });
6151
+ }
6152
+ for (const m of lines[i].matchAll(SDD_DEEPLINK_RE)) {
6153
+ found.push({ index: m.index, match: m[0], kind: "sdd-deeplink" });
6154
+ }
6155
+ found.sort((a, b) => a.index - b.index);
6156
+ for (const f of found) {
6157
+ citations.push({ line: i + 1, match: f.match, kind: f.kind });
6158
+ }
6159
+ }
6160
+ return citations;
6161
+ }
6095
6162
  var TEST_FILE_PATH_RE = /[\w./-]+\.(?:test|spec)\.[a-z0-9]+/i;
6096
6163
  var TEST_FILE_PHRASE_RE = /\btest files?\b/i;
6097
6164
  var COMMAND_PROMPT_RE = /^\s*[$>]\s*\S/;
6098
6165
  var RUNNER_RE = /\b(?:bun|pnpm|npm|yarn|npx|bunx)\s+(?:test|run|exec)\b|\b(?:npx|bunx)\s+[\w./-]+\b|\b(?:tsc|vitest|jest|mocha|pytest)\b|\bgo\s+test\b|\bcargo\s+test\b/i;
6099
- var OUTPUT_TOKEN_RE = /[✓✔✗✘]|\b(?:PASS|FAIL)\b|\b\d+\s+(?:pass(?:es|ed)?|fail(?:s|ed|ing)?|skipped|tests?|ok)\b|\bok\s+\d+\b|\ball\s+ok\b|exit(?:ed)?\s+(?:with\s+)?(?:code\s+)?\d+/i;
6166
+ var OUTPUT_TOKEN_RE = /[\u2713\u2714\u2717\u2718]|\b(?:PASS|FAIL)\b|\b\d+\s+(?:pass(?:es|ed)?|fail(?:s|ed|ing)?|skipped|tests?|ok)\b|\bok\s+\d+\b|\ball\s+ok\b|exit(?:ed)?\s+(?:with\s+)?(?:code\s+)?\d+/i;
6100
6167
  function assertSddTddTriple(reportText) {
6101
6168
  const violations = [];
6102
6169
  const lines = reportText.split(/\r?\n/);
@@ -6114,13 +6181,13 @@ function assertSddTddTriple(reportText) {
6114
6181
  break;
6115
6182
  }
6116
6183
  if (!hasTests) {
6117
- violations.push(violation9("medium", "lint.sdd-tdd.missing-tests", "task report carries no test file reference the TDD triple needs covering test file(s) (mstar-coding-behavior § Integration Notes; mstar-sdd/references/file-handoffs.md)", 'add a "Covering test file(s): <path>.test.ts" line or a `.test.<ext>` path to the report'));
6184
+ violations.push(violation9("medium", "lint.sdd-tdd.missing-tests", "task report carries no test file reference \u2014 the TDD triple needs covering test file(s) (mstar-coding-behavior \u00a7 Integration Notes; mstar-sdd/references/file-handoffs.md)", 'add a "Covering test file(s): <path>.test.ts" line or a `.test.<ext>` path to the report'));
6118
6185
  }
6119
6186
  if (!hasCommand) {
6120
- violations.push(violation9("medium", "lint.sdd-tdd.missing-command", "task report carries no command the TDD triple needs the exact command run (mstar-coding-behavior § Integration Notes; mstar-sdd/references/file-handoffs.md)", 'add a "Command run: `bun test <file>`" line to the report'));
6187
+ violations.push(violation9("medium", "lint.sdd-tdd.missing-command", "task report carries no command \u2014 the TDD triple needs the exact command run (mstar-coding-behavior \u00a7 Integration Notes; mstar-sdd/references/file-handoffs.md)", 'add a "Command run: `bun test <file>`" line to the report'));
6121
6188
  }
6122
6189
  if (!hasOutput) {
6123
- violations.push(violation9("medium", "lint.sdd-tdd.missing-output", "task report carries no output evidence the TDD triple needs the run output (pass/fail counts or exit code) (mstar-coding-behavior § Integration Notes; mstar-sdd/references/file-handoffs.md)", 'paste the test-run output (e.g. "12 pass / 0 fail") into the report'));
6190
+ violations.push(violation9("medium", "lint.sdd-tdd.missing-output", "task report carries no output evidence \u2014 the TDD triple needs the run output (pass/fail counts or exit code) (mstar-coding-behavior \u00a7 Integration Notes; mstar-sdd/references/file-handoffs.md)", 'paste the test-run output (e.g. "12 pass / 0 fail") into the report'));
6124
6191
  }
6125
6192
  return { ok: violations.length === 0, violations };
6126
6193
  }
@@ -6173,18 +6240,18 @@ function lintSkillFrontmatter(frontmatterText) {
6173
6240
  const violations = [];
6174
6241
  const fm = parseFrontmatter(frontmatterText);
6175
6242
  if (fm === null) {
6176
- violations.push(violation9("medium", "lint.frontmatter.missing", "no YAML frontmatter block found a skill file must open with a `---` fenced frontmatter (mstar-skill-authoring § Frontmatter Contract)", "add a frontmatter block with `name` and `description` at the top of the file"));
6243
+ violations.push(violation9("medium", "lint.frontmatter.missing", "no YAML frontmatter block found \u2014 a skill file must open with a `---` fenced frontmatter (mstar-skill-authoring \u00a7 Frontmatter Contract)", "add a frontmatter block with `name` and `description` at the top of the file"));
6177
6244
  return { ok: false, violations };
6178
6245
  }
6179
6246
  const name = fm.name ?? "";
6180
6247
  if (name === "") {
6181
- violations.push(violation9("medium", "lint.frontmatter.name.missing", "frontmatter `name` is missing required (mstar-skill-authoring § Frontmatter Contract)", "add `name: <lowercase-hyphen-id>` to the frontmatter"));
6248
+ violations.push(violation9("medium", "lint.frontmatter.name.missing", "frontmatter `name` is missing \u2014 required (mstar-skill-authoring \u00a7 Frontmatter Contract)", "add `name: <lowercase-hyphen-id>` to the frontmatter"));
6182
6249
  } else if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
6183
- violations.push(violation9("medium", "lint.frontmatter.name.format", `frontmatter \`name\` must be lowercase-hyphen ("${name}") e.g. example-skill (mstar-skill-authoring § Frontmatter Contract)`, "rename to a stable lowercase-hyphen id, e.g. `name: example-skill`"));
6250
+ violations.push(violation9("medium", "lint.frontmatter.name.format", `frontmatter \`name\` must be lowercase-hyphen ("${name}") \u2014 e.g. example-skill (mstar-skill-authoring \u00a7 Frontmatter Contract)`, "rename to a stable lowercase-hyphen id, e.g. `name: example-skill`"));
6184
6251
  }
6185
6252
  const description = fm.description ?? "";
6186
6253
  if (description === "") {
6187
- violations.push(violation9("medium", "lint.frontmatter.description.missing", "frontmatter `description` is missing the trigger contract is required (mstar-skill-authoring § Frontmatter Contract)", "add a `description:` that states when the skill loads (symptoms, context, roles, exclusions)"));
6254
+ violations.push(violation9("medium", "lint.frontmatter.description.missing", "frontmatter `description` is missing \u2014 the trigger contract is required (mstar-skill-authoring \u00a7 Frontmatter Contract)", "add a `description:` that states when the skill loads (symptoms, context, roles, exclusions)"));
6188
6255
  } else {
6189
6256
  const stripped = description.replace(/`[^`]*`/g, " ").replace(/'[^']*'/g, " ").replace(/"[^"]*"/g, " ");
6190
6257
  let pronoun = null;
@@ -6195,15 +6262,15 @@ function lintSkillFrontmatter(frontmatterText) {
6195
6262
  break;
6196
6263
  }
6197
6264
  if (pronoun !== null) {
6198
- violations.push(violation9("low", "lint.frontmatter.description.person", `description uses first/second-person pronoun "${pronoun[0]}" keep the trigger contract third person (mstar-skill-authoring § Frontmatter Contract)`, 'rewrite without I/we/you/my/our/your/us, e.g. "Use when the user asks "'));
6265
+ violations.push(violation9("low", "lint.frontmatter.description.person", `description uses first/second-person pronoun "${pronoun[0]}" \u2014 keep the trigger contract third person (mstar-skill-authoring \u00a7 Frontmatter Contract)`, 'rewrite without I/we/you/my/our/your/us, e.g. "Use when the user asks \u2026"'));
6199
6266
  }
6200
6267
  const start = description.trim().replace(/^[*_#>]+/, "").replace(/^["'`]+/, "").trim();
6201
6268
  if (WORKFLOW_VERB_START_RE.test(start)) {
6202
- violations.push(violation9("low", "lint.frontmatter.description.workflow", 'description reads as a workflow summary ("Explains/Describes/Covers ") the description is the trigger contract, not a summary of steps (mstar-skill-authoring § Frontmatter Contract)', "describe when to load the skill (symptoms, context, roles, exclusions) instead of summarizing its steps"));
6269
+ violations.push(violation9("low", "lint.frontmatter.description.workflow", 'description reads as a workflow summary ("Explains/Describes/Covers \u2026") \u2014 the description is the trigger contract, not a summary of steps (mstar-skill-authoring \u00a7 Frontmatter Contract)', "describe when to load the skill (symptoms, context, roles, exclusions) instead of summarizing its steps"));
6203
6270
  } else {
6204
6271
  const words = description.trim().split(/\s+/).filter(Boolean).length;
6205
6272
  if (words > DESCRIPTION_MAX_WORDS) {
6206
- violations.push(violation9("low", "lint.frontmatter.description.workflow", `description is ${words} words paragraph-length summaries bury the trigger contract (threshold ${DESCRIPTION_MAX_WORDS}, above the longest corpus description at 114 words, mstar-design-md; mstar-skill-authoring § Frontmatter Contract)`, "trim the description to a scannable trigger contract and move detail into the body"));
6273
+ violations.push(violation9("low", "lint.frontmatter.description.workflow", `description is ${words} words \u2014 paragraph-length summaries bury the trigger contract (threshold ${DESCRIPTION_MAX_WORDS}, above the longest corpus description at 114 words, mstar-design-md; mstar-skill-authoring \u00a7 Frontmatter Contract)`, "trim the description to a scannable trigger contract and move detail into the body"));
6207
6274
  }
6208
6275
  }
6209
6276
  }
@@ -6250,7 +6317,7 @@ function lintStrategySections(docText) {
6250
6317
  }
6251
6318
  for (const required of REQUIRED_STRATEGY_SECTIONS) {
6252
6319
  if (!headings.has(required.toLowerCase())) {
6253
- violations.push(violation9("medium", "lint.strategy.missing-section", `missing required section "${required}" (mstar-strategy § STRATEGY.md structure)`, "add a `## <Section>` heading; required: Vision, What we build, What we don't build, Guiding Principles, Technology Direction, Decision Log"));
6320
+ violations.push(violation9("medium", "lint.strategy.missing-section", `missing required section "${required}" (mstar-strategy \u00a7 STRATEGY.md structure)`, "add a `## <Section>` heading; required: Vision, What we build, What we don't build, Guiding Principles, Technology Direction, Decision Log"));
6254
6321
  }
6255
6322
  }
6256
6323
  return { ok: violations.length === 0, violations };
@@ -6263,6 +6330,8 @@ function detectHost(signals2) {
6263
6330
  return "opencode";
6264
6331
  if (s.has("task_agent_batch") || s.has("ask") || s.has("hub"))
6265
6332
  return "omp";
6333
+ if (s.has("subagent"))
6334
+ return "dsh";
6266
6335
  if (s.has("AgentSwarm"))
6267
6336
  return "kimi";
6268
6337
  if (s.has("Agent") || s.has("AskUserQuestion") || s.has("EnterPlanMode") || s.has("TodoWrite"))
@@ -6289,7 +6358,7 @@ function lintFiveQuestion(bodyText) {
6289
6358
  const label = section.label.toLowerCase();
6290
6359
  const covered = headings.some((heading) => heading.includes(label));
6291
6360
  if (!covered) {
6292
- violations.push(violation11("low", `skill-authoring.five-question.${section.key}`, `body does not answer "${section.question}" no "${section.label}" section (mstar-skill-authoring § Body 必须回答的 5 / § 默认 Body 结构)`, `add a "## ${section.label}" section covering ${section.question}`));
6361
+ violations.push(violation11("low", `skill-authoring.five-question.${section.key}`, `body does not answer "${section.question}" \u2014 no "${section.label}" section (mstar-skill-authoring \u00a7 Body \u5fc5\u987b\u56de\u7b54\u7684 5 \u95ee / \u00a7 \u9ed8\u8ba4 Body \u7ed3\u6784)`, `add a "## ${section.label}" section covering ${section.question}`));
6293
6362
  }
6294
6363
  }
6295
6364
  return { ok: violations.length === 0, violations };
@@ -6386,7 +6455,7 @@ var GITIGNORE_SNIPPET2 = `# Morning Star harness (.mstar/)
6386
6455
  !.mstar/specs/
6387
6456
  !.mstar/specs/**
6388
6457
  `;
6389
- var GITIGNORE_SNIPPET_AGENTS2 = `# Morning Star harness (.agents/) legacy
6458
+ var GITIGNORE_SNIPPET_AGENTS2 = `# Morning Star harness (.agents/) \u2014 legacy
6390
6459
  # Default-ignore everything under .agents/, then re-include the tracked results.
6391
6460
  .agents/**
6392
6461
  !.agents/AGENTS.md
@@ -6424,7 +6493,7 @@ function validateExecutionLease(lease) {
6424
6493
  return {
6425
6494
  ok: false,
6426
6495
  violations: [
6427
- violation("high", "lease.execution-lease.invalid", "execution_lease must be an object null and tombstone objects are invalid; writers delete the key on release")
6496
+ violation("high", "lease.execution-lease.invalid", "execution_lease must be an object \u2014 null and tombstone objects are invalid; writers delete the key on release")
6428
6497
  ]
6429
6498
  };
6430
6499
  }
@@ -6439,11 +6508,11 @@ function validateExecutionLease(lease) {
6439
6508
  } else if (typeof lease.worktree_path !== "string" || lease.worktree_path.trim() === "") {
6440
6509
  violations.push(violation("medium", "lease.execution-lease.invalid-worktree-path", "worktree_path must be a non-empty string"));
6441
6510
  } else if (!isAbsolute22(lease.worktree_path)) {
6442
- violations.push(violation("medium", "lease.execution-lease.invalid-worktree-path", "worktree_path must be an absolute path it identifies the dedicated feature-worktree root (and MUST differ from metadata.control_worktree_path)"));
6511
+ violations.push(violation("medium", "lease.execution-lease.invalid-worktree-path", "worktree_path must be an absolute path \u2014 it identifies the dedicated feature-worktree root (and MUST differ from metadata.control_worktree_path)"));
6443
6512
  }
6444
6513
  validateNonEmptyString(violations, lease.working_branch, "working_branch", "lease.execution-lease.missing-working-branch", "lease.execution-lease.invalid-working-branch");
6445
6514
  if (lease.session_label !== undefined && typeof lease.session_label !== "string") {
6446
- violations.push(violation("medium", "lease.execution-lease.invalid-session-label", "session_label must be a string (display only never used for ownership comparison)"));
6515
+ violations.push(violation("medium", "lease.execution-lease.invalid-session-label", "session_label must be a string (display only \u2014 never used for ownership comparison)"));
6447
6516
  }
6448
6517
  return { ok: violations.length === 0, violations };
6449
6518
  }
@@ -6460,7 +6529,7 @@ function verifyPlanExecutionLease(row, planId) {
6460
6529
  return {
6461
6530
  ok: false,
6462
6531
  violations: [
6463
- violation("high", "lease.verify.orphan", "plan is InProgress without an execution_lease orphan: STOP, no writable dispatch until recovery (status-and-residuals.md § Orphan recovery)")
6532
+ violation("high", "lease.verify.orphan", "plan is InProgress without an execution_lease \u2014 orphan: STOP, no writable dispatch until recovery (status-and-residuals.md \u00a7 Orphan recovery)")
6464
6533
  ]
6465
6534
  };
6466
6535
  }
@@ -6473,9 +6542,9 @@ function verifyPlanExecutionLease(row, planId) {
6473
6542
  }
6474
6543
  const violations = [];
6475
6544
  if (rowLease !== undefined && metadataLease !== undefined) {
6476
- violations.push(violation("high", "lease.verify.dual-write", "execution_lease present in BOTH plans[].execution_lease (SSOT) and plans[].metadata.execution_lease the row-level lease wins; delete the metadata copy to remove the dual write"));
6545
+ violations.push(violation("high", "lease.verify.dual-write", "execution_lease present in BOTH plans[].execution_lease (SSOT) and plans[].metadata.execution_lease \u2014 the row-level lease wins; delete the metadata copy to remove the dual write"));
6477
6546
  } else if (rowLease === undefined) {
6478
- violations.push(violation("high", "lease.verify.non-ssot-location", "execution_lease found only under plans[].metadata.execution_lease the SSOT location is plans[].execution_lease; the metadata location is a legacy/hand-written read-compat fallback, not equivalent to SSOT success (migrate the lease to the plan row)"));
6547
+ violations.push(violation("high", "lease.verify.non-ssot-location", "execution_lease found only under plans[].metadata.execution_lease \u2014 the SSOT location is plans[].execution_lease; the metadata location is a legacy/hand-written read-compat fallback, not equivalent to SSOT success (migrate the lease to the plan row)"));
6479
6548
  }
6480
6549
  violations.push(...validateExecutionLease(lease).violations);
6481
6550
  return { ok: violations.length === 0, violations, lease };
@@ -6653,11 +6722,11 @@ function validateManifest(manifest, errors2, warnings) {
6653
6722
  }
6654
6723
  if (doc.extensions !== undefined) {
6655
6724
  if (!isPlainObject6(doc.extensions)) {
6656
- warnings.push('plugin.json: "extensions" is not an object ignored');
6725
+ warnings.push('plugin.json: "extensions" is not an object \u2014 ignored');
6657
6726
  } else {
6658
6727
  for (const [namespace, value] of Object.entries(doc.extensions)) {
6659
6728
  if (!isPlainObject6(value)) {
6660
- warnings.push(`plugin.json: "extensions.${namespace}" is not an object ignored`);
6729
+ warnings.push(`plugin.json: "extensions.${namespace}" is not an object \u2014 ignored`);
6661
6730
  }
6662
6731
  }
6663
6732
  }
@@ -6718,7 +6787,7 @@ function validateMcpServer(name, entry, errors2) {
6718
6787
  } else {
6719
6788
  const remainder = stripMcpPathPrefix(server.cwd);
6720
6789
  if (remainder === null) {
6721
- errors2.push(`${prefix}: "cwd" must be "./…", "${"${PLUGIN_ROOT}"}", or "${"${PLUGIN_DATA}"}"`);
6790
+ errors2.push(`${prefix}: "cwd" must be "./\u2026", "${"${PLUGIN_ROOT}"}\u2026", or "${"${PLUGIN_DATA}"}\u2026"`);
6722
6791
  } else if (escapesPluginRoot(remainder)) {
6723
6792
  errors2.push(`${prefix}: "cwd" must remain within the plugin root (got "${server.cwd}")`);
6724
6793
  }
@@ -6947,7 +7016,7 @@ import path5 from "node:path";
6947
7016
  import fs2 from "node:fs";
6948
7017
  import os from "node:os";
6949
7018
  import path4 from "node:path";
6950
- import { execFileSync as execFileSync3 } from "node:child_process";
7019
+ import { execFileSync as execFileSync4 } from "node:child_process";
6951
7020
  var REPO_URL = "https://github.com/btspoony/mstar-harness.git";
6952
7021
  var PLUGIN_NAME = "morning-star-harness";
6953
7022
  var HARNESS_REPO_PATH = path4.join(os.homedir(), ".mstar", "harness");
@@ -6981,7 +7050,7 @@ function ensureDir(dirPath, dryRun) {
6981
7050
  function runCommand(command, cwd, dryRun) {
6982
7051
  if (dryRun)
6983
7052
  return;
6984
- execFileSync3(command[0], command.slice(1), { cwd, stdio: "pipe", encoding: "utf8" });
7053
+ execFileSync4(command[0], command.slice(1), { cwd, stdio: "pipe", encoding: "utf8" });
6985
7054
  }
6986
7055
  function ensureLocalHarnessRepo(dryRun) {
6987
7056
  const notes = [];
@@ -7293,7 +7362,7 @@ function ensureIterationSkillLinks(dryRun) {
7293
7362
  }
7294
7363
  const gitignoreEntries = CODEX_PROJECT_COMMAND_NAMES.map(iterationSkillGitignoreEntry);
7295
7364
  notes.push(...appendGitignore(projectRoot, gitignoreEntries, dryRun));
7296
- notes.push("Installed Codex project-scoped command skills under .agents/skills/ (iteration-start, iteration-drive, iteration-loop, codebase-audit) symlinked to harness commands/*.md.");
7365
+ notes.push("Installed Codex project-scoped command skills under .agents/skills/ (iteration-start, iteration-drive, iteration-loop, codebase-audit) \u2014 symlinked to harness commands/*.md.");
7297
7366
  return notes;
7298
7367
  }
7299
7368
  function validateIterationSkillLinks() {
@@ -7467,7 +7536,7 @@ var cursorAdapter = {
7467
7536
  // src/adapters/omp.ts
7468
7537
  import fs5 from "node:fs";
7469
7538
  import path7 from "node:path";
7470
- import { execFileSync as execFileSync5 } from "node:child_process";
7539
+ import { execFileSync as execFileSync6 } from "node:child_process";
7471
7540
  var OMP_PLUGIN_MARKER = ".omp-plugin/plugin.json";
7472
7541
  var CLAUDE_PLUGIN_MARKER = ".claude-plugin/plugin.json";
7473
7542
  var PACKAGE_NAMES = new Set(["morning-star", PLUGIN_NAME, "github:btspoony/mstar-harness"]);
@@ -7475,7 +7544,7 @@ var SKILL_SMOKE = ["mstar-host", "mstar-harness-core", "pm"];
7475
7544
  var COMMAND_SMOKE = ["iteration-start", "iteration-drive", "iteration-loop", "codebase-audit"];
7476
7545
  function ompAvailable() {
7477
7546
  try {
7478
- execFileSync5("omp", ["--version"], { stdio: "pipe", encoding: "utf8" });
7547
+ execFileSync6("omp", ["--version"], { stdio: "pipe", encoding: "utf8" });
7479
7548
  return true;
7480
7549
  } catch {
7481
7550
  return false;
@@ -7484,11 +7553,11 @@ function ompAvailable() {
7484
7553
  function runOmp(args, dryRun) {
7485
7554
  if (dryRun)
7486
7555
  return;
7487
- execFileSync5("omp", args, { stdio: "pipe", encoding: "utf8" });
7556
+ execFileSync6("omp", args, { stdio: "pipe", encoding: "utf8" });
7488
7557
  }
7489
7558
  function listInstalledPlugins() {
7490
7559
  try {
7491
- const raw = execFileSync5("omp", ["plugin", "list", "--json"], {
7560
+ const raw = execFileSync6("omp", ["plugin", "list", "--json"], {
7492
7561
  stdio: "pipe",
7493
7562
  encoding: "utf8"
7494
7563
  });
@@ -7564,7 +7633,7 @@ function runInit2(scope, dryRun) {
7564
7633
  notes.push(`Would update local harness repo: git -C ${HARNESS_REPO_PATH} pull --ff-only`);
7565
7634
  } else {
7566
7635
  try {
7567
- execFileSync5("git", ["-C", HARNESS_REPO_PATH, "pull", "--ff-only"], {
7636
+ execFileSync6("git", ["-C", HARNESS_REPO_PATH, "pull", "--ff-only"], {
7568
7637
  stdio: "pipe",
7569
7638
  encoding: "utf8"
7570
7639
  });
@@ -7609,7 +7678,7 @@ function runInit2(scope, dryRun) {
7609
7678
  }
7610
7679
  notes.push("Verify with: omp plugin list");
7611
7680
  notes.push("Enter PM with /skill:pm ; commands: /iteration-start /iteration-drive /iteration-loop /codebase-audit");
7612
- notes.push(`Host adapter: mstar-host references/omp.md (skill://mstar-host/references/omp.md)`);
7681
+ notes.push(`Host adapter: mstar-host \u2192 references/omp.md (skill://mstar-host/references/omp.md)`);
7613
7682
  notes.push(`Alternate install without CLI link: omp plugin install ${REPO_URL.replace("https://github.com/", "github:").replace(/\.git$/, "")}`);
7614
7683
  return {
7615
7684
  location: HARNESS_REPO_PATH,
@@ -7734,10 +7803,10 @@ function getDoctorWarnings(config) {
7734
7803
  const hasNpm = strings.some(isMstarHarnessOpencodePlugin);
7735
7804
  const hasLegacy = strings.some(isLegacyMorningStarGitPlugin);
7736
7805
  if (hasLegacy && !hasNpm) {
7737
- warnings.push("Plugin list uses legacy `morning-star@git+…` for this harness; run `mstar-harness init --target opencode` to rewrite to `@mstar-harness/opencode@latest`.");
7806
+ warnings.push("Plugin list uses legacy `morning-star@git+\u2026` for this harness; run `mstar-harness init --target opencode` to rewrite to `@mstar-harness/opencode@latest`.");
7738
7807
  }
7739
7808
  if (hasLegacy && hasNpm) {
7740
- warnings.push("Both legacy `morning-star@git+…` and `@mstar-harness/opencode` appear in `plugin`; run `init` again to dedupe and keep a single npm plugin line.");
7809
+ warnings.push("Both legacy `morning-star@git+\u2026` and `@mstar-harness/opencode` appear in `plugin`; run `init` again to dedupe and keep a single npm plugin line.");
7741
7810
  }
7742
7811
  const agent = ensureObject(config.agent);
7743
7812
  const missingModels = ALL_ROLES.filter((roleId) => {
@@ -7745,7 +7814,7 @@ function getDoctorWarnings(config) {
7745
7814
  return typeof role.model !== "string" || !role.model.trim();
7746
7815
  });
7747
7816
  if (missingModels.length) {
7748
- warnings.push(`${missingModels.length} role(s) have no explicit agent.<role>.model OpenCode default model will be used (recommended for fastest setup).`);
7817
+ warnings.push(`${missingModels.length} role(s) have no explicit agent.<role>.model \u2014 OpenCode default model will be used (recommended for fastest setup).`);
7749
7818
  }
7750
7819
  return warnings;
7751
7820
  }
@@ -7933,7 +8002,7 @@ function runInit3(scope, dryRun) {
7933
8002
  if (!dryRun)
7934
8003
  writeJson2(KNOWN_MARKETPLACES_PATH, knownNext);
7935
8004
  notes.push(`Registered ${MARKETPLACE_ID} marketplace in ${KNOWN_MARKETPLACES_PATH}`);
7936
- notes.push(`Then in ZCode: Settings Plugin Management Discover install ${PLUGIN_NAME} from the ${MARKETPLACE_ID} marketplace.`);
8005
+ notes.push(`Then in ZCode: Settings \u2192 Plugin Management \u2192 Discover \u2192 install ${PLUGIN_NAME} from the ${MARKETPLACE_ID} marketplace.`);
7937
8006
  return {
7938
8007
  location: KNOWN_MARKETPLACES_PATH,
7939
8008
  notes
@@ -8184,7 +8253,7 @@ pathCommand.command("resolve").description("Resolve {HARNESS_DIR} + {SPECS_DIR}
8184
8253
  const startDir = pathArg ? path11.resolve(pathArg) : process.cwd();
8185
8254
  const harnessDir = resolveHarnessDir(startDir);
8186
8255
  if (!harnessDir) {
8187
- const guidance = "no harness dir found (probed .mstar/, .agents/, .plans/, plans/ walking up from " + `${startDir}) \u2014 run \`mstar init\` to bootstrap, or pass a start dir inside a harness-enabled project`;
8256
+ const guidance = "no harness dir found \u2014 the bounded probe (.mstar/, .agents/, .plans/, plans/) walked up from " + `${startDir} only within the workspace root (git top-level of the start dir; non-git start probes only itself) \u2014 run \`mstar init\` to bootstrap, or pass a start dir inside a harness-enabled project`;
8188
8257
  if (options.json) {
8189
8258
  console.log(JSON.stringify({ ok: false, startDir, harnessDir: null, specsDir: null, guidance }));
8190
8259
  } else {
@@ -8792,7 +8861,7 @@ function resolveAuditShortSha(cwd, override) {
8792
8861
  if (override !== undefined && override !== "")
8793
8862
  return override;
8794
8863
  try {
8795
- const out = execFileSync6("git", ["rev-parse", "--short", "HEAD"], {
8864
+ const out = execFileSync7("git", ["rev-parse", "--short", "HEAD"], {
8796
8865
  cwd,
8797
8866
  encoding: "utf8",
8798
8867
  stdio: ["ignore", "pipe", "ignore"]
@@ -8902,7 +8971,7 @@ hostCommand.command("detect").description("Detect the active host from --signals
8902
8971
  }
8903
8972
  });
8904
8973
  var skillCommand = program2.command("skill").description("skill-authoring lints (engine-backed)");
8905
- skillCommand.command("lint").description("Lint <skill-dir>/SKILL.md: frontmatter contract (name lowercase-hyphen, description trigger contract) " + "+ the five-question body (exit 1 on violations, 2 on usage)").argument("[skill-dir]", "Skill directory containing SKILL.md").action((skillDir) => {
8974
+ skillCommand.command("lint").description("Lint <skill-dir>/SKILL.md: frontmatter contract (name lowercase-hyphen, description trigger contract) " + "+ the five-question body + ephemeral-citation scan (task-<digits>-* artifacts and .mstar/sdd/\u2026 deeplinks \u2014 " + "exit 1 on violations, 2 on usage)").argument("[skill-dir]", "Skill directory containing SKILL.md").action((skillDir) => {
8906
8975
  try {
8907
8976
  if (!skillDir)
8908
8977
  throw new SddScriptError("usage: skill lint <skill-dir>", 2);
@@ -8917,6 +8986,19 @@ skillCommand.command("lint").description("Lint <skill-dir>/SKILL.md: frontmatter
8917
8986
  const fiveQuestion = lintFiveQuestion(stripFrontmatter(text));
8918
8987
  printChecklist("skill lint (five questions)", fiveQuestion);
8919
8988
  violations.push(...fiveQuestion.violations);
8989
+ const ephemeral = findEphemeralCitations(text);
8990
+ const ephemeralGate = {
8991
+ ok: ephemeral.length === 0,
8992
+ violations: ephemeral.map((citation) => ({
8993
+ ok: false,
8994
+ severity: "medium",
8995
+ code: `skill.ephemeral.${citation.kind}`,
8996
+ message: `ephemeral ${citation.kind} citation at line ${citation.line}: "${citation.match}" \u2014 task artifacts and SDD deeplinks survive nothing; durable skill text cites in-repo artifacts only (knowledge conventions/skill-content-porting-discipline.md \xA73)`,
8997
+ fix: `rewrite "${citation.match}" as a placeholder form (e.g. task-N-report, <plan-id>, {SDD_DIR}/task-N-report.md) or cite a stable in-repo artifact instead`
8998
+ }))
8999
+ };
9000
+ printChecklist("skill lint (ephemeral citations)", ephemeralGate);
9001
+ violations.push(...ephemeralGate.violations);
8920
9002
  if (violations.length > 0)
8921
9003
  process.exitCode = 1;
8922
9004
  } catch (error) {