Keep item revisions and edge decisions as records, and refuse to treat a missing index as no impact. Co-authored-by: Cursor <cursoragent@cursor.com>
75 lines
1.9 KiB
Go
75 lines
1.9 KiB
Go
package traceimpact
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
explicitKeyRe = regexp.MustCompile(`\b((?:UR|RS|REQ|TC|RF|RP|DI)-\d+)\b`)
|
|
untypedKeyRe = regexp.MustCompile(`\b([A-Z]{2,10}-\d+)\b`)
|
|
)
|
|
|
|
// ExtractedKey is a business key found in markdown.
|
|
type ExtractedKey struct {
|
|
Key string
|
|
Kind string
|
|
Explicit bool
|
|
Line string
|
|
}
|
|
|
|
// ExtractKeys returns explicit UR/RS/REQ/TC-style keys and untyped ID mentions.
|
|
// Untyped mentions stay candidates and never become confirmed edges by themselves.
|
|
func ExtractKeys(markdown string) []ExtractedKey {
|
|
lines := strings.Split(markdown, "\n")
|
|
var out []ExtractedKey
|
|
seen := map[string]bool{}
|
|
for _, line := range lines {
|
|
for _, m := range explicitKeyRe.FindAllStringSubmatch(line, -1) {
|
|
key := m[1]
|
|
if seen["e:"+key] {
|
|
continue
|
|
}
|
|
seen["e:"+key] = true
|
|
kind := KindRequirement
|
|
if strings.HasPrefix(key, "TC-") {
|
|
kind = KindTest
|
|
}
|
|
out = append(out, ExtractedKey{Key: key, Kind: kind, Explicit: true, Line: line})
|
|
}
|
|
for _, m := range untypedKeyRe.FindAllStringSubmatch(line, -1) {
|
|
key := m[1]
|
|
if explicitKeyRe.MatchString(key) || seen["u:"+key] || seen["e:"+key] {
|
|
continue
|
|
}
|
|
seen["u:"+key] = true
|
|
out = append(out, ExtractedKey{Key: key, Kind: KindRequirement, Explicit: false, Line: line})
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// ExplicitPairsOnLine returns ordered pairs of explicit keys that share a line.
|
|
func ExplicitPairsOnLine(markdown string) [][2]string {
|
|
var pairs [][2]string
|
|
seen := map[string]bool{}
|
|
for _, line := range strings.Split(markdown, "\n") {
|
|
found := explicitKeyRe.FindAllStringSubmatch(line, -1)
|
|
if len(found) < 2 {
|
|
continue
|
|
}
|
|
for i := 0; i < len(found); i++ {
|
|
for j := i + 1; j < len(found); j++ {
|
|
a, b := found[i][1], found[j][1]
|
|
k := a + ">" + b
|
|
if seen[k] {
|
|
continue
|
|
}
|
|
seen[k] = true
|
|
pairs = append(pairs, [2]string{a, b})
|
|
}
|
|
}
|
|
}
|
|
return pairs
|
|
}
|