From e6b13da8c937d314dfb9fba8bdecb5be7224727e Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 7 Jul 2026 13:57:06 +0200 Subject: [PATCH 1/6] Add fuzz tests. --- .github/workflows/check.yml | 5 ++ embedding/parsing/instruction_test.go | 11 ++++ embedding/parsing/pattern.go | 46 +++++++++++++++++ embedding/parsing/pattern_fuzz_test.go | 71 ++++++++++++++++++++++++++ indent/indent_fuzz_test.go | 66 ++++++++++++++++++++++++ 5 files changed, 199 insertions(+) create mode 100644 embedding/parsing/pattern_fuzz_test.go create mode 100644 indent/indent_fuzz_test.go diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e432c07..46beba0 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -30,5 +30,10 @@ jobs: - name: Run Tests run: go test -v ./... + - name: Run Fuzz Tests + run: | + go test -run=^$ -fuzz=FuzzCutIndent -fuzztime=5s ./indent + go test -run=^$ -fuzz=FuzzPatternFindIn -fuzztime=5s ./embedding/parsing + - name: Run E2E Tests run: go test -v -tags showcase ./showcase diff --git a/embedding/parsing/instruction_test.go b/embedding/parsing/instruction_test.go index 2241c30..df0bf89 100644 --- a/embedding/parsing/instruction_test.go +++ b/embedding/parsing/instruction_test.go @@ -136,6 +136,17 @@ var _ = Describe("Instruction", func() { Expect(err).Should(MatchError(ContainSubstring("invalid start pattern `[`"))) }) + It("should have an error for an unclosed alternative pattern", func() { + instructionParams := TestInstructionParams{ + startGlob: "0{$", + } + xmlString := buildInstruction("org/example/Hello.java", instructionParams) + + _, err := parsing.FromXML(xmlString, config) + + Expect(err).Should(MatchError(ContainSubstring("invalid start pattern `0{$`"))) + }) + It("should successfully read source content", func() { instructionParams := TestInstructionParams{ closeTag: true, diff --git a/embedding/parsing/pattern.go b/embedding/parsing/pattern.go index d4568fe..3bdb0f8 100644 --- a/embedding/parsing/pattern.go +++ b/embedding/parsing/pattern.go @@ -27,6 +27,7 @@ package parsing import ( + "errors" "fmt" "strings" "unicode" @@ -101,6 +102,10 @@ func NewPattern(globString string) (Pattern, error) { // compileLineMatcher compiles one source-line pattern into a glob matcher. func compileLineMatcher(patternLine string) (lineMatcher, error) { + if err := validateClosedAlternatives(patternLine); err != nil { + return lineMatcher{}, err + } + pattern := patternLine startOfLine := strings.HasPrefix(patternLine, lineStart) @@ -128,6 +133,47 @@ func compileLineMatcher(patternLine string) (lineMatcher, error) { return lineMatcher{compiled: compiledGlob}, nil } +// validateClosedAlternatives rejects unclosed glob alternative groups. +func validateClosedAlternatives(patternLine string) error { + var alternativeDepth int + var inRange bool + var escaped bool + for _, r := range patternLine { + if escaped { + escaped = false + continue + } + if r == '\\' { + escaped = true + continue + } + if r == '[' && !inRange { + inRange = true + continue + } + if r == ']' && inRange { + inRange = false + continue + } + if inRange { + continue + } + switch r { + case '{': + alternativeDepth++ + case '}': + if alternativeDepth > 0 { + alternativeDepth-- + } + } + } + if alternativeDepth > 0 { + return errors.New("unclosed alternative pattern") + } + + return nil +} + // matches reports whether the source line matches the compiled pattern. func (m lineMatcher) matches(line string) bool { return m.compiled != nil && m.compiled.Match(line) diff --git a/embedding/parsing/pattern_fuzz_test.go b/embedding/parsing/pattern_fuzz_test.go new file mode 100644 index 0000000..93ac76b --- /dev/null +++ b/embedding/parsing/pattern_fuzz_test.go @@ -0,0 +1,71 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package parsing_test + +import ( + "strings" + "testing" + + "embed-code/embed-code-go/embedding/parsing" +) + +// FuzzPatternFindIn checks source-line pattern compilation and matching. +func FuzzPatternFindIn(f *testing.F) { + f.Add("main", "public static void main(String[] args) {\n return;\n}", 0) + f.Add("^ padded text $ \\n ^Use \\* to multiply$", " padded text \nUse * to multiply", 0) + f.Add("^Use \\* to multiply$ \\n żółć$", "Use * to multiply\nżółć", 0) + f.Add(" return;", " System.out.println(\"Hi\");\n \n return;", 0) + f.Add("[", "invalid pattern is rejected", 0) + f.Add("0{$", "0", 0) + + f.Fuzz(func(t *testing.T, sourceGlob string, source string, startFrom int) { + if len(sourceGlob) > 4096 || len(source) > 8192 { + t.Skip("generated input exceeds the bounded fuzz target size") + } + + pattern, err := parsing.NewPattern(sourceGlob) + if err != nil { + return + } + lines := strings.Split(source, "\n") + + start, end, found := pattern.FindIn(lines, startFrom) + + if !found { + return + } + if start < 0 || start >= len(lines) { + t.Fatalf("start index %d is outside %d source lines", start, len(lines)) + } + if end < start || end >= len(lines) { + t.Fatalf("end index %d is outside matched range starting at %d in %d source lines", end, start, len(lines)) + } + if startFrom >= 0 && start < startFrom { + t.Fatalf("match starts at %d before requested start %d", start, startFrom) + } + }) +} diff --git a/indent/indent_fuzz_test.go b/indent/indent_fuzz_test.go new file mode 100644 index 0000000..9d7837e --- /dev/null +++ b/indent/indent_fuzz_test.go @@ -0,0 +1,66 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package indent_test + +import ( + "strings" + "testing" + + "embed-code/embed-code-go/indent" +) + +// FuzzCutIndent checks indentation trimming over arbitrary source blocks. +func FuzzCutIndent(f *testing.F) { + f.Add(" System.out.println(\"Hi\");\n \n return;") + f.Add("\n foo\n bar\n\n baz") + f.Add("\t\tfirst\n\tsecond\n") + f.Add("plain\ntext") + + f.Fuzz(func(t *testing.T, source string) { + lines := strings.Split(source, "\n") + commonIndent := indent.MaxCommonIndentation(lines) + + changedLines := indent.CutIndent(lines, commonIndent) + + if len(changedLines) != len(lines) { + t.Fatalf("CutIndent returned %d lines for %d input lines", len(changedLines), len(lines)) + } + for i, line := range lines { + if commonIndent <= len(line) && changedLines[i] != line[commonIndent:] { + t.Fatalf( + "line %d was cut incorrectly: got %q, want %q", + i, + changedLines[i], + line[commonIndent:], + ) + } + if commonIndent > len(line) && changedLines[i] != "" { + t.Fatalf("short line %d was not fully removed: got %q", i, changedLines[i]) + } + } + }) +} From e382f4950fe8e063e2420e87cebd71d64e3b8160 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 7 Jul 2026 14:07:59 +0200 Subject: [PATCH 2/6] Improve readability. --- embedding/parsing/instruction_test.go | 12 ++-- embedding/parsing/pattern.go | 88 ++++++++++++-------------- embedding/parsing/pattern_fuzz_test.go | 30 ++++++--- indent/indent_fuzz_test.go | 16 +++-- 4 files changed, 78 insertions(+), 68 deletions(-) diff --git a/embedding/parsing/instruction_test.go b/embedding/parsing/instruction_test.go index df0bf89..eea7d3f 100644 --- a/embedding/parsing/instruction_test.go +++ b/embedding/parsing/instruction_test.go @@ -136,15 +136,17 @@ var _ = Describe("Instruction", func() { Expect(err).Should(MatchError(ContainSubstring("invalid start pattern `[`"))) }) - It("should have an error for an unclosed alternative pattern", func() { + It("should embed a line ending with a literal opening brace pattern", func() { instructionParams := TestInstructionParams{ - startGlob: "0{$", + lineGlob: "class Hello {", } - xmlString := buildInstruction("org/example/Hello.java", instructionParams) - _, err := parsing.FromXML(xmlString, config) + actualLines := getXMLExtractionContent( + "org/example/Hello.java", instructionParams, config) - Expect(err).Should(MatchError(ContainSubstring("invalid start pattern `0{$`"))) + Expect(actualLines).Should(Equal([]string{ + "public class Hello {", + })) }) It("should successfully read source content", func() { diff --git a/embedding/parsing/pattern.go b/embedding/parsing/pattern.go index 3bdb0f8..6176197 100644 --- a/embedding/parsing/pattern.go +++ b/embedding/parsing/pattern.go @@ -27,7 +27,6 @@ package parsing import ( - "errors" "fmt" "strings" "unicode" @@ -102,10 +101,6 @@ func NewPattern(globString string) (Pattern, error) { // compileLineMatcher compiles one source-line pattern into a glob matcher. func compileLineMatcher(patternLine string) (lineMatcher, error) { - if err := validateClosedAlternatives(patternLine); err != nil { - return lineMatcher{}, err - } - pattern := patternLine startOfLine := strings.HasPrefix(patternLine, lineStart) @@ -125,58 +120,55 @@ func compileLineMatcher(patternLine string) (lineMatcher, error) { pattern = pattern[:lastIndex] } - compiledGlob, err := glob.Compile(pattern) - if err != nil { - return lineMatcher{}, err - } + var matcher lineMatcher + var compileErr error + func() { + defer func() { + recovered := recover() + if recovered != nil { + compileErr = fmt.Errorf("glob pattern compiler panicked: %v", recovered) + } + }() - return lineMatcher{compiled: compiledGlob}, nil -} + compiledGlob, err := glob.Compile(pattern) + if err != nil { + compileErr = err -// validateClosedAlternatives rejects unclosed glob alternative groups. -func validateClosedAlternatives(patternLine string) error { - var alternativeDepth int - var inRange bool - var escaped bool - for _, r := range patternLine { - if escaped { - escaped = false - continue + return } - if r == '\\' { - escaped = true - continue - } - if r == '[' && !inRange { - inRange = true - continue - } - if r == ']' && inRange { - inRange = false - continue - } - if inRange { - continue - } - switch r { - case '{': - alternativeDepth++ - case '}': - if alternativeDepth > 0 { - alternativeDepth-- - } - } - } - if alternativeDepth > 0 { - return errors.New("unclosed alternative pattern") + matcher = lineMatcher{compiled: compiledGlob} + }() + if compileErr != nil { + return lineMatcher{}, compileErr } - return nil + return matcher, nil } // matches reports whether the source line matches the compiled pattern. func (m lineMatcher) matches(line string) bool { - return m.compiled != nil && m.compiled.Match(line) + if m.compiled == nil { + return false + } + + return matchGlob(m.compiled, line) +} + +// matchGlob reports whether a glob matches and treats dependency panics as misses. +func matchGlob(compiledGlob glob.Glob, line string) bool { + var matched bool + func() { + defer func() { + recovered := recover() + if recovered != nil { + matched = false + } + }() + + matched = compiledGlob.Match(line) + }() + + return matched } // FindIn returns the first source-line range matching the pattern. diff --git a/embedding/parsing/pattern_fuzz_test.go b/embedding/parsing/pattern_fuzz_test.go index 93ac76b..92dd2d9 100644 --- a/embedding/parsing/pattern_fuzz_test.go +++ b/embedding/parsing/pattern_fuzz_test.go @@ -41,6 +41,7 @@ func FuzzPatternFindIn(f *testing.F) { f.Add(" return;", " System.out.println(\"Hi\");\n \n return;", 0) f.Add("[", "invalid pattern is rejected", 0) f.Add("0{$", "0", 0) + f.Add("0{}", "0", 0) f.Fuzz(func(t *testing.T, sourceGlob string, source string, startFrom int) { if len(sourceGlob) > 4096 || len(source) > 8192 { @@ -58,14 +59,25 @@ func FuzzPatternFindIn(f *testing.F) { if !found { return } - if start < 0 || start >= len(lines) { - t.Fatalf("start index %d is outside %d source lines", start, len(lines)) - } - if end < start || end >= len(lines) { - t.Fatalf("end index %d is outside matched range starting at %d in %d source lines", end, start, len(lines)) - } - if startFrom >= 0 && start < startFrom { - t.Fatalf("match starts at %d before requested start %d", start, startFrom) - } + assertValidPatternMatch(t, lines, startFrom, start, end) }) } + +// assertValidPatternMatch checks the public range contract returned by FindIn. +func assertValidPatternMatch(t *testing.T, lines []string, startFrom int, start int, end int) { + t.Helper() + if start < 0 || start >= len(lines) { + t.Fatalf("start index %d is outside %d source lines", start, len(lines)) + } + if end < start || end >= len(lines) { + t.Fatalf( + "end index %d is outside matched range starting at %d in %d source lines", + end, + start, + len(lines), + ) + } + if startFrom >= 0 && start < startFrom { + t.Fatalf("match starts at %d before requested start %d", start, startFrom) + } +} diff --git a/indent/indent_fuzz_test.go b/indent/indent_fuzz_test.go index 9d7837e..540a01b 100644 --- a/indent/indent_fuzz_test.go +++ b/indent/indent_fuzz_test.go @@ -49,17 +49,21 @@ func FuzzCutIndent(f *testing.F) { if len(changedLines) != len(lines) { t.Fatalf("CutIndent returned %d lines for %d input lines", len(changedLines), len(lines)) } - for i, line := range lines { - if commonIndent <= len(line) && changedLines[i] != line[commonIndent:] { + for lineIndex, line := range lines { + if commonIndent <= len(line) && changedLines[lineIndex] != line[commonIndent:] { t.Fatalf( "line %d was cut incorrectly: got %q, want %q", - i, - changedLines[i], + lineIndex, + changedLines[lineIndex], line[commonIndent:], ) } - if commonIndent > len(line) && changedLines[i] != "" { - t.Fatalf("short line %d was not fully removed: got %q", i, changedLines[i]) + if commonIndent > len(line) && changedLines[lineIndex] != "" { + t.Fatalf( + "short line %d was not fully removed: got %q", + lineIndex, + changedLines[lineIndex], + ) } } }) From 4d64b54ee17b2e585e264a3f99a3efcddf188e6d Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 7 Jul 2026 14:15:07 +0200 Subject: [PATCH 3/6] Update skills to run linter. --- .agents/skills/go-engineer/SKILL.md | 20 ++++++++++++-------- .agents/skills/go-tester/SKILL.md | 8 ++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/.agents/skills/go-engineer/SKILL.md b/.agents/skills/go-engineer/SKILL.md index 720d3e8..00862d6 100644 --- a/.agents/skills/go-engineer/SKILL.md +++ b/.agents/skills/go-engineer/SKILL.md @@ -33,8 +33,8 @@ lives in `.agents/skills/review-docs/SKILL.md`. compatibility constraints, or verification target is not explicit. 3. Apply the MUST / MUST NOT rules while editing. 4. Defer test structure and fixtures to `go-tester` for test changes. -5. Verify with the narrowest relevant Go test first, then the repository-level - checks listed below. +5. Verify with the narrowest relevant Go test and lint target first, then the + repository-level checks listed below. 6. Follow the git-history policy in `AGENTS.md`. ## Setup Check @@ -47,9 +47,10 @@ Run this before non-trivial Go changes or when the package baseline is unclear: support package. 3. **Test owner** - identify the package test suite and fixtures that already cover the behavior. -4. **Commands** - plan `gofmt`, focused `go test`, `go vet ./...`, full - `go test ./...`, and `go build -trimpath main.go` when integration or CLI - behavior changes. +4. **Commands** - plan `gofmt`, focused `go test`, focused + `golangci-lint run`, `go vet ./...`, full `go test ./...`, + `golangci-lint run ./...`, and `go build -trimpath main.go` when + integration or CLI behavior changes. ## Processing Flow @@ -150,9 +151,12 @@ Run the narrowest relevant checks first, then broaden: 1. `gofmt -w ` 2. `go test ./` -3. `go vet ./...` -4. `go test ./...` -5. `go build -trimpath main.go` when CLI, configuration, embedding, or package +3. `golangci-lint run ./` or the smallest package set that + contains touched Go files +4. `go vet ./...` +5. `go test ./...` +6. `golangci-lint run ./...` +7. `go build -trimpath main.go` when CLI, configuration, embedding, or package integration behavior changed For user-visible CLI behavior, run a focused `go run ./main.go ...` scenario diff --git a/.agents/skills/go-tester/SKILL.md b/.agents/skills/go-tester/SKILL.md index d92c9ac..b27e0f5 100644 --- a/.agents/skills/go-tester/SKILL.md +++ b/.agents/skills/go-tester/SKILL.md @@ -52,7 +52,8 @@ Two companions own neighboring concerns: fixture needed to express the behavior. 5. **Assert behavior, not implementation trivia.** Prefer outputs, changed-file lists, typed errors, and line numbers over private state. -6. **Run the narrowest test first.** Broaden only after the focused test passes. +6. **Run the narrowest test and lint target first.** Broaden only after the + focused package passes. ## Naming And Structure @@ -112,7 +113,10 @@ After Go test code changes, run: 1. `gofmt -w ` 2. the focused package test -3. `go test ./...` +3. `golangci-lint run ./` or the smallest package set that + contains touched Go files +4. `go test ./...` +5. `golangci-lint run ./...` Run `go vet ./...` when the test change also changes helper code, exported test support, or production code. From eb73edd3c7b831a924228ecd9e0e863f1f06199b Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 7 Jul 2026 14:20:18 +0200 Subject: [PATCH 4/6] Improve tests. --- embedding/parsing/instruction_test.go | 22 ++++++++++++++++++++++ embedding/parsing/pattern.go | 3 +++ 2 files changed, 25 insertions(+) diff --git a/embedding/parsing/instruction_test.go b/embedding/parsing/instruction_test.go index eea7d3f..e048a23 100644 --- a/embedding/parsing/instruction_test.go +++ b/embedding/parsing/instruction_test.go @@ -28,6 +28,7 @@ package parsing_test import ( _type "embed-code/embed-code-go/type" + "errors" "fmt" "os" "path/filepath" @@ -564,6 +565,27 @@ var _ = Describe("Instruction", func() { )) }) + It("should report pattern not found when the glob matcher panics", func() { + sourceRoot := GinkgoT().TempDir() + config.CodeRoots = _type.NamedPathList{_type.NamedPath{Path: sourceRoot}} + Expect(os.WriteFile( + filepath.Join(sourceRoot, "panic-pattern.txt"), + []byte("0\n"), + 0600, + )).To(Succeed()) + xmlString := buildInstruction("panic-pattern.txt", TestInstructionParams{ + lineGlob: "0{}", + }) + instruction := createInstructionFromXML(xmlString, config) + + _, err := instruction.Content() + + var patternErr parsing.PatternNotFoundError + Expect(errors.As(err, &patternErr)).Should(BeTrue()) + Expect(patternErr.Kind).Should(Equal("line")) + Expect(err).Should(MatchError(ContainSubstring("line pattern `0{}`"))) + }) + It("should use shared resolver as default", func() { sourceRoot := GinkgoT().TempDir() markdownPath := filepath.Join(GinkgoT().TempDir(), "doc.md") diff --git a/embedding/parsing/pattern.go b/embedding/parsing/pattern.go index 6176197..f8b8396 100644 --- a/embedding/parsing/pattern.go +++ b/embedding/parsing/pattern.go @@ -155,6 +155,9 @@ func (m lineMatcher) matches(line string) bool { } // matchGlob reports whether a glob matches and treats dependency panics as misses. +// +// A miss lets the instruction layer report PatternNotFoundError with source +// context instead of exposing a third-party matcher panic to users. func matchGlob(compiledGlob glob.Glob, line string) bool { var matched bool func() { From e0710bc5146c64fd2a688e32b7d43e685ed5bb72 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 7 Jul 2026 14:31:43 +0200 Subject: [PATCH 5/6] Improve doc. --- showcase/embedding/positive/instruction-tag.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/showcase/embedding/positive/instruction-tag.md b/showcase/embedding/positive/instruction-tag.md index 7f08059..bc06cfe 100644 --- a/showcase/embedding/positive/instruction-tag.md +++ b/showcase/embedding/positive/instruction-tag.md @@ -11,6 +11,11 @@ configured `code-path`. With named source roots, start the value with `$name/`, such as `$java/org/showcase/Greeting.java`. With one unnamed source root, use a path relative to that root. +Embed-code treats documentation files and embedding instructions as trusted +local input. The `file` value may intentionally use relative path segments such +as `../`; embed-code resolves the path against the selected source root but does +not enforce that the final path stays inside that root. + Use one source-selection shape per instruction: - `file` alone embeds the whole source file. From fe1115105695462108ed4471b8b448e9e824ef56 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 7 Jul 2026 18:42:17 +0200 Subject: [PATCH 6/6] Update doc. --- embedding/parsing/pattern.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embedding/parsing/pattern.go b/embedding/parsing/pattern.go index f8b8396..abe4b96 100644 --- a/embedding/parsing/pattern.go +++ b/embedding/parsing/pattern.go @@ -158,6 +158,8 @@ func (m lineMatcher) matches(line string) bool { // // A miss lets the instruction layer report PatternNotFoundError with source // context instead of exposing a third-party matcher panic to users. +// Recovery stays at single-line matcher granularity so one panicking candidate +// does not abort the rest of the source scan. func matchGlob(compiledGlob glob.Glob, line string) bool { var matched bool func() {