@mstar-harness/cli 2.2.0 → 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.
- package/dist/mstar-harness.js +405 -371
- package/package.json +4 -4
package/dist/mstar-harness.js
CHANGED
|
@@ -2546,267 +2546,267 @@ function isUnicodeSupported() {
|
|
|
2546
2546
|
var common = {
|
|
2547
2547
|
circleQuestionMark: "(?)",
|
|
2548
2548
|
questionMarkPrefix: "(?)",
|
|
2549
|
-
square: "
|
|
2550
|
-
squareDarkShade: "
|
|
2551
|
-
squareMediumShade: "
|
|
2552
|
-
squareLightShade: "
|
|
2553
|
-
squareTop: "
|
|
2554
|
-
squareBottom: "
|
|
2555
|
-
squareLeft: "
|
|
2556
|
-
squareRight: "
|
|
2557
|
-
squareCenter: "
|
|
2558
|
-
bullet: "
|
|
2559
|
-
dot: "
|
|
2560
|
-
ellipsis: "
|
|
2561
|
-
pointerSmall: "
|
|
2562
|
-
triangleUp: "
|
|
2563
|
-
triangleUpSmall: "
|
|
2564
|
-
triangleDown: "
|
|
2565
|
-
triangleDownSmall: "
|
|
2566
|
-
triangleLeftSmall: "
|
|
2567
|
-
triangleRightSmall: "
|
|
2568
|
-
home: "
|
|
2569
|
-
heart: "
|
|
2570
|
-
musicNote: "
|
|
2571
|
-
musicNoteBeamed: "
|
|
2572
|
-
arrowUp: "
|
|
2573
|
-
arrowDown: "
|
|
2574
|
-
arrowLeft: "
|
|
2575
|
-
arrowRight: "
|
|
2576
|
-
arrowLeftRight: "
|
|
2577
|
-
arrowUpDown: "
|
|
2578
|
-
almostEqual: "
|
|
2579
|
-
notEqual: "
|
|
2580
|
-
lessOrEqual: "
|
|
2581
|
-
greaterOrEqual: "
|
|
2582
|
-
identical: "
|
|
2583
|
-
infinity: "
|
|
2584
|
-
subscriptZero: "
|
|
2585
|
-
subscriptOne: "
|
|
2586
|
-
subscriptTwo: "
|
|
2587
|
-
subscriptThree: "
|
|
2588
|
-
subscriptFour: "
|
|
2589
|
-
subscriptFive: "
|
|
2590
|
-
subscriptSix: "
|
|
2591
|
-
subscriptSeven: "
|
|
2592
|
-
subscriptEight: "
|
|
2593
|
-
subscriptNine: "
|
|
2594
|
-
oneHalf: "
|
|
2595
|
-
oneThird: "
|
|
2596
|
-
oneQuarter: "
|
|
2597
|
-
oneFifth: "
|
|
2598
|
-
oneSixth: "
|
|
2599
|
-
oneEighth: "
|
|
2600
|
-
twoThirds: "
|
|
2601
|
-
twoFifths: "
|
|
2602
|
-
threeQuarters: "
|
|
2603
|
-
threeFifths: "
|
|
2604
|
-
threeEighths: "
|
|
2605
|
-
fourFifths: "
|
|
2606
|
-
fiveSixths: "
|
|
2607
|
-
fiveEighths: "
|
|
2608
|
-
sevenEighths: "
|
|
2609
|
-
line: "
|
|
2610
|
-
lineBold: "
|
|
2611
|
-
lineDouble: "
|
|
2612
|
-
lineDashed0: "
|
|
2613
|
-
lineDashed1: "
|
|
2614
|
-
lineDashed2: "
|
|
2615
|
-
lineDashed3: "
|
|
2616
|
-
lineDashed4: "
|
|
2617
|
-
lineDashed5: "
|
|
2618
|
-
lineDashed6: "
|
|
2619
|
-
lineDashed7: "
|
|
2620
|
-
lineDashed8: "
|
|
2621
|
-
lineDashed9: "
|
|
2622
|
-
lineDashed10: "
|
|
2623
|
-
lineDashed11: "
|
|
2624
|
-
lineDashed12: "
|
|
2625
|
-
lineDashed13: "
|
|
2626
|
-
lineDashed14: "
|
|
2627
|
-
lineDashed15: "
|
|
2628
|
-
lineVertical: "
|
|
2629
|
-
lineVerticalBold: "
|
|
2630
|
-
lineVerticalDouble: "
|
|
2631
|
-
lineVerticalDashed0: "
|
|
2632
|
-
lineVerticalDashed1: "
|
|
2633
|
-
lineVerticalDashed2: "
|
|
2634
|
-
lineVerticalDashed3: "
|
|
2635
|
-
lineVerticalDashed4: "
|
|
2636
|
-
lineVerticalDashed5: "
|
|
2637
|
-
lineVerticalDashed6: "
|
|
2638
|
-
lineVerticalDashed7: "
|
|
2639
|
-
lineVerticalDashed8: "
|
|
2640
|
-
lineVerticalDashed9: "
|
|
2641
|
-
lineVerticalDashed10: "
|
|
2642
|
-
lineVerticalDashed11: "
|
|
2643
|
-
lineDownLeft: "
|
|
2644
|
-
lineDownLeftArc: "
|
|
2645
|
-
lineDownBoldLeftBold: "
|
|
2646
|
-
lineDownBoldLeft: "
|
|
2647
|
-
lineDownLeftBold: "
|
|
2648
|
-
lineDownDoubleLeftDouble: "
|
|
2649
|
-
lineDownDoubleLeft: "
|
|
2650
|
-
lineDownLeftDouble: "
|
|
2651
|
-
lineDownRight: "
|
|
2652
|
-
lineDownRightArc: "
|
|
2653
|
-
lineDownBoldRightBold: "
|
|
2654
|
-
lineDownBoldRight: "
|
|
2655
|
-
lineDownRightBold: "
|
|
2656
|
-
lineDownDoubleRightDouble: "
|
|
2657
|
-
lineDownDoubleRight: "
|
|
2658
|
-
lineDownRightDouble: "
|
|
2659
|
-
lineUpLeft: "
|
|
2660
|
-
lineUpLeftArc: "
|
|
2661
|
-
lineUpBoldLeftBold: "
|
|
2662
|
-
lineUpBoldLeft: "
|
|
2663
|
-
lineUpLeftBold: "
|
|
2664
|
-
lineUpDoubleLeftDouble: "
|
|
2665
|
-
lineUpDoubleLeft: "
|
|
2666
|
-
lineUpLeftDouble: "
|
|
2667
|
-
lineUpRight: "
|
|
2668
|
-
lineUpRightArc: "
|
|
2669
|
-
lineUpBoldRightBold: "
|
|
2670
|
-
lineUpBoldRight: "
|
|
2671
|
-
lineUpRightBold: "
|
|
2672
|
-
lineUpDoubleRightDouble: "
|
|
2673
|
-
lineUpDoubleRight: "
|
|
2674
|
-
lineUpRightDouble: "
|
|
2675
|
-
lineUpDownLeft: "
|
|
2676
|
-
lineUpBoldDownBoldLeftBold: "
|
|
2677
|
-
lineUpBoldDownBoldLeft: "
|
|
2678
|
-
lineUpDownLeftBold: "
|
|
2679
|
-
lineUpBoldDownLeftBold: "
|
|
2680
|
-
lineUpDownBoldLeftBold: "
|
|
2681
|
-
lineUpDownBoldLeft: "
|
|
2682
|
-
lineUpBoldDownLeft: "
|
|
2683
|
-
lineUpDoubleDownDoubleLeftDouble: "
|
|
2684
|
-
lineUpDoubleDownDoubleLeft: "
|
|
2685
|
-
lineUpDownLeftDouble: "
|
|
2686
|
-
lineUpDownRight: "
|
|
2687
|
-
lineUpBoldDownBoldRightBold: "
|
|
2688
|
-
lineUpBoldDownBoldRight: "
|
|
2689
|
-
lineUpDownRightBold: "
|
|
2690
|
-
lineUpBoldDownRightBold: "
|
|
2691
|
-
lineUpDownBoldRightBold: "
|
|
2692
|
-
lineUpDownBoldRight: "
|
|
2693
|
-
lineUpBoldDownRight: "
|
|
2694
|
-
lineUpDoubleDownDoubleRightDouble: "
|
|
2695
|
-
lineUpDoubleDownDoubleRight: "
|
|
2696
|
-
lineUpDownRightDouble: "
|
|
2697
|
-
lineDownLeftRight: "
|
|
2698
|
-
lineDownBoldLeftBoldRightBold: "
|
|
2699
|
-
lineDownLeftBoldRightBold: "
|
|
2700
|
-
lineDownBoldLeftRight: "
|
|
2701
|
-
lineDownBoldLeftBoldRight: "
|
|
2702
|
-
lineDownBoldLeftRightBold: "
|
|
2703
|
-
lineDownLeftRightBold: "
|
|
2704
|
-
lineDownLeftBoldRight: "
|
|
2705
|
-
lineDownDoubleLeftDoubleRightDouble: "
|
|
2706
|
-
lineDownDoubleLeftRight: "
|
|
2707
|
-
lineDownLeftDoubleRightDouble: "
|
|
2708
|
-
lineUpLeftRight: "
|
|
2709
|
-
lineUpBoldLeftBoldRightBold: "
|
|
2710
|
-
lineUpLeftBoldRightBold: "
|
|
2711
|
-
lineUpBoldLeftRight: "
|
|
2712
|
-
lineUpBoldLeftBoldRight: "
|
|
2713
|
-
lineUpBoldLeftRightBold: "
|
|
2714
|
-
lineUpLeftRightBold: "
|
|
2715
|
-
lineUpLeftBoldRight: "
|
|
2716
|
-
lineUpDoubleLeftDoubleRightDouble: "
|
|
2717
|
-
lineUpDoubleLeftRight: "
|
|
2718
|
-
lineUpLeftDoubleRightDouble: "
|
|
2719
|
-
lineUpDownLeftRight: "
|
|
2720
|
-
lineUpBoldDownBoldLeftBoldRightBold: "
|
|
2721
|
-
lineUpDownBoldLeftBoldRightBold: "
|
|
2722
|
-
lineUpBoldDownLeftBoldRightBold: "
|
|
2723
|
-
lineUpBoldDownBoldLeftRightBold: "
|
|
2724
|
-
lineUpBoldDownBoldLeftBoldRight: "
|
|
2725
|
-
lineUpBoldDownLeftRight: "
|
|
2726
|
-
lineUpDownBoldLeftRight: "
|
|
2727
|
-
lineUpDownLeftBoldRight: "
|
|
2728
|
-
lineUpDownLeftRightBold: "
|
|
2729
|
-
lineUpBoldDownBoldLeftRight: "
|
|
2730
|
-
lineUpDownLeftBoldRightBold: "
|
|
2731
|
-
lineUpBoldDownLeftBoldRight: "
|
|
2732
|
-
lineUpBoldDownLeftRightBold: "
|
|
2733
|
-
lineUpDownBoldLeftBoldRight: "
|
|
2734
|
-
lineUpDownBoldLeftRightBold: "
|
|
2735
|
-
lineUpDoubleDownDoubleLeftDoubleRightDouble: "
|
|
2736
|
-
lineUpDoubleDownDoubleLeftRight: "
|
|
2737
|
-
lineUpDownLeftDoubleRightDouble: "
|
|
2738
|
-
lineCross: "
|
|
2739
|
-
lineBackslash: "
|
|
2740
|
-
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"
|
|
2741
2741
|
};
|
|
2742
2742
|
var specialMainSymbols = {
|
|
2743
|
-
tick: "
|
|
2744
|
-
info: "
|
|
2745
|
-
warning: "
|
|
2746
|
-
cross: "
|
|
2747
|
-
squareSmall: "
|
|
2748
|
-
squareSmallFilled: "
|
|
2749
|
-
circle: "
|
|
2750
|
-
circleFilled: "
|
|
2751
|
-
circleDotted: "
|
|
2752
|
-
circleDouble: "
|
|
2753
|
-
circleCircle: "
|
|
2754
|
-
circleCross: "
|
|
2755
|
-
circlePipe: "
|
|
2756
|
-
radioOn: "
|
|
2757
|
-
radioOff: "
|
|
2758
|
-
checkboxOn: "
|
|
2759
|
-
checkboxOff: "
|
|
2760
|
-
checkboxCircleOn: "
|
|
2761
|
-
checkboxCircleOff: "
|
|
2762
|
-
pointer: "
|
|
2763
|
-
triangleUpOutline: "
|
|
2764
|
-
triangleLeft: "
|
|
2765
|
-
triangleRight: "
|
|
2766
|
-
lozenge: "
|
|
2767
|
-
lozengeOutline: "
|
|
2768
|
-
hamburger: "
|
|
2769
|
-
smiley: "
|
|
2770
|
-
mustache: "
|
|
2771
|
-
star: "
|
|
2772
|
-
play: "
|
|
2773
|
-
nodejs: "
|
|
2774
|
-
oneSeventh: "
|
|
2775
|
-
oneNinth: "
|
|
2776
|
-
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"
|
|
2777
2777
|
};
|
|
2778
2778
|
var specialFallbackSymbols = {
|
|
2779
|
-
tick: "
|
|
2779
|
+
tick: "\u221a",
|
|
2780
2780
|
info: "i",
|
|
2781
|
-
warning: "
|
|
2782
|
-
cross: "
|
|
2783
|
-
squareSmall: "
|
|
2784
|
-
squareSmallFilled: "
|
|
2781
|
+
warning: "\u203c",
|
|
2782
|
+
cross: "\u00d7",
|
|
2783
|
+
squareSmall: "\u25a1",
|
|
2784
|
+
squareSmallFilled: "\u25a0",
|
|
2785
2785
|
circle: "( )",
|
|
2786
2786
|
circleFilled: "(*)",
|
|
2787
2787
|
circleDotted: "( )",
|
|
2788
2788
|
circleDouble: "( )",
|
|
2789
|
-
circleCircle: "(
|
|
2790
|
-
circleCross: "(
|
|
2791
|
-
circlePipe: "(
|
|
2789
|
+
circleCircle: "(\u25cb)",
|
|
2790
|
+
circleCross: "(\u00d7)",
|
|
2791
|
+
circlePipe: "(\u2502)",
|
|
2792
2792
|
radioOn: "(*)",
|
|
2793
2793
|
radioOff: "( )",
|
|
2794
|
-
checkboxOn: "[
|
|
2794
|
+
checkboxOn: "[\u00d7]",
|
|
2795
2795
|
checkboxOff: "[ ]",
|
|
2796
|
-
checkboxCircleOn: "(
|
|
2796
|
+
checkboxCircleOn: "(\u00d7)",
|
|
2797
2797
|
checkboxCircleOff: "( )",
|
|
2798
2798
|
pointer: ">",
|
|
2799
|
-
triangleUpOutline: "
|
|
2800
|
-
triangleLeft: "
|
|
2801
|
-
triangleRight: "
|
|
2802
|
-
lozenge: "
|
|
2803
|
-
lozengeOutline: "
|
|
2804
|
-
hamburger: "
|
|
2805
|
-
smiley: "
|
|
2806
|
-
mustache: "
|
|
2807
|
-
star: "
|
|
2808
|
-
play: "
|
|
2809
|
-
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",
|
|
2810
2810
|
oneSeventh: "1/7",
|
|
2811
2811
|
oneNinth: "1/9",
|
|
2812
2812
|
oneTenth: "1/10"
|
|
@@ -2832,7 +2832,7 @@ var defaultTheme = {
|
|
|
2832
2832
|
},
|
|
2833
2833
|
spinner: {
|
|
2834
2834
|
interval: 80,
|
|
2835
|
-
frames: ["
|
|
2835
|
+
frames: ["\u280b", "\u2819", "\u2839", "\u2838", "\u283c", "\u2834", "\u2826", "\u2827", "\u2807", "\u280f"].map((frame) => styleText("yellow", frame))
|
|
2836
2836
|
},
|
|
2837
2837
|
keybindings: [],
|
|
2838
2838
|
style: {
|
|
@@ -3078,7 +3078,7 @@ var dist_default3 = fastStringWidth;
|
|
|
3078
3078
|
|
|
3079
3079
|
// ../../node_modules/fast-wrap-ansi/lib/main.js
|
|
3080
3080
|
var ESC = "\x1B";
|
|
3081
|
-
var CSI = "
|
|
3081
|
+
var CSI = "\u009b";
|
|
3082
3082
|
var END_CODE = 39;
|
|
3083
3083
|
var ANSI_ESCAPE_BELL = "\x07";
|
|
3084
3084
|
var ANSI_CSI = "[";
|
|
@@ -3808,7 +3808,7 @@ var selectTheme = {
|
|
|
3808
3808
|
style: {
|
|
3809
3809
|
disabled: (text) => styleText3("dim", text),
|
|
3810
3810
|
description: (text) => styleText3("cyan", text),
|
|
3811
|
-
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 "))
|
|
3812
3812
|
},
|
|
3813
3813
|
i18n: { disabledError: "This option is disabled and cannot be selected." },
|
|
3814
3814
|
indexMode: "hidden"
|
|
@@ -3933,8 +3933,8 @@ var dist_default4 = createPrompt((config, done) => {
|
|
|
3933
3933
|
}, []);
|
|
3934
3934
|
const message = theme.style.message(config.message, status);
|
|
3935
3935
|
const helpLine = theme.style.keysHelpTip([
|
|
3936
|
-
["
|
|
3937
|
-
["
|
|
3936
|
+
["\u2191\u2193", "navigate"],
|
|
3937
|
+
["\u23ce", "select"]
|
|
3938
3938
|
]);
|
|
3939
3939
|
let separatorCount = 0;
|
|
3940
3940
|
const page = usePagination({
|
|
@@ -4109,7 +4109,7 @@ function resolveSpecsDir(harnessDir, opts = {}) {
|
|
|
4109
4109
|
}
|
|
4110
4110
|
function assertSafePathComponent(value, what) {
|
|
4111
4111
|
if (value === "" || value === "." || value === ".." || !/^[A-Za-z0-9._-]+$/.test(value)) {
|
|
4112
|
-
throw new Error(`${what} must be a single safe path component ([A-Za-z0-9._-]+; not "", ".", "..", or containing "/" or "\\")
|
|
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)}`);
|
|
4113
4113
|
}
|
|
4114
4114
|
}
|
|
4115
4115
|
function resolveSddDir(harnessDir, planId) {
|
|
@@ -4126,7 +4126,7 @@ var GITIGNORE_SNIPPET = `# Morning Star harness (.mstar/)
|
|
|
4126
4126
|
!.mstar/specs/
|
|
4127
4127
|
!.mstar/specs/**
|
|
4128
4128
|
`;
|
|
4129
|
-
var GITIGNORE_SNIPPET_AGENTS = `# Morning Star harness (.agents/)
|
|
4129
|
+
var GITIGNORE_SNIPPET_AGENTS = `# Morning Star harness (.agents/) \u2014 legacy
|
|
4130
4130
|
# Default-ignore everything under .agents/, then re-include the tracked results.
|
|
4131
4131
|
.agents/**
|
|
4132
4132
|
!.agents/AGENTS.md
|
|
@@ -4171,7 +4171,7 @@ async function withStatusWriteLock(statusPath, fn, opts = {}) {
|
|
|
4171
4171
|
const lockDir = join3(dirname3(resolve3(statusPath)), STATUS_WRITE_LOCKDIR);
|
|
4172
4172
|
const held = heldLockDirs.getStore();
|
|
4173
4173
|
if (held !== undefined && held.has(lockDir)) {
|
|
4174
|
-
throw new Error(`${lockDir} is already held by this process in this async context
|
|
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`);
|
|
4175
4175
|
}
|
|
4176
4176
|
const timeoutMs = opts.timeoutMs ?? 30000;
|
|
4177
4177
|
const pollMs = opts.pollMs ?? 25;
|
|
@@ -4187,7 +4187,7 @@ async function withStatusWriteLock(statusPath, fn, opts = {}) {
|
|
|
4187
4187
|
if (error.code !== "EEXIST")
|
|
4188
4188
|
throw error;
|
|
4189
4189
|
if (Date.now() >= deadline) {
|
|
4190
|
-
throw new Error(`${lockDir} already exists
|
|
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)`);
|
|
4191
4191
|
}
|
|
4192
4192
|
await sleep(pollMs);
|
|
4193
4193
|
}
|
|
@@ -4212,7 +4212,7 @@ async function withStatusWriteLock(statusPath, fn, opts = {}) {
|
|
|
4212
4212
|
} catch {}
|
|
4213
4213
|
}
|
|
4214
4214
|
}
|
|
4215
|
-
var BRANCH_FORMS_HINT = '"Working branch: <existing>" | "Working branch: create <new> from <base>" | "Branch policy: direct on <branch>
|
|
4215
|
+
var BRANCH_FORMS_HINT = '"Working branch: <existing>" | "Working branch: create <new> from <base>" | "Branch policy: direct on <branch> \u2014 <reason>"';
|
|
4216
4216
|
var REQUIRED_FIELDS = [
|
|
4217
4217
|
{ key: "executeAs", label: "Execute as", code: "execute-as" },
|
|
4218
4218
|
{ key: "delegation", label: "Delegation", code: "delegation" },
|
|
@@ -4279,7 +4279,7 @@ function parseAssignmentBranchForms(assignmentText) {
|
|
|
4279
4279
|
if (fields.branchPolicy !== undefined && fields.branchPolicy !== "") {
|
|
4280
4280
|
const direct = fields.branchPolicy.match(/^direct\s+on\s+(\S+)/i);
|
|
4281
4281
|
if (direct) {
|
|
4282
|
-
const strict = fields.branchPolicy.match(/^direct\s+on\s+(\S+)(?:\s*(?:[
|
|
4282
|
+
const strict = fields.branchPolicy.match(/^direct\s+on\s+(\S+)(?:\s*(?:[\u2014\u2013]|--|-)\s*(.+))?$/);
|
|
4283
4283
|
forms.directOn = { branch: direct[1].trim(), reason: strict ? (strict[2] ?? "").trim() : "" };
|
|
4284
4284
|
}
|
|
4285
4285
|
}
|
|
@@ -4308,7 +4308,7 @@ function validateAssignmentFields(assignmentText, opts = {}) {
|
|
|
4308
4308
|
if (formCount === 0) {
|
|
4309
4309
|
violations.push(violation2("high", "assignment.field.branch-missing", "writable assignment must contain exactly one branch form", `add exactly one of: ${BRANCH_FORMS_HINT}`));
|
|
4310
4310
|
} else if (formCount > 1) {
|
|
4311
|
-
violations.push(violation2("high", "assignment.field.branch-multiple", `writable assignment contains ${formCount} branch forms (Working branch + Branch policy)
|
|
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}`));
|
|
4312
4312
|
} else if (workingPresent) {
|
|
4313
4313
|
const create = forms.createForm;
|
|
4314
4314
|
if (create !== undefined && (create.base === undefined || create.base.trim() === "" || create.name.trim() === "")) {
|
|
@@ -4317,9 +4317,9 @@ function validateAssignmentFields(assignmentText, opts = {}) {
|
|
|
4317
4317
|
} else if (policyPresent) {
|
|
4318
4318
|
const direct = forms.directOn;
|
|
4319
4319
|
if (direct === undefined) {
|
|
4320
|
-
violations.push(violation2("high", "assignment.field.branch-policy-missing-branch", `unparseable Branch policy: "${fields.branchPolicy}" (expected "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>`"));
|
|
4321
4321
|
} else if (direct.reason === "") {
|
|
4322
|
-
violations.push(violation2("high", "assignment.field.branch-policy-missing-reason", `Branch policy "direct on ${direct.branch}" is missing the reason`, 'append "
|
|
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'));
|
|
4323
4323
|
}
|
|
4324
4324
|
}
|
|
4325
4325
|
}
|
|
@@ -4330,7 +4330,7 @@ function assertDefaultBranchProtected(branch, opts = {}) {
|
|
|
4330
4330
|
const violations = [];
|
|
4331
4331
|
const normalized = branch.trim();
|
|
4332
4332
|
if (normalized !== "" && defaultBranches.includes(normalized) && opts.directOnException !== true) {
|
|
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}
|
|
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`));
|
|
4334
4334
|
}
|
|
4335
4335
|
return { ok: violations.length === 0, violations };
|
|
4336
4336
|
}
|
|
@@ -4347,9 +4347,9 @@ function executionModeToN(executionMode, opts = {}) {
|
|
|
4347
4347
|
} else if (mode === "targeted") {
|
|
4348
4348
|
const seats = [...new Set((opts.seats ?? []).map((s) => s.trim()).filter((s) => s !== ""))];
|
|
4349
4349
|
if (seats.length === 0) {
|
|
4350
|
-
violations.push(violation2("high", "dispatch.execution-mode.missing-seats", 'execution mode "targeted" requires listed reviewer seats', 'add "QC re-review: targeted
|
|
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'));
|
|
4351
4351
|
} else if (seats.length > 3) {
|
|
4352
|
-
violations.push(violation2("high", "dispatch.execution-mode.too-many-seats", `execution mode "targeted" lists ${seats.length} reviewer seats
|
|
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"));
|
|
4353
4353
|
} else {
|
|
4354
4354
|
n = seats.length;
|
|
4355
4355
|
}
|
|
@@ -4416,7 +4416,7 @@ function validatePlanRow(row) {
|
|
|
4416
4416
|
validateNonEmptyString2(violations, planId, "plan_id", "status.plan-row.missing-plan-id", "status.plan-row.invalid-plan-id");
|
|
4417
4417
|
}
|
|
4418
4418
|
if (id !== undefined && planId !== undefined && id !== planId) {
|
|
4419
|
-
violations.push(violation3("medium", "status.plan-row.dual-id", "row has both id and plan_id with different values
|
|
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)"));
|
|
4420
4420
|
}
|
|
4421
4421
|
}
|
|
4422
4422
|
validateNonEmptyString2(violations, title, "title", "status.plan-row.missing-title", "status.plan-row.invalid-title");
|
|
@@ -4424,7 +4424,7 @@ function validatePlanRow(row) {
|
|
|
4424
4424
|
if (status === undefined) {
|
|
4425
4425
|
violations.push(violation3("high", "status.plan-row.missing-status", "missing required field: status"));
|
|
4426
4426
|
} else if (typeof status !== "string" || !PLAN_STATUSES.includes(status)) {
|
|
4427
|
-
violations.push(violation3("medium", "status.plan-row.invalid-status", `status must be one of ${PLAN_STATUSES.join(" | ")}
|
|
4427
|
+
violations.push(violation3("medium", "status.plan-row.invalid-status", `status must be one of ${PLAN_STATUSES.join(" | ")} \u2014 got ${JSON.stringify(status)}`));
|
|
4428
4428
|
}
|
|
4429
4429
|
if (metadata !== undefined && !isPlainObject2(metadata)) {
|
|
4430
4430
|
violations.push(violation3("medium", "status.plan-row.invalid-metadata", "metadata must be an object"));
|
|
@@ -4433,7 +4433,7 @@ function validatePlanRow(row) {
|
|
|
4433
4433
|
violations.push(violation3("medium", "status.plan-row.invalid-execution-lease", "execution_lease must be an object"));
|
|
4434
4434
|
}
|
|
4435
4435
|
if (status === "Done" && execution_lease !== undefined) {
|
|
4436
|
-
violations.push(violation3("medium", "status.plan-row.done-with-lease", 'plan status Done must not carry an execution_lease
|
|
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"'));
|
|
4437
4437
|
}
|
|
4438
4438
|
return { ok: violations.length === 0, violations };
|
|
4439
4439
|
}
|
|
@@ -4451,14 +4451,14 @@ function validateResidual(entry) {
|
|
|
4451
4451
|
if (severity === undefined) {
|
|
4452
4452
|
violations.push(violation3("high", "status.residual.missing-severity", "missing required field: severity"));
|
|
4453
4453
|
} else if (typeof severity !== "string" || !SEVERITY_ORDER.includes(severity) && severity !== "warning") {
|
|
4454
|
-
violations.push(violation3("medium", "status.residual.invalid-severity", `severity must be one of ${SEVERITY_ORDER.join(" | ")}
|
|
4454
|
+
violations.push(violation3("medium", "status.residual.invalid-severity", `severity must be one of ${SEVERITY_ORDER.join(" | ")} \u2014 got ${JSON.stringify(severity)}`));
|
|
4455
4455
|
} else if (severity === "warning") {
|
|
4456
|
-
violations.push(violation3("low", "status.residual.legacy-warning", `severity "warning" is legacy
|
|
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')`));
|
|
4457
4457
|
}
|
|
4458
4458
|
if (decision === undefined) {
|
|
4459
4459
|
violations.push(violation3("high", "status.residual.missing-decision", "missing required field: decision"));
|
|
4460
4460
|
} else if (typeof decision !== "string" || !RESIDUAL_DECISIONS.includes(decision)) {
|
|
4461
|
-
violations.push(violation3("medium", "status.residual.invalid-decision", `decision must be one of ${RESIDUAL_DECISIONS.join(" | ")}
|
|
4461
|
+
violations.push(violation3("medium", "status.residual.invalid-decision", `decision must be one of ${RESIDUAL_DECISIONS.join(" | ")} \u2014 got ${JSON.stringify(decision)}`));
|
|
4462
4462
|
}
|
|
4463
4463
|
if (target === undefined) {
|
|
4464
4464
|
violations.push(violation3("high", "status.residual.missing-target", "missing required field: target"));
|
|
@@ -4478,7 +4478,7 @@ function validateResidual(entry) {
|
|
|
4478
4478
|
}
|
|
4479
4479
|
if (lifecycle !== undefined) {
|
|
4480
4480
|
if (typeof lifecycle !== "string" || !RESIDUAL_LIFECYCLES.includes(lifecycle)) {
|
|
4481
|
-
violations.push(violation3("medium", "status.residual.invalid-lifecycle", `lifecycle must be one of ${RESIDUAL_LIFECYCLES.join(" | ")}
|
|
4481
|
+
violations.push(violation3("medium", "status.residual.invalid-lifecycle", `lifecycle must be one of ${RESIDUAL_LIFECYCLES.join(" | ")} \u2014 got ${JSON.stringify(lifecycle)}`));
|
|
4482
4482
|
} else if (lifecycle !== "open") {
|
|
4483
4483
|
if (closed_at === undefined) {
|
|
4484
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")'));
|
|
@@ -4511,7 +4511,7 @@ function validateStatus(docOrPath) {
|
|
|
4511
4511
|
} else if (typeof version !== "number" || !Number.isInteger(version)) {
|
|
4512
4512
|
violations.push(violation3("high", "status.invalid-version", "version must be an integer"));
|
|
4513
4513
|
} else if (version !== 1) {
|
|
4514
|
-
violations.push(violation3("medium", "status.unsupported-version", `unsupported status.json schema version ${version}
|
|
4514
|
+
violations.push(violation3("medium", "status.unsupported-version", `unsupported status.json schema version ${version} \u2014 expected 1`));
|
|
4515
4515
|
}
|
|
4516
4516
|
if (updated_at === undefined) {
|
|
4517
4517
|
violations.push(violation3("high", "status.missing-updated-at", "missing required field: updated_at"));
|
|
@@ -4536,7 +4536,7 @@ function validateStatus(docOrPath) {
|
|
|
4536
4536
|
if (!Array.isArray(list)) {
|
|
4537
4537
|
violations.push(violation3("high", "status.residual.invalid-list", `residual_findings["${planId}"] must be an array`));
|
|
4538
4538
|
} else if (list.length === 0) {
|
|
4539
|
-
violations.push(violation3("low", "status.residual.empty-key", `residual_findings["${planId}"] is empty
|
|
4539
|
+
violations.push(violation3("low", "status.residual.empty-key", `residual_findings["${planId}"] is empty \u2014 delete the key (no "plan-id": [])`));
|
|
4540
4540
|
} else {
|
|
4541
4541
|
for (const entry of list) {
|
|
4542
4542
|
violations.push(...validateResidual(entry).violations);
|
|
@@ -4549,14 +4549,14 @@ function validateStatus(docOrPath) {
|
|
|
4549
4549
|
} else if (!isPlainObject2(metadata)) {
|
|
4550
4550
|
violations.push(violation3("high", "status.invalid-metadata", "metadata must be an object"));
|
|
4551
4551
|
} else if (Object.prototype.hasOwnProperty.call(metadata, "residual_findings")) {
|
|
4552
|
-
violations.push(violation3("medium", "status.dual-write-residuals", "residual_findings must be root-only
|
|
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"));
|
|
4553
4553
|
}
|
|
4554
4554
|
return { ok: violations.length === 0, violations };
|
|
4555
4555
|
}
|
|
4556
4556
|
async function archiveResiduals(planId, harnessDir) {
|
|
4557
4557
|
const dir = harnessDir !== undefined ? resolve4(harnessDir) : resolveHarnessDir();
|
|
4558
4558
|
if (dir === null) {
|
|
4559
|
-
throw new Error(`harness dir not found from ${process.cwd()}
|
|
4559
|
+
throw new Error(`harness dir not found from ${process.cwd()} \u2014 pass harnessDir or set MSTAR_HARNESS_DIR`);
|
|
4560
4560
|
}
|
|
4561
4561
|
assertSafePathComponent(planId, "planId");
|
|
4562
4562
|
const statusPath = join4(dir, "status.json");
|
|
@@ -4633,16 +4633,16 @@ function l1PreDispatchCheck(input, opts = {}) {
|
|
|
4633
4633
|
const violations = [];
|
|
4634
4634
|
const { controlWorktreePath, leaseWorktreePath, leaseWorkingBranch, planId } = input;
|
|
4635
4635
|
if (controlWorktreePath.trim() === "") {
|
|
4636
|
-
violations.push(violation4("high", "worktree.l1.control-missing", "metadata.control_worktree_path is not recorded
|
|
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"));
|
|
4637
4637
|
}
|
|
4638
4638
|
if (leaseWorktreePath.trim() === "") {
|
|
4639
|
-
violations.push(violation4("high", "worktree.l1.lease-missing", `execution_lease.worktree_path is empty for plan "${planId}"
|
|
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"));
|
|
4640
4640
|
}
|
|
4641
4641
|
if (leaseWorkingBranch.trim() === "") {
|
|
4642
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"));
|
|
4643
4643
|
}
|
|
4644
4644
|
if (controlWorktreePath !== "" && leaseWorktreePath !== "" && resolve5(controlWorktreePath) === resolve5(leaseWorktreePath)) {
|
|
4645
|
-
violations.push(violation4("critical", "worktree.l1.lease-equals-control", `execution_lease.worktree_path "${leaseWorktreePath}" equals metadata.control_worktree_path
|
|
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"));
|
|
4646
4646
|
}
|
|
4647
4647
|
if (leaseWorktreePath !== "" && !existsSync3(leaseWorktreePath)) {
|
|
4648
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>`));
|
|
@@ -4661,7 +4661,7 @@ function l2PreDispatchCheck(input, opts = {}) {
|
|
|
4661
4661
|
const tracks = input.tracks ?? [];
|
|
4662
4662
|
const seenPaths = new Set;
|
|
4663
4663
|
if (tracks.length < 1) {
|
|
4664
|
-
violations.push(violation4("high", "worktree.l2.no-tracks", "no parallel writable tracks
|
|
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"));
|
|
4665
4665
|
}
|
|
4666
4666
|
tracks.forEach((track, index) => {
|
|
4667
4667
|
if (track.worktreePath.trim() === "" || track.workingBranch.trim() === "") {
|
|
@@ -4669,12 +4669,12 @@ function l2PreDispatchCheck(input, opts = {}) {
|
|
|
4669
4669
|
return;
|
|
4670
4670
|
}
|
|
4671
4671
|
if (!isAbsolute3(track.worktreePath)) {
|
|
4672
|
-
violations.push(violation4("high", "worktree.l2.track-path-relative", `track ${index + 1} worktreePath "${track.worktreePath}" is not an absolute path
|
|
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>)`));
|
|
4673
4673
|
return;
|
|
4674
4674
|
}
|
|
4675
4675
|
const normalized = resolve5(track.worktreePath);
|
|
4676
4676
|
if (seenPaths.has(normalized)) {
|
|
4677
|
-
violations.push(violation4("high", "worktree.l2.track-path-collision", `duplicate worktreePath "${track.worktreePath}" across parallel tracks
|
|
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"));
|
|
4678
4678
|
return;
|
|
4679
4679
|
}
|
|
4680
4680
|
seenPaths.add(normalized);
|
|
@@ -4771,7 +4771,7 @@ function sddWorkspace(planId, opts = {}) {
|
|
|
4771
4771
|
throw new SddScriptError(`mstar sdd workspace: linked worktree at ${root} has no {HARNESS_DIR}/status.json (default gitignore).
|
|
4772
4772
|
` + ` Refusing to create a second SDD tree under the feature checkout.
|
|
4773
4773
|
` + ` Re-run with MSTAR_CONTROL_ROOT=<control_worktree_path> or: mstar sdd workspace ${planId} <control_worktree_path>
|
|
4774
|
-
` + ` See mstar-branch-worktree
|
|
4774
|
+
` + ` See mstar-branch-worktree \u00abHarness path SSOT under default gitignore\u00bb.`, 1);
|
|
4775
4775
|
}
|
|
4776
4776
|
const harnessOverride = opts.harnessDir ?? (process.env.MSTAR_HARNESS_DIR || undefined);
|
|
4777
4777
|
let harnessDir;
|
|
@@ -4973,7 +4973,7 @@ function validateCompassFrontmatter(doc) {
|
|
|
4973
4973
|
return {
|
|
4974
4974
|
ok: false,
|
|
4975
4975
|
violations: [
|
|
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
|
|
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")
|
|
4977
4977
|
]
|
|
4978
4978
|
};
|
|
4979
4979
|
}
|
|
@@ -4983,17 +4983,17 @@ function validateCompassFrontmatter(doc) {
|
|
|
4983
4983
|
ok: false,
|
|
4984
4984
|
violations: parsed.issues.map((issue) => {
|
|
4985
4985
|
const field = issue.path.join(".") || "(root)";
|
|
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
|
|
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)`);
|
|
4987
4987
|
})
|
|
4988
4988
|
};
|
|
4989
4989
|
}
|
|
4990
4990
|
const violations = [];
|
|
4991
4991
|
const { status, end_date } = parsed.data;
|
|
4992
4992
|
if (status === "completed" && end_date === undefined) {
|
|
4993
|
-
violations.push(violation5("high", "COMPASS_END_DATE_REQUIRED", "Compass frontmatter status is 'completed' but end_date is missing
|
|
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"));
|
|
4994
4994
|
}
|
|
4995
4995
|
if (status !== "completed" && end_date !== undefined) {
|
|
4996
|
-
violations.push(violation5("medium", "COMPASS_END_DATE_NOT_ALLOWED", `Compass frontmatter sets end_date while status is '${status}'
|
|
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"));
|
|
4997
4997
|
}
|
|
4998
4998
|
return { ok: violations.length === 0, violations };
|
|
4999
4999
|
}
|
|
@@ -5017,17 +5017,17 @@ function findPlanRow(statusDoc, planId) {
|
|
|
5017
5017
|
function entryPlansAllDone(statusDoc, registered) {
|
|
5018
5018
|
const violations = [];
|
|
5019
5019
|
if (registered.length === 0) {
|
|
5020
|
-
violations.push(violation5("medium", "COMPASS_NO_PLANS", "Compass frontmatter registers no 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`"));
|
|
5021
5021
|
return violations;
|
|
5022
5022
|
}
|
|
5023
5023
|
for (const planId of registered) {
|
|
5024
5024
|
const row = findPlanRow(statusDoc, planId);
|
|
5025
5025
|
if (row === null) {
|
|
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
|
|
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"));
|
|
5027
5027
|
continue;
|
|
5028
5028
|
}
|
|
5029
5029
|
if (row.status !== PLAN_STATUS_DONE) {
|
|
5030
|
-
violations.push(violation5("high", "PLAN_NOT_DONE", `Plan '${planId}' status is ${JSON.stringify(row.status)} in status.json
|
|
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)`));
|
|
5031
5031
|
}
|
|
5032
5032
|
}
|
|
5033
5033
|
return violations;
|
|
@@ -5038,14 +5038,14 @@ function entryResidualsOpen(statusDoc, planId) {
|
|
|
5038
5038
|
if (residualRoot === undefined || residualRoot === null)
|
|
5039
5039
|
return violations;
|
|
5040
5040
|
if (!isPlainObject3(residualRoot)) {
|
|
5041
|
-
violations.push(violation5("medium", "RESIDUAL_MALFORMED", "status.json residual_findings must be a plan-id
|
|
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)"));
|
|
5042
5042
|
return violations;
|
|
5043
5043
|
}
|
|
5044
5044
|
const entries = residualRoot[planId];
|
|
5045
5045
|
if (entries === undefined)
|
|
5046
5046
|
return violations;
|
|
5047
5047
|
if (!Array.isArray(entries)) {
|
|
5048
|
-
violations.push(violation5("medium", "RESIDUAL_MALFORMED", `status.json residual_findings['${planId}'] must be an array of residual entries (mstar-iteration
|
|
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)`));
|
|
5049
5049
|
return violations;
|
|
5050
5050
|
}
|
|
5051
5051
|
const openIds = [];
|
|
@@ -5058,7 +5058,7 @@ function entryResidualsOpen(statusDoc, planId) {
|
|
|
5058
5058
|
openIds.push(typeof entry.id === "string" ? entry.id : "<unnamed>");
|
|
5059
5059
|
}
|
|
5060
5060
|
if (openIds.length > 0) {
|
|
5061
|
-
violations.push(violation5("high", "OPEN_RESIDUALS", `Plan '${planId}' has ${openIds.length} open residual finding(s) not exempted as blocker-defers (${openIds.join(", ")})
|
|
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"));
|
|
5062
5062
|
}
|
|
5063
5063
|
return violations;
|
|
5064
5064
|
}
|
|
@@ -5068,11 +5068,11 @@ function entryFrontmatterComplete(compassDoc) {
|
|
|
5068
5068
|
function exitFrontmatterClosed(compassDoc) {
|
|
5069
5069
|
const violations = [];
|
|
5070
5070
|
if (compassDoc.status !== "completed") {
|
|
5071
|
-
violations.push(violation5("high", "EXIT_STATUS_NOT_COMPLETED", `Compass frontmatter status must be 'completed' at close exit
|
|
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)`));
|
|
5072
5072
|
}
|
|
5073
5073
|
const endDate = compassDoc.end_date;
|
|
5074
5074
|
if (typeof endDate !== "string" || !DATE_RE2.test(endDate)) {
|
|
5075
|
-
violations.push(violation5("high", "EXIT_END_DATE_REQUIRED", "Compass frontmatter end_date (YYYY-MM-DD) is required when closing (mstar-iteration
|
|
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)"));
|
|
5076
5076
|
}
|
|
5077
5077
|
return violations;
|
|
5078
5078
|
}
|
|
@@ -5080,9 +5080,9 @@ function exitBranchCheck(opts) {
|
|
|
5080
5080
|
const violations = [];
|
|
5081
5081
|
const { currentBranch, specIntegrationBranch } = opts;
|
|
5082
5082
|
if (currentBranch === undefined || specIntegrationBranch === undefined) {
|
|
5083
|
-
violations.push(violation5("medium", "EXIT_BRANCH_UNVERIFIABLE", "Cannot verify the current branch is spec_integration_branch
|
|
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)"));
|
|
5084
5084
|
} else if (currentBranch !== specIntegrationBranch) {
|
|
5085
|
-
violations.push(violation5("high", "EXIT_BRANCH_MISMATCH", `Current branch '${currentBranch}' is not the spec_integration_branch '${specIntegrationBranch}' (mstar-iteration
|
|
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)`));
|
|
5086
5086
|
}
|
|
5087
5087
|
return violations;
|
|
5088
5088
|
}
|
|
@@ -5091,9 +5091,9 @@ function exitPrBaseCheck(compassDoc, opts) {
|
|
|
5091
5091
|
const target = compassDoc.target_branch;
|
|
5092
5092
|
const { prBaseBranch } = opts;
|
|
5093
5093
|
if (prBaseBranch === undefined) {
|
|
5094
|
-
violations.push(violation5("medium", "EXIT_PR_BASE_UNVERIFIABLE", "Cannot verify the PR base
|
|
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)"));
|
|
5095
5095
|
} else if (typeof target !== "string" || prBaseBranch !== target) {
|
|
5096
|
-
violations.push(violation5("high", "EXIT_PR_BASE_MISMATCH", `PR base '${prBaseBranch}' must equal the compass target_branch '${String(target)}'
|
|
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)`));
|
|
5097
5097
|
}
|
|
5098
5098
|
return violations;
|
|
5099
5099
|
}
|
|
@@ -5135,10 +5135,10 @@ function evaluatePhaseGate(statusDoc, compassDoc, opts = {}) {
|
|
|
5135
5135
|
function pushCadenceProbe(ciRunning, reviewWaveActive) {
|
|
5136
5136
|
const violations = [];
|
|
5137
5137
|
if (ciRunning) {
|
|
5138
|
-
violations.push(violation5("high", "PUSH_BLOCKED_CI", "CI checks are still queued/in_progress on the current head
|
|
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"));
|
|
5139
5139
|
}
|
|
5140
5140
|
if (reviewWaveActive) {
|
|
5141
|
-
violations.push(violation5("high", "PUSH_BLOCKED_REVIEW_WAVE", "An AI/bot review wave is still running on the current head
|
|
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"));
|
|
5142
5142
|
}
|
|
5143
5143
|
return { ok: violations.length === 0, violations };
|
|
5144
5144
|
}
|
|
@@ -5179,7 +5179,7 @@ function parseCompassFrontmatter(filePath) {
|
|
|
5179
5179
|
function parseFlowArray(raw, filePath) {
|
|
5180
5180
|
const inner = raw.slice(1, -1);
|
|
5181
5181
|
if (/[[\]]/.test(inner)) {
|
|
5182
|
-
throw new Error(`nested flow-style array in ${filePath}: ${JSON.stringify(raw)}
|
|
5182
|
+
throw new Error(`nested flow-style array in ${filePath}: ${JSON.stringify(raw)} \u2014 only flat scalar items are supported (e.g. [a, b])`);
|
|
5183
5183
|
}
|
|
5184
5184
|
let quote = null;
|
|
5185
5185
|
for (const ch of inner) {
|
|
@@ -5189,7 +5189,7 @@ function parseFlowArray(raw, filePath) {
|
|
|
5189
5189
|
else if (quote === ch)
|
|
5190
5190
|
quote = null;
|
|
5191
5191
|
} else if (ch === "," && quote !== null) {
|
|
5192
|
-
throw new Error(`ambiguous flow-style array in ${filePath}: ${JSON.stringify(raw)}
|
|
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)`);
|
|
5193
5193
|
}
|
|
5194
5194
|
}
|
|
5195
5195
|
if (quote !== null) {
|
|
@@ -5317,7 +5317,7 @@ function validateDesignTokenFrontmatter(frontmatterText) {
|
|
|
5317
5317
|
const violations = [];
|
|
5318
5318
|
const fm = parseDesignFrontmatter(frontmatterText);
|
|
5319
5319
|
if (fm === null) {
|
|
5320
|
-
violations.push(violation6("medium", "design-md.tokens.missing-frontmatter", "no `---` YAML frontmatter block found
|
|
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"));
|
|
5321
5321
|
return { ok: false, violations };
|
|
5322
5322
|
}
|
|
5323
5323
|
const groupEntries = (group) => {
|
|
@@ -5332,61 +5332,61 @@ function validateDesignTokenFrontmatter(frontmatterText) {
|
|
|
5332
5332
|
};
|
|
5333
5333
|
for (const group of ["colors", "typography", "spacing", "rounded"]) {
|
|
5334
5334
|
if (!isMap(fm[group])) {
|
|
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
|
|
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`));
|
|
5336
5336
|
} else if (!groupIsMap(group)) {
|
|
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
|
|
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`));
|
|
5338
5338
|
} else if (groupEntries(group).length === 0) {
|
|
5339
|
-
violations.push(violation6("medium", "design-md.tokens.missing-group", `missing required token group "${group}"
|
|
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`));
|
|
5340
5340
|
}
|
|
5341
5341
|
}
|
|
5342
5342
|
if (!isMap(fm.components) || !groupIsMap("components")) {
|
|
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
|
|
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`));
|
|
5344
5344
|
}
|
|
5345
|
-
const placeholder = (group, name, value) => violations.push(violation6("low", "design-md.tokens.placeholder", `token "${group}.${name}" uses a "[...]" template 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`));
|
|
5346
5346
|
for (const [name, value] of groupEntries("colors")) {
|
|
5347
5347
|
if (typeof value !== "string") {
|
|
5348
|
-
violations.push(violation6("medium", "design-md.tokens.color-format", `color "${name}" must be a string value (design-md-spec
|
|
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"));
|
|
5349
5349
|
continue;
|
|
5350
5350
|
}
|
|
5351
5351
|
if (isPlaceholder(value)) {
|
|
5352
5352
|
placeholder("colors", name, value);
|
|
5353
5353
|
} else if (!HEX_RE.test(value) && !OKLCH_RE.test(value)) {
|
|
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
|
|
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"));
|
|
5355
5355
|
}
|
|
5356
5356
|
}
|
|
5357
5357
|
for (const [name, value] of groupEntries("typography")) {
|
|
5358
5358
|
if (!isMap(value)) {
|
|
5359
|
-
violations.push(violation6("medium", "design-md.tokens.typography-shape", `typography token "${name}" must be a map of the five properties (design-md-spec
|
|
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"));
|
|
5360
5360
|
continue;
|
|
5361
5361
|
}
|
|
5362
5362
|
const keys = Object.keys(value);
|
|
5363
5363
|
const missing = TYPOGRAPHY_PROPS.filter((p) => !keys.includes(p));
|
|
5364
5364
|
const extra = keys.filter((k) => !TYPOGRAPHY_PROPS.includes(k));
|
|
5365
5365
|
if (missing.length > 0 || extra.length > 0) {
|
|
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
|
|
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"));
|
|
5367
5367
|
}
|
|
5368
5368
|
for (const prop of ["fontFamily", "fontSize"]) {
|
|
5369
5369
|
const v = value[prop];
|
|
5370
5370
|
if (typeof v === "string" && isPlaceholder(v))
|
|
5371
5371
|
placeholder("typography", name, v);
|
|
5372
5372
|
else if (typeof v !== "string" || v.trim() === "") {
|
|
5373
|
-
violations.push(violation6("medium", "design-md.tokens.typography-shape", `typography token "${name}" has an empty \`${prop}\` (design-md-spec
|
|
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`));
|
|
5374
5374
|
}
|
|
5375
5375
|
}
|
|
5376
5376
|
}
|
|
5377
5377
|
if (groupIsMap("spacing")) {
|
|
5378
5378
|
const spacing = fm.spacing;
|
|
5379
5379
|
if (!Object.prototype.hasOwnProperty.call(spacing, "base")) {
|
|
5380
|
-
violations.push(violation6("medium", "design-md.tokens.spacing-base", "spacing must declare the base unit as `base` (design-md-spec
|
|
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"));
|
|
5381
5381
|
}
|
|
5382
5382
|
for (const [name, value] of Object.entries(spacing)) {
|
|
5383
5383
|
if (name !== "base" && name !== RAW_GROUP && !/^\d+$/.test(name)) {
|
|
5384
|
-
violations.push(violation6("medium", "design-md.tokens.spacing-key", `spacing key "${name}" must be \`base\` or a numeric multiplier (design-md-spec
|
|
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`"));
|
|
5385
5385
|
}
|
|
5386
5386
|
if (typeof value === "string" && isPlaceholder(value)) {
|
|
5387
5387
|
placeholder("spacing", name, value);
|
|
5388
5388
|
} else if (typeof value !== "string" || !PX_RE.test(value)) {
|
|
5389
|
-
violations.push(violation6("medium", "design-md.tokens.spacing-format", `spacing value "${name}" = "${String(value)}" is not a px length (design-md-spec
|
|
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`"));
|
|
5390
5390
|
}
|
|
5391
5391
|
}
|
|
5392
5392
|
}
|
|
@@ -5394,12 +5394,12 @@ function validateDesignTokenFrontmatter(frontmatterText) {
|
|
|
5394
5394
|
if (typeof value === "string" && isPlaceholder(value)) {
|
|
5395
5395
|
placeholder("rounded", name, value);
|
|
5396
5396
|
} else if (typeof value !== "string" || !PX_RE.test(value)) {
|
|
5397
|
-
violations.push(violation6("medium", "design-md.tokens.rounded-format", `rounded value "${name}" = "${String(value)}" is not a px length (design-md-spec
|
|
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`"));
|
|
5398
5398
|
}
|
|
5399
5399
|
}
|
|
5400
5400
|
for (const [name, value] of groupEntries("components")) {
|
|
5401
5401
|
if (!isMap(value)) {
|
|
5402
|
-
violations.push(violation6("medium", "design-md.tokens.components-shape", `component token "${name}" must be a map of properties (design-md-spec
|
|
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"));
|
|
5403
5403
|
continue;
|
|
5404
5404
|
}
|
|
5405
5405
|
for (const [prop, v] of Object.entries(value)) {
|
|
@@ -5415,7 +5415,7 @@ function validateDesignTokenFrontmatter(frontmatterText) {
|
|
|
5415
5415
|
const [, refGroup, refKey] = ref;
|
|
5416
5416
|
const resolves = REF_GROUPS.includes(refGroup) && groupIsMap(refGroup) && Object.prototype.hasOwnProperty.call(fm[refGroup], refKey);
|
|
5417
5417
|
if (!resolves) {
|
|
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
|
|
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`));
|
|
5419
5419
|
}
|
|
5420
5420
|
}
|
|
5421
5421
|
}
|
|
@@ -5427,7 +5427,7 @@ function assertLightDarkParity(lightFm, darkFm) {
|
|
|
5427
5427
|
const light = parseDesignFrontmatter(lightFm);
|
|
5428
5428
|
const dark = parseDesignFrontmatter(darkFm);
|
|
5429
5429
|
if (light === null || dark === null) {
|
|
5430
|
-
violations.push(violation6("medium", "design-md.parity.missing-frontmatter", `light/dark parity needs a YAML frontmatter in both 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"));
|
|
5431
5431
|
return { ok: false, violations };
|
|
5432
5432
|
}
|
|
5433
5433
|
const activeKeys = (fm) => {
|
|
@@ -5445,12 +5445,12 @@ function assertLightDarkParity(lightFm, darkFm) {
|
|
|
5445
5445
|
const darkKeys = activeKeys(dark);
|
|
5446
5446
|
for (const key of lightKeys) {
|
|
5447
5447
|
if (!darkKeys.has(key)) {
|
|
5448
|
-
violations.push(violation6("medium", "design-md.parity.missing-dark", `token "${key}" is active in DESIGN.md but missing from DESIGN.dark.md
|
|
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"));
|
|
5449
5449
|
}
|
|
5450
5450
|
}
|
|
5451
5451
|
for (const key of darkKeys) {
|
|
5452
5452
|
if (!lightKeys.has(key)) {
|
|
5453
|
-
violations.push(violation6("medium", "design-md.parity.missing-light", `token "${key}" is active in DESIGN.dark.md but missing from DESIGN.md
|
|
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"));
|
|
5454
5454
|
}
|
|
5455
5455
|
}
|
|
5456
5456
|
return { ok: violations.length === 0, violations };
|
|
@@ -5643,7 +5643,7 @@ function slugify(title) {
|
|
|
5643
5643
|
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5644
5644
|
}
|
|
5645
5645
|
var escapeCell = (value) => value.replace(/\|/g, "\\|");
|
|
5646
|
-
var truncate = (value, max) => value.length > max ? `${value.slice(0, max)}
|
|
5646
|
+
var truncate = (value, max) => value.length > max ? `${value.slice(0, max)}\u2026` : value;
|
|
5647
5647
|
function renderPlanFile(finding, plannedAt) {
|
|
5648
5648
|
const sections = [
|
|
5649
5649
|
`# ${finding.title}`,
|
|
@@ -5682,14 +5682,14 @@ function renderIndex(params) {
|
|
|
5682
5682
|
const { date, repoName, repoShortSha, rows, rejected } = params;
|
|
5683
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(`
|
|
5684
5684
|
`);
|
|
5685
|
-
const directionRows = rows.filter((r) => r.category === "direction").map((r) => `- ${escapeCell(r.title)}
|
|
5685
|
+
const directionRows = rows.filter((r) => r.category === "direction").map((r) => `- ${escapeCell(r.title)} \u2014 ${escapeCell(truncate(r.impact, 120))}`).join(`
|
|
5686
5686
|
`);
|
|
5687
5687
|
const executionRows = rows.map((r) => `| ${r.num} | ${escapeCell(r.title)} | ${r.priority} | ${r.effort} | ${r.dependsOn} | TODO |`).join(`
|
|
5688
5688
|
`);
|
|
5689
5689
|
const rejectedRows = rejected.map((r) => `- ${escapeCell(r.title)}: ${escapeCell(r.reason)}`).join(`
|
|
5690
5690
|
`);
|
|
5691
5691
|
const sections = [
|
|
5692
|
-
`# Audit Report
|
|
5692
|
+
`# Audit Report \u2014 ${repoName} @ ${repoShortSha} (${date})`,
|
|
5693
5693
|
"",
|
|
5694
5694
|
"## Findings",
|
|
5695
5695
|
"",
|
|
@@ -5704,6 +5704,7 @@ function renderIndex(params) {
|
|
|
5704
5704
|
if (rejectedRows !== "") {
|
|
5705
5705
|
sections.push("", "## Findings considered and rejected", "", rejectedRows);
|
|
5706
5706
|
}
|
|
5707
|
+
sections.push("", "## Red-team dispositions", "", "- <finding>: <survived / refuted / hallucination-dropped / uncovered-kept>, <one-line reason>");
|
|
5707
5708
|
return `${sections.join(`
|
|
5708
5709
|
`)}
|
|
5709
5710
|
`;
|
|
@@ -5738,14 +5739,14 @@ function scaffoldAuditPlan(outDir, findings, options = {}) {
|
|
|
5738
5739
|
return {
|
|
5739
5740
|
num: file.slice(0, 3),
|
|
5740
5741
|
title: summary.title,
|
|
5741
|
-
category: fields.get("Category") ?? "
|
|
5742
|
+
category: fields.get("Category") ?? "\u2014",
|
|
5742
5743
|
impact: "see plan file",
|
|
5743
|
-
effort: fields.get("Effort") ?? "
|
|
5744
|
-
risk: fields.get("Risk") ?? "
|
|
5745
|
-
confidence: "
|
|
5746
|
-
evidence: fields.get("Evidence") ?? "
|
|
5747
|
-
priority: fields.get("Priority") ?? "
|
|
5748
|
-
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"
|
|
5749
5750
|
};
|
|
5750
5751
|
});
|
|
5751
5752
|
const byNum = new Map(rows.map((r) => [r.num, r]));
|
|
@@ -5939,7 +5940,7 @@ function validateSchemaYaml(frontmatterText) {
|
|
|
5939
5940
|
const violations = [];
|
|
5940
5941
|
const doc = parseYamlLite(frontmatterText);
|
|
5941
5942
|
if (doc === null) {
|
|
5942
|
-
violations.push(violation8("medium", "compound.schema.missing-frontmatter", "no `---` YAML frontmatter block found
|
|
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"));
|
|
5943
5944
|
return { ok: false, violations };
|
|
5944
5945
|
}
|
|
5945
5946
|
const isStr = (v) => typeof v === "string";
|
|
@@ -5953,14 +5954,14 @@ function validateSchemaYaml(frontmatterText) {
|
|
|
5953
5954
|
}
|
|
5954
5955
|
const problemType = doc.problem_type;
|
|
5955
5956
|
if (problemType !== undefined && !isStr(problemType)) {
|
|
5956
|
-
violations.push(violation8("medium", "compound.schema.invalid-problem-type", `problem_type "${String(problemType)}" must be a string
|
|
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"));
|
|
5957
5958
|
}
|
|
5958
5959
|
const problemTypeValid = isStr(problemType) && KNOWLEDGE_PROBLEM_TYPES.includes(problemType);
|
|
5959
5960
|
if (isStr(problemType) && !problemTypeValid) {
|
|
5960
|
-
violations.push(violation8("medium", "compound.schema.invalid-problem-type", `problem_type "${problemType}" is not one of the schema.yaml enum values (bug: build_error
|
|
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"));
|
|
5961
5962
|
}
|
|
5962
5963
|
if (doc.severity !== undefined && !isStr(doc.severity)) {
|
|
5963
|
-
violations.push(violation8("medium", "compound.schema.invalid-severity", `severity "${String(doc.severity)}" must be a string
|
|
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"));
|
|
5964
5965
|
}
|
|
5965
5966
|
if (isStr(doc.severity) && !KNOWLEDGE_SEVERITIES.includes(doc.severity)) {
|
|
5966
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"));
|
|
@@ -5968,7 +5969,7 @@ function validateSchemaYaml(frontmatterText) {
|
|
|
5968
5969
|
if (problemTypeValid && isStr(doc.category)) {
|
|
5969
5970
|
const expected = KNOWLEDGE_CATEGORY_MAP[problemType];
|
|
5970
5971
|
if (doc.category !== expected) {
|
|
5971
|
-
violations.push(violation8("medium", "compound.schema.category-mismatch", `category "${doc.category}" does not match problem_type "${problemType}"
|
|
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})`));
|
|
5972
5973
|
}
|
|
5973
5974
|
}
|
|
5974
5975
|
if (problemTypeValid) {
|
|
@@ -5999,7 +6000,7 @@ function validateSchemaYaml(frontmatterText) {
|
|
|
5999
6000
|
if (!Array.isArray(doc.tags)) {
|
|
6000
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"));
|
|
6001
6002
|
} else if (doc.tags.length > 8) {
|
|
6002
|
-
violations.push(violation8("low", "compound.schema.tags-too-many", `tags has ${doc.tags.length} entries
|
|
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"));
|
|
6003
6004
|
}
|
|
6004
6005
|
}
|
|
6005
6006
|
if (doc.last_updated !== undefined && (!isStr(doc.last_updated) || !DATE_RE3.test(doc.last_updated))) {
|
|
@@ -6047,7 +6048,7 @@ function assertIndexRows(knowledgeDir) {
|
|
|
6047
6048
|
const violations = [];
|
|
6048
6049
|
const readmePath = join8(knowledgeDir, "README.md");
|
|
6049
6050
|
if (!existsSync5(readmePath)) {
|
|
6050
|
-
violations.push(violation8("medium", "compound.index.missing-readme", `missing ${readmePath}
|
|
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"));
|
|
6051
6052
|
return { ok: false, violations };
|
|
6052
6053
|
}
|
|
6053
6054
|
const docs = collectKnowledgeDocs(knowledgeDir);
|
|
@@ -6086,7 +6087,7 @@ function scopeGuard(path2, allowedRoots) {
|
|
|
6086
6087
|
return {
|
|
6087
6088
|
ok: false,
|
|
6088
6089
|
violations: [
|
|
6089
|
-
violation8("medium", "compound.scope.outside", `path "${path2}" is outside the compound-refresh scope (allowed: ${allowedRoots.join(", ")})
|
|
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")
|
|
6090
6091
|
]
|
|
6091
6092
|
};
|
|
6092
6093
|
}
|
|
@@ -6133,16 +6134,36 @@ function findTemporaryMarkers(fileText) {
|
|
|
6133
6134
|
}
|
|
6134
6135
|
markers.push({ line: i + 1, text, removalPath });
|
|
6135
6136
|
if (removalPath === null) {
|
|
6136
|
-
violations.push(violation9("medium", "lint.temporary.no-removal-path", `temporary marker at line ${i + 1} records no removal path (plan/status artifact reference)
|
|
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"'));
|
|
6137
6138
|
}
|
|
6138
6139
|
}
|
|
6139
6140
|
return { ok: violations.length === 0, violations, markers };
|
|
6140
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
|
+
}
|
|
6141
6162
|
var TEST_FILE_PATH_RE = /[\w./-]+\.(?:test|spec)\.[a-z0-9]+/i;
|
|
6142
6163
|
var TEST_FILE_PHRASE_RE = /\btest files?\b/i;
|
|
6143
6164
|
var COMMAND_PROMPT_RE = /^\s*[$>]\s*\S/;
|
|
6144
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;
|
|
6145
|
-
var OUTPUT_TOKEN_RE = /[
|
|
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;
|
|
6146
6167
|
function assertSddTddTriple(reportText) {
|
|
6147
6168
|
const violations = [];
|
|
6148
6169
|
const lines = reportText.split(/\r?\n/);
|
|
@@ -6160,13 +6181,13 @@ function assertSddTddTriple(reportText) {
|
|
|
6160
6181
|
break;
|
|
6161
6182
|
}
|
|
6162
6183
|
if (!hasTests) {
|
|
6163
|
-
violations.push(violation9("medium", "lint.sdd-tdd.missing-tests", "task report carries no test file reference
|
|
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'));
|
|
6164
6185
|
}
|
|
6165
6186
|
if (!hasCommand) {
|
|
6166
|
-
violations.push(violation9("medium", "lint.sdd-tdd.missing-command", "task report carries no command
|
|
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'));
|
|
6167
6188
|
}
|
|
6168
6189
|
if (!hasOutput) {
|
|
6169
|
-
violations.push(violation9("medium", "lint.sdd-tdd.missing-output", "task report carries no output evidence
|
|
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'));
|
|
6170
6191
|
}
|
|
6171
6192
|
return { ok: violations.length === 0, violations };
|
|
6172
6193
|
}
|
|
@@ -6219,18 +6240,18 @@ function lintSkillFrontmatter(frontmatterText) {
|
|
|
6219
6240
|
const violations = [];
|
|
6220
6241
|
const fm = parseFrontmatter(frontmatterText);
|
|
6221
6242
|
if (fm === null) {
|
|
6222
|
-
violations.push(violation9("medium", "lint.frontmatter.missing", "no YAML frontmatter block found
|
|
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"));
|
|
6223
6244
|
return { ok: false, violations };
|
|
6224
6245
|
}
|
|
6225
6246
|
const name = fm.name ?? "";
|
|
6226
6247
|
if (name === "") {
|
|
6227
|
-
violations.push(violation9("medium", "lint.frontmatter.name.missing", "frontmatter `name` is missing
|
|
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"));
|
|
6228
6249
|
} else if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
|
|
6229
|
-
violations.push(violation9("medium", "lint.frontmatter.name.format", `frontmatter \`name\` must be lowercase-hyphen ("${name}")
|
|
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`"));
|
|
6230
6251
|
}
|
|
6231
6252
|
const description = fm.description ?? "";
|
|
6232
6253
|
if (description === "") {
|
|
6233
|
-
violations.push(violation9("medium", "lint.frontmatter.description.missing", "frontmatter `description` is missing
|
|
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)"));
|
|
6234
6255
|
} else {
|
|
6235
6256
|
const stripped = description.replace(/`[^`]*`/g, " ").replace(/'[^']*'/g, " ").replace(/"[^"]*"/g, " ");
|
|
6236
6257
|
let pronoun = null;
|
|
@@ -6241,15 +6262,15 @@ function lintSkillFrontmatter(frontmatterText) {
|
|
|
6241
6262
|
break;
|
|
6242
6263
|
}
|
|
6243
6264
|
if (pronoun !== null) {
|
|
6244
|
-
violations.push(violation9("low", "lint.frontmatter.description.person", `description uses first/second-person pronoun "${pronoun[0]}"
|
|
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"'));
|
|
6245
6266
|
}
|
|
6246
6267
|
const start = description.trim().replace(/^[*_#>]+/, "").replace(/^["'`]+/, "").trim();
|
|
6247
6268
|
if (WORKFLOW_VERB_START_RE.test(start)) {
|
|
6248
|
-
violations.push(violation9("low", "lint.frontmatter.description.workflow", 'description reads as a workflow summary ("Explains/Describes/Covers
|
|
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"));
|
|
6249
6270
|
} else {
|
|
6250
6271
|
const words = description.trim().split(/\s+/).filter(Boolean).length;
|
|
6251
6272
|
if (words > DESCRIPTION_MAX_WORDS) {
|
|
6252
|
-
violations.push(violation9("low", "lint.frontmatter.description.workflow", `description is ${words} words
|
|
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"));
|
|
6253
6274
|
}
|
|
6254
6275
|
}
|
|
6255
6276
|
}
|
|
@@ -6296,7 +6317,7 @@ function lintStrategySections(docText) {
|
|
|
6296
6317
|
}
|
|
6297
6318
|
for (const required of REQUIRED_STRATEGY_SECTIONS) {
|
|
6298
6319
|
if (!headings.has(required.toLowerCase())) {
|
|
6299
|
-
violations.push(violation9("medium", "lint.strategy.missing-section", `missing required section "${required}" (mstar-strategy
|
|
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"));
|
|
6300
6321
|
}
|
|
6301
6322
|
}
|
|
6302
6323
|
return { ok: violations.length === 0, violations };
|
|
@@ -6337,7 +6358,7 @@ function lintFiveQuestion(bodyText) {
|
|
|
6337
6358
|
const label = section.label.toLowerCase();
|
|
6338
6359
|
const covered = headings.some((heading) => heading.includes(label));
|
|
6339
6360
|
if (!covered) {
|
|
6340
|
-
violations.push(violation11("low", `skill-authoring.five-question.${section.key}`, `body does not answer "${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}`));
|
|
6341
6362
|
}
|
|
6342
6363
|
}
|
|
6343
6364
|
return { ok: violations.length === 0, violations };
|
|
@@ -6434,7 +6455,7 @@ var GITIGNORE_SNIPPET2 = `# Morning Star harness (.mstar/)
|
|
|
6434
6455
|
!.mstar/specs/
|
|
6435
6456
|
!.mstar/specs/**
|
|
6436
6457
|
`;
|
|
6437
|
-
var GITIGNORE_SNIPPET_AGENTS2 = `# Morning Star harness (.agents/)
|
|
6458
|
+
var GITIGNORE_SNIPPET_AGENTS2 = `# Morning Star harness (.agents/) \u2014 legacy
|
|
6438
6459
|
# Default-ignore everything under .agents/, then re-include the tracked results.
|
|
6439
6460
|
.agents/**
|
|
6440
6461
|
!.agents/AGENTS.md
|
|
@@ -6472,7 +6493,7 @@ function validateExecutionLease(lease) {
|
|
|
6472
6493
|
return {
|
|
6473
6494
|
ok: false,
|
|
6474
6495
|
violations: [
|
|
6475
|
-
violation("high", "lease.execution-lease.invalid", "execution_lease must be an object
|
|
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")
|
|
6476
6497
|
]
|
|
6477
6498
|
};
|
|
6478
6499
|
}
|
|
@@ -6487,11 +6508,11 @@ function validateExecutionLease(lease) {
|
|
|
6487
6508
|
} else if (typeof lease.worktree_path !== "string" || lease.worktree_path.trim() === "") {
|
|
6488
6509
|
violations.push(violation("medium", "lease.execution-lease.invalid-worktree-path", "worktree_path must be a non-empty string"));
|
|
6489
6510
|
} else if (!isAbsolute22(lease.worktree_path)) {
|
|
6490
|
-
violations.push(violation("medium", "lease.execution-lease.invalid-worktree-path", "worktree_path must be an absolute 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)"));
|
|
6491
6512
|
}
|
|
6492
6513
|
validateNonEmptyString(violations, lease.working_branch, "working_branch", "lease.execution-lease.missing-working-branch", "lease.execution-lease.invalid-working-branch");
|
|
6493
6514
|
if (lease.session_label !== undefined && typeof lease.session_label !== "string") {
|
|
6494
|
-
violations.push(violation("medium", "lease.execution-lease.invalid-session-label", "session_label must be a string (display only
|
|
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)"));
|
|
6495
6516
|
}
|
|
6496
6517
|
return { ok: violations.length === 0, violations };
|
|
6497
6518
|
}
|
|
@@ -6508,7 +6529,7 @@ function verifyPlanExecutionLease(row, planId) {
|
|
|
6508
6529
|
return {
|
|
6509
6530
|
ok: false,
|
|
6510
6531
|
violations: [
|
|
6511
|
-
violation("high", "lease.verify.orphan", "plan is InProgress without an execution_lease
|
|
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)")
|
|
6512
6533
|
]
|
|
6513
6534
|
};
|
|
6514
6535
|
}
|
|
@@ -6521,9 +6542,9 @@ function verifyPlanExecutionLease(row, planId) {
|
|
|
6521
6542
|
}
|
|
6522
6543
|
const violations = [];
|
|
6523
6544
|
if (rowLease !== undefined && metadataLease !== undefined) {
|
|
6524
|
-
violations.push(violation("high", "lease.verify.dual-write", "execution_lease present in BOTH plans[].execution_lease (SSOT) and plans[].metadata.execution_lease
|
|
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"));
|
|
6525
6546
|
} else if (rowLease === undefined) {
|
|
6526
|
-
violations.push(violation("high", "lease.verify.non-ssot-location", "execution_lease found only under plans[].metadata.execution_lease
|
|
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)"));
|
|
6527
6548
|
}
|
|
6528
6549
|
violations.push(...validateExecutionLease(lease).violations);
|
|
6529
6550
|
return { ok: violations.length === 0, violations, lease };
|
|
@@ -6701,11 +6722,11 @@ function validateManifest(manifest, errors2, warnings) {
|
|
|
6701
6722
|
}
|
|
6702
6723
|
if (doc.extensions !== undefined) {
|
|
6703
6724
|
if (!isPlainObject6(doc.extensions)) {
|
|
6704
|
-
warnings.push('plugin.json: "extensions" is not an object
|
|
6725
|
+
warnings.push('plugin.json: "extensions" is not an object \u2014 ignored');
|
|
6705
6726
|
} else {
|
|
6706
6727
|
for (const [namespace, value] of Object.entries(doc.extensions)) {
|
|
6707
6728
|
if (!isPlainObject6(value)) {
|
|
6708
|
-
warnings.push(`plugin.json: "extensions.${namespace}" is not an object
|
|
6729
|
+
warnings.push(`plugin.json: "extensions.${namespace}" is not an object \u2014 ignored`);
|
|
6709
6730
|
}
|
|
6710
6731
|
}
|
|
6711
6732
|
}
|
|
@@ -6766,7 +6787,7 @@ function validateMcpServer(name, entry, errors2) {
|
|
|
6766
6787
|
} else {
|
|
6767
6788
|
const remainder = stripMcpPathPrefix(server.cwd);
|
|
6768
6789
|
if (remainder === null) {
|
|
6769
|
-
errors2.push(`${prefix}: "cwd" must be "
|
|
6790
|
+
errors2.push(`${prefix}: "cwd" must be "./\u2026", "${"${PLUGIN_ROOT}"}\u2026", or "${"${PLUGIN_DATA}"}\u2026"`);
|
|
6770
6791
|
} else if (escapesPluginRoot(remainder)) {
|
|
6771
6792
|
errors2.push(`${prefix}: "cwd" must remain within the plugin root (got "${server.cwd}")`);
|
|
6772
6793
|
}
|
|
@@ -7341,7 +7362,7 @@ function ensureIterationSkillLinks(dryRun) {
|
|
|
7341
7362
|
}
|
|
7342
7363
|
const gitignoreEntries = CODEX_PROJECT_COMMAND_NAMES.map(iterationSkillGitignoreEntry);
|
|
7343
7364
|
notes.push(...appendGitignore(projectRoot, gitignoreEntries, dryRun));
|
|
7344
|
-
notes.push("Installed Codex project-scoped command skills under .agents/skills/ (iteration-start, iteration-drive, iteration-loop, codebase-audit)
|
|
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.");
|
|
7345
7366
|
return notes;
|
|
7346
7367
|
}
|
|
7347
7368
|
function validateIterationSkillLinks() {
|
|
@@ -7657,7 +7678,7 @@ function runInit2(scope, dryRun) {
|
|
|
7657
7678
|
}
|
|
7658
7679
|
notes.push("Verify with: omp plugin list");
|
|
7659
7680
|
notes.push("Enter PM with /skill:pm ; commands: /iteration-start /iteration-drive /iteration-loop /codebase-audit");
|
|
7660
|
-
notes.push(`Host adapter: mstar-host
|
|
7681
|
+
notes.push(`Host adapter: mstar-host \u2192 references/omp.md (skill://mstar-host/references/omp.md)`);
|
|
7661
7682
|
notes.push(`Alternate install without CLI link: omp plugin install ${REPO_URL.replace("https://github.com/", "github:").replace(/\.git$/, "")}`);
|
|
7662
7683
|
return {
|
|
7663
7684
|
location: HARNESS_REPO_PATH,
|
|
@@ -7782,10 +7803,10 @@ function getDoctorWarnings(config) {
|
|
|
7782
7803
|
const hasNpm = strings.some(isMstarHarnessOpencodePlugin);
|
|
7783
7804
|
const hasLegacy = strings.some(isLegacyMorningStarGitPlugin);
|
|
7784
7805
|
if (hasLegacy && !hasNpm) {
|
|
7785
|
-
warnings.push("Plugin list uses legacy `morning-star@git
|
|
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`.");
|
|
7786
7807
|
}
|
|
7787
7808
|
if (hasLegacy && hasNpm) {
|
|
7788
|
-
warnings.push("Both legacy `morning-star@git
|
|
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.");
|
|
7789
7810
|
}
|
|
7790
7811
|
const agent = ensureObject(config.agent);
|
|
7791
7812
|
const missingModels = ALL_ROLES.filter((roleId) => {
|
|
@@ -7793,7 +7814,7 @@ function getDoctorWarnings(config) {
|
|
|
7793
7814
|
return typeof role.model !== "string" || !role.model.trim();
|
|
7794
7815
|
});
|
|
7795
7816
|
if (missingModels.length) {
|
|
7796
|
-
warnings.push(`${missingModels.length} role(s) have no explicit agent.<role>.model
|
|
7817
|
+
warnings.push(`${missingModels.length} role(s) have no explicit agent.<role>.model \u2014 OpenCode default model will be used (recommended for fastest setup).`);
|
|
7797
7818
|
}
|
|
7798
7819
|
return warnings;
|
|
7799
7820
|
}
|
|
@@ -7981,7 +8002,7 @@ function runInit3(scope, dryRun) {
|
|
|
7981
8002
|
if (!dryRun)
|
|
7982
8003
|
writeJson2(KNOWN_MARKETPLACES_PATH, knownNext);
|
|
7983
8004
|
notes.push(`Registered ${MARKETPLACE_ID} marketplace in ${KNOWN_MARKETPLACES_PATH}`);
|
|
7984
|
-
notes.push(`Then in ZCode: Settings
|
|
8005
|
+
notes.push(`Then in ZCode: Settings \u2192 Plugin Management \u2192 Discover \u2192 install ${PLUGIN_NAME} from the ${MARKETPLACE_ID} marketplace.`);
|
|
7985
8006
|
return {
|
|
7986
8007
|
location: KNOWN_MARKETPLACES_PATH,
|
|
7987
8008
|
notes
|
|
@@ -8950,7 +8971,7 @@ hostCommand.command("detect").description("Detect the active host from --signals
|
|
|
8950
8971
|
}
|
|
8951
8972
|
});
|
|
8952
8973
|
var skillCommand = program2.command("skill").description("skill-authoring lints (engine-backed)");
|
|
8953
|
-
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) => {
|
|
8954
8975
|
try {
|
|
8955
8976
|
if (!skillDir)
|
|
8956
8977
|
throw new SddScriptError("usage: skill lint <skill-dir>", 2);
|
|
@@ -8965,6 +8986,19 @@ skillCommand.command("lint").description("Lint <skill-dir>/SKILL.md: frontmatter
|
|
|
8965
8986
|
const fiveQuestion = lintFiveQuestion(stripFrontmatter(text));
|
|
8966
8987
|
printChecklist("skill lint (five questions)", fiveQuestion);
|
|
8967
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);
|
|
8968
9002
|
if (violations.length > 0)
|
|
8969
9003
|
process.exitCode = 1;
|
|
8970
9004
|
} catch (error) {
|