fix(score): filter URLs, env-vars, Windows paths, and HTTP routes from grounding checker#168
Merged
Merged
Conversation
…m grounding checker
The path reference extractor was producing false positives for:
- Env-var assignments in code blocks (FOO=http://...)
- Windows drive-letter absolute paths (C:/Users/...)
- HTTP API route paths in tables (/api/v1/users)
- hostname:port strings (localhost:3000)
- Windows path fragments captured by the raw prose regex
Three changes:
1. `looksLikePath()`: add guards for env-var `=`, Windows drive letters,
multi-segment `/`-prefixed routes with no extension, and hostname:port.
2. `extractPathCandidates()`: tighten the raw prose regex extractor to only
include candidates that have an explicit `./`/`../` prefix or a known file
extension, eliminating Windows path fragments and cross-repo noise.
3. `resolveReferencedPath()`: use `path.isAbsolute()` alongside `startsWith('/')`
for cross-platform correctness.
Adds a regression test covering all false-positive categories.
Closes #164, #163
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
stackbilt-admin
commented
May 23, 2026
| for (const match of content.matchAll(/(^|[\s(])((?:\.{1,2}\/)?(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+(?:\.[A-Za-z0-9._-]+)?)/gm)) { | ||
| const candidate = normalizePathCandidate(match[2]); | ||
| const ext = path.posix.extname(candidate).toLowerCase(); | ||
| if (!candidate.startsWith('./') && !candidate.startsWith('../') && !KNOWN_PATH_EXTENSIONS.has(ext)) continue; |
Member
Author
There was a problem hiding this comment.
This gate looks right in intent, but it currently never sees raw ./ candidates because normalizePathCandidate() strips a leading ./ before this check runs. That means prose refs like ./docs/runbooks (no known extension) get dropped and wont be checked as broken paths. Consider checking for the .//../ prefix before normalization, or preserving a pre-normalized copy for this predicate.
Member
Author
There was a problem hiding this comment.
Fixed in e9044cf — now capture raw = match[2] before normalization and use it for the .//../ prefix check. A new regression test (./docs/runbooks with no extension, missing file → must appear in broken-paths list) confirms the behavior. Thanks for the catch.
…te strips it
normalizePathCandidate() removes the leading ./ from path candidates.
The gate added in the previous commit checked the already-normalized string,
so ./docs/runbooks (no extension) became docs/runbooks, failed the
startsWith('./') check, and was silently dropped — never flagged as a
broken reference even though the intent was unambiguous.
Fix: capture the raw regex match before normalization and use it for the
./ / ../ prefix predicate; apply normalization only for the looksLikePath
check and set insertion.
Adds a regression test: ./docs/runbooks (no extension, missing file) must
appear in the broken-paths list.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
looksLikePath()now rejects env-var assignments (FOO=...), Windows drive-letter paths (C:/...), multi-segment HTTP route paths (/api/v1/users), andhostname:portstrings.//../prefix or a known file extension — eliminates Windows path fragments and cross-repo noise from freeform textresolveReferencedPath()usespath.isAbsolute()alongsidestartsWith('/')for cross-platform correctnessWhat's still a known gap (follow-up)
Cross-repo references with known extensions (e.g.
`stackbilt_llc/policies/oss-infrastructure-update-policy.md`in CLAUDE.md) are still caught by the backtick extractor and marked broken. A config-based exclusion mechanism (groundingExcludePatternsin.charter/config.json) is the right fix and will be addressed in a separate issue.Test plan
pnpm test— 491/491 pass (1 new test added)Closes #164, #163
🤖 Generated with Claude Code