diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md
index 4cd10caad..6cb9ad710 100644
--- a/docs/development/agent-workflow.md
+++ b/docs/development/agent-workflow.md
@@ -60,13 +60,14 @@ as Markdown links, including decoded attributes and fragment validation.
URL attributes remove ASCII tabs/newlines throughout and discard surrounding
ASCII control/space characters before resolution.
Iframe/embed sources and object data attributes are document references and retain Markdown-target anchor checks.
-Image and media references must resolve to files, not directories.
+Image and media references require nonempty targets that resolve to files, not directories.
Image references check file existence without interpreting image fragments as
Markdown headings; document links keep anchor checks even when sharing a target.
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 from the import scan.
Extensionless inline candidates are also imports when they resolve to repository files,
checking the full filename before prefixes at ASCII/Unicode prose separators.
Declared scoped dependencies, scope wildcards and matching TypeScript path aliases
diff --git a/tools/skills/validate-agent-guidance.mjs b/tools/skills/validate-agent-guidance.mjs
index f7e4e0c27..6d4288654 100644
--- a/tools/skills/validate-agent-guidance.mjs
+++ b/tools/skills/validate-agent-guidance.mjs
@@ -41,6 +41,7 @@ async function validateReference(
if (unresolvedReference !== undefined)
return `${source}: unresolved Markdown reference "${unresolvedReference}"`;
if (/^(?:[a-z][a-z\d+.-]*:|\/\/)/iu.test(target)) return;
+ if (image && !target) return `${source}: empty media target`;
let path;
let anchor;
try {
@@ -184,7 +185,10 @@ export async function validateAgentGuidance({ rootDir }) {
diagnostics.push(
`${source}: at most ${maxBytes} UTF-8 bytes allowed (received ${bytes})`
);
- const prose = guidanceProse(markdown);
+ const prose = guidanceProse(markdown).replace(
+ /(?:\b[a-z][a-z\d+.-]*:\/\/|\/\/)[^\s]+/giu,
+ ' '
+ );
const imports = guidanceStandaloneImports(markdown);
const inlineImports = [];
for (const match of prose.matchAll(
diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs
index 6aac2c98a..e5ca63685 100644
--- a/tools/skills/validate-agent-guidance.test.mjs
+++ b/tools/skills/validate-agent-guidance.test.mjs
@@ -1381,3 +1381,21 @@ for (const entity of [' ', '
', '
']) {
);
});
}
+
+for (const markup of ['![logo]()', '
', '']) {
+ test(`empty media references are rejected: ${markup}`, async (t) => {
+ assert.ok((await diagnostics(t, { 'AGENTS.md': markup })).length > 0);
+ });
+}
+for (const url of [
+ 'https://example.com/@docs/guide',
+ 'https://example.com/user?next=@docs/guide',
+ '//example.com/@docs/guide',
+]) {
+ test(`external URL at-sign is not an import: ${url}`, async (t) => {
+ assert.deepEqual(
+ await diagnostics(t, { 'AGENTS.md': 'Visit ' + url }),
+ []
+ );
+ });
+}