From 3ea051532af9d79ab5a0d381bd3515afbd6d6e6a Mon Sep 17 00:00:00 2001 From: 4gray Date: Mon, 21 Sep 2026 03:45:04 +0200 Subject: [PATCH] fix(agents): exclude opaque URI prose from import scans --- docs/development/agent-workflow.md | 4 +++- tools/skills/agent-guidance-markdown.mjs | 2 +- tools/skills/validate-agent-guidance.test.mjs | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md index e862497c0..32de7cd5b 100644 --- a/docs/development/agent-workflow.md +++ b/docs/development/agent-workflow.md @@ -71,7 +71,9 @@ HTML video, audio, source and track `src` assets and video posters use the same existence checks as images. Entity decoding uses full HTML text/attribute rules, including references whose semicolon may be omitted. Inline guidance imports are rejected after punctuation as well as whitespace. -At-signs inside external URLs are excluded per HTML text node, preserving adjacent imports. +At-signs inside external URIs (including opaque schemes such as mailto) are excluded +per HTML text node, preserving adjacent imports. A colon directly before an import +does not make that import a URI. A closing bracket followed by punctuation and an at-sign terminates a bare URL exclusion. Extensionless inline candidates are also imports when they resolve to repository files, checking the full filename before prefixes at ASCII/Unicode prose separators. diff --git a/tools/skills/agent-guidance-markdown.mjs b/tools/skills/agent-guidance-markdown.mjs index 78a17fab8..ff3a34754 100644 --- a/tools/skills/agent-guidance-markdown.mjs +++ b/tools/skills/agent-guidance-markdown.mjs @@ -147,7 +147,7 @@ export function guidanceProse(markdown) { return ' '; if (node.nodeName === '#text') return node.value.replace( - /(?:\b[a-z][a-z\d+.-]*:\/\/|\/\/)[^\s]*?(?=[)\]}>][.,;:!?]*@|\s|$)/giu, + /(?:\b[a-z][a-z\d+.-]*:(?![@\s])|\/\/)[^\s]*?(?=[)\]}>][.,;:!?]*@|\s|$)/giu, ' ' ); const content = (node.childNodes ?? []).map(text).join(''); diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs index 649f6e861..a463cebbe 100644 --- a/tools/skills/validate-agent-guidance.test.mjs +++ b/tools/skills/validate-agent-guidance.test.mjs @@ -1504,3 +1504,22 @@ for (const boundary of [').', '];', '}']) { ); }); } + +for (const uri of [ + '', + 'mailto:user@example.com?subject=@docs/guide.md', + '', +]) { + test(`opaque URI is not a guidance import: ${uri}`, async (t) => { + assert.deepEqual(await diagnostics(t, { 'AGENTS.md': uri }), []); + }); +} +test('colon directly before an import remains checked', async (t) => { + assert.ok( + ( + await diagnostics(t, { + 'CLAUDE.md': '@AGENTS.md\n\nRead:@docs/guide.md', + }) + ).some((message) => message.includes('additional or inline')) + ); +});