返回 DeepSeek-Reasonix
full_verification.go
根目录 / internal / evidence / full_verification.go
1 package evidence
2
3 import (
4 "path/filepath"
5 "strings"
6
7 "reasonix/internal/shellparse"
8 "reasonix/internal/shellsafe"
9 )
10
11 // IsFullVerificationCommand reports whether a recognized verifier clearly
12 // covers the current project rather than one named package, file, or test.
13 // Project-declared checks remain authoritative at the task-contract layer;
14 // this conservative fallback is for repositories without declared checks.
15 func IsFullVerificationCommand(command string) bool {
16 if !bashCommandIsVerification(command) {
17 return false
18 }
19 if masks, ok := shellparse.CanMaskEarlierFailure(command); !ok || masks {
20 return false
21 }
22 segments, _, ok := shellparse.SplitTopLevel(command)
23 if !ok {
24 return false
25 }
26 for _, segment := range segments {
27 normalized, safe := shellsafe.NormalizeBashSafeRedirectsForMatch(segment)
28 if !safe {
29 continue
30 }
31 fields, ok := bashStaticArgv(normalized)
32 if ok && fullVerificationArgv(fields) {
33 return true
34 }
35 }
36 return false
37 }
38
39 func fullVerificationArgv(fields []string) bool {
40 if !bashSegmentIsVerification(fields) || len(fields) == 0 {
41 return false
42 }
43 base := strings.ToLower(filepath.Base(fields[0]))
44 args := fields[1:]
45 switch base {
46 case "go":
47 return fullGoVerification(args)
48 case "pytest", "py.test":
49 return onlyBroadVerificationTargets(args, ".", "./", "test", "test/", "tests", "tests/")
50 case "gotestsum", "staticcheck":
51 return len(args) == 0 || slicesContainFold(args, "./...")
52 case "golangci-lint":
53 return onlyBroadVerificationTargets(args)
54 case "tsc":
55 return onlyBroadVerificationTargets(args)
56 case "mypy":
57 return onlyBroadVerificationTargets(args, ".", "./", "src", "src/")
58 case "npm", "pnpm", "yarn", "bun":
59 return fullScriptVerification(args)
60 case "cargo":
61 return len(args) > 0 && onlyBroadVerificationTargets(args[1:])
62 case "npx":
63 return fullNpxVerification(args)
64 case "node":
65 return len(args) > 0 && args[0] == "--test" && onlyBroadVerificationTargets(args[1:])
66 case "make", "just":
67 return len(args) == 1
68 case "python", "python3":
69 return len(args) > 1 && args[0] == "-m" && onlyBroadVerificationTargets(args[2:])
70 case "dotnet":
71 return len(args) > 0 && args[0] == "test" && onlyBroadVerificationTargets(args[1:])
72 case "swift":
73 return len(args) > 0 && args[0] == "test" && onlyBroadVerificationTargets(args[1:])
74 case "mvn", "mvnw", "gradle", "gradlew":
75 return buildToolVerificationIsFull(args)
76 default:
77 return false
78 }
79 }
80
81 func fullGoVerification(args []string) bool {
82 if len(args) < 2 || (args[0] != "test" && args[0] != "vet") || !slicesContainFold(args[1:], "./...") {
83 return false
84 }
85 if args[0] == "vet" {
86 return true
87 }
88 for _, arg := range args[1:] {
89 name := strings.TrimPrefix(strings.ToLower(arg), "-test.")
90 name = strings.TrimLeft(name, "-")
91 if before, _, ok := strings.Cut(name, "="); ok {
92 name = before
93 }
94 switch name {
95 case "run", "bench", "list", "skip":
96 return false
97 case "count":
98 if strings.HasSuffix(strings.ToLower(arg), "=0") {
99 return false
100 }
101 }
102 }
103 return true
104 }
105
106 func fullScriptVerification(args []string) bool {
107 if len(args) == 0 {
108 return false
109 }
110 start := 1
111 if args[0] == "run" {
112 if len(args) < 2 {
113 return false
114 }
115 start = 2
116 }
117 return onlyBroadVerificationTargets(args[start:])
118 }
119
120 func fullNpxVerification(args []string) bool {
121 if len(args) == 0 {
122 return false
123 }
124 runner, ok := npxRunnerName(args[0])
125 if !ok {
126 return false
127 }
128 switch runner {
129 case "tsc":
130 return tscSegmentIsVerification(args[1:]) && onlyBroadVerificationTargets(args[1:])
131 case "vitest", "jest", "mocha", "ava", "eslint", "prettier":
132 return onlyBroadVerificationTargets(args[1:], ".", "./")
133 default:
134 return false
135 }
136 }
137
138 func buildToolVerificationIsFull(args []string) bool {
139 seenTask := false
140 for _, arg := range args {
141 if strings.HasPrefix(arg, "-") {
142 continue
143 }
144 switch strings.ToLower(arg) {
145 case "test", "check", "verify":
146 seenTask = true
147 default:
148 return false
149 }
150 }
151 return seenTask
152 }
153
154 func onlyBroadVerificationTargets(args []string, broad ...string) bool {
155 for _, arg := range args {
156 if strings.HasPrefix(arg, "-") {
157 continue
158 }
159 if !slicesContainFold(broad, arg) {
160 return false
161 }
162 }
163 return true
164 }
165
166 func slicesContainFold(items []string, want string) bool {
167 for _, item := range items {
168 if strings.EqualFold(item, want) {
169 return true
170 }
171 }
172 return false
173 }
174
174 lines GO