From 5cc0b22ec601b59b672992a7d8bac05e54ce047b Mon Sep 17 00:00:00 2001 From: Tomas <180413002+Tomauskasz@users.noreply.github.com> Date: Tue, 8 Sep 2026 15:18:04 +0300 Subject: [PATCH 1/4] fix: ignore slash-separated prose references Signed-off-by: Tomas <180413002+Tomauskasz@users.noreply.github.com> --- src/skillspector/references.py | 3 +- tests/nodes/test_security_remediation.py | 35 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/skillspector/references.py b/src/skillspector/references.py index b91626d3..f1270f43 100644 --- a/src/skillspector/references.py +++ b/src/skillspector/references.py @@ -44,7 +44,8 @@ class ReferenceResolutionResult: _PLAIN_RELATIVE_PATH = re.compile( - r"(? None: + records = resolve_bundle_references( + tmp_path, + source_path="SKILL.md", + source_text=( + "Compare reads/writes, environment/profile settings, and operation/node behavior." + ), + known_paths=["SKILL.md"], + ) + + assert records == [] + + +@pytest.mark.parametrize( + ("source_text", "target_path"), + [ + ("Read references/guide.md before continuing.", "references/guide.md"), + ("Read ./references/guide before continuing.", "references/guide"), + ], +) +def test_plain_local_reference_requires_an_explicit_path_signal( + tmp_path: Path, source_text: str, target_path: str +) -> None: + records = resolve_bundle_references( + tmp_path, + source_path="SKILL.md", + source_text=source_text, + known_paths=["SKILL.md", target_path], + ) + + assert len(records) == 1 + assert records[0]["status"] == "resolved" + assert records[0]["target_path"] == target_path + + def test_reference_resolver_rejects_external_and_parent_escape(tmp_path: Path) -> None: records = resolve_bundle_references( tmp_path, From 62a0bbecfe772d63a9b255d662de4eea659f51ea Mon Sep 17 00:00:00 2001 From: Tomas <180413002+Tomauskasz@users.noreply.github.com> Date: Tue, 1 Sep 2026 07:57:21 +0300 Subject: [PATCH 2/4] fix(references): preserve root-level paths Context: Plain reference matching required a directory segment after an explicit `./`, so `./guide` produced no candidate even though the prefix disambiguates the path from prose. Changes: - Allow zero or more directory segments between `./` and the final path component. - Add the root-level extensionless case to the existing explicit-path regression matrix. Impact: Root-level extensionless references with an explicit `./` resolve again. Unprefixed slash-separated prose remains excluded, and nested explicit paths keep their existing behavior. Validation: - `uv run --extra dev pytest -q tests/nodes/test_security_remediation.py::test_plain_local_reference_requires_an_explicit_path_signal`: 3 passed. - Focused reference-resolver set: 10 passed. - Ruff on the changed source and test files: passed. - `git diff --check`: passed. Notes: None. Signed-off-by: Tomas <180413002+Tomauskasz@users.noreply.github.com> --- src/skillspector/references.py | 2 +- tests/nodes/test_security_remediation.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/skillspector/references.py b/src/skillspector/references.py index f1270f43..6c0d5cd1 100644 --- a/src/skillspector/references.py +++ b/src/skillspector/references.py @@ -44,7 +44,7 @@ class ReferenceResolutionResult: _PLAIN_RELATIVE_PATH = re.compile( - r"(? None: ("source_text", "target_path"), [ ("Read references/guide.md before continuing.", "references/guide.md"), + ("Read ./guide before continuing.", "guide"), ("Read ./references/guide before continuing.", "references/guide"), ], ) From b01bb14588f17f5037524d67bf721e54a90ce2e0 Mon Sep 17 00:00:00 2001 From: Tomas <180413002+Tomauskasz@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:56:02 +0300 Subject: [PATCH 3/4] test(references): cover model/provider prose Context: Issue #450 lists model/provider as ordinary slash-separated prose that must not become a local file reference. Changes: - Add model/provider to the existing plain-prose reference regression case. Impact: Extend the false-positive contract coverage without changing production reference parsing. Validation: - Focused reference suite: 14 passed, 57 deselected. - Lint: All checks passed. - Format check: 193 files already formatted. - Build: source distribution and wheel built successfully. - git diff --check: passed. Notes: Bare dotted tokens remain excluded because versions, domains, and identifiers are ambiguous with root-level filenames. Signed-off-by: Tomas <180413002+Tomauskasz@users.noreply.github.com> --- tests/nodes/test_security_remediation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/nodes/test_security_remediation.py b/tests/nodes/test_security_remediation.py index 24ac5a14..b409d635 100644 --- a/tests/nodes/test_security_remediation.py +++ b/tests/nodes/test_security_remediation.py @@ -357,7 +357,8 @@ def test_plain_slash_separated_prose_is_not_a_reference(tmp_path: Path) -> None: tmp_path, source_path="SKILL.md", source_text=( - "Compare reads/writes, environment/profile settings, and operation/node behavior." + "Compare reads/writes, environment/profile settings, operation/node behavior, " + "and model/provider options." ), known_paths=["SKILL.md"], ) From 6710ddfe7946bab2e2e0de37440340d4cbfa4398 Mon Sep 17 00:00:00 2001 From: Tomas <180413002+Tomauskasz@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:58:23 +0300 Subject: [PATCH 4/4] test(references): guard dotted prose Context: Bare dotted tokens are ambiguous with root-level filenames and can fabricate missing references for versions, domains, or ecosystem names. Changes: - Add SkillSpector 2.10.0, node.js, and example.com to the plain-prose negative reference case. Impact: Protect the partial-analysis boundary from a future broad match of unquoted dotted prose. Validation: - Focused reference suite: 14 passed, 57 deselected. - Ruff check: All checks passed. - git diff --check: passed. Notes: Root-level filenames remain available through quotes, code spans, Markdown links, or an explicit ./ prefix. Signed-off-by: Tomas <180413002+Tomauskasz@users.noreply.github.com> --- tests/nodes/test_security_remediation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/nodes/test_security_remediation.py b/tests/nodes/test_security_remediation.py index b409d635..6964d9a7 100644 --- a/tests/nodes/test_security_remediation.py +++ b/tests/nodes/test_security_remediation.py @@ -358,7 +358,7 @@ def test_plain_slash_separated_prose_is_not_a_reference(tmp_path: Path) -> None: source_path="SKILL.md", source_text=( "Compare reads/writes, environment/profile settings, operation/node behavior, " - "and model/provider options." + "and model/provider options. SkillSpector 2.10.0 supports node.js; see example.com." ), known_paths=["SKILL.md"], )