From 5da10082e53903fba9eec756040d5e8869c537d5 Mon Sep 17 00:00:00 2001 From: 4gray Date: Mon, 21 Sep 2026 00:58:25 +0200 Subject: [PATCH] fix(agents): validate iframe document references --- docs/development/agent-workflow.md | 1 + tools/skills/agent-guidance-markdown.mjs | 13 ++++++++---- tools/skills/validate-agent-guidance.test.mjs | 21 +++++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md index 567b87c50..109af4ff7 100644 --- a/docs/development/agent-workflow.md +++ b/docs/development/agent-workflow.md @@ -57,6 +57,7 @@ Only headings present outside inert HTML containers contribute slugs or duplicat Explicit HTML anchors use `parse5`, excluding comments, scripts, styles and template contents. Rendered HTML anchor and image-map area hrefs and image sources use the same local-reference checks as Markdown links, including decoded attributes and fragment validation. +Iframe sources are document references and retain Markdown-target anchor checks. 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 diff --git a/tools/skills/agent-guidance-markdown.mjs b/tools/skills/agent-guidance-markdown.mjs index 5ec5943da..4484bd3ef 100644 --- a/tools/skills/agent-guidance-markdown.mjs +++ b/tools/skills/agent-guidance-markdown.mjs @@ -71,15 +71,20 @@ function htmlNavigation(html, inspect = () => {}) { if ( (['a', 'area'].includes(node.tagName) && attribute.name === 'href') || - (['img', 'video', 'audio', 'source', 'track'].includes( - node.tagName - ) && + ([ + 'img', + 'video', + 'audio', + 'source', + 'track', + 'iframe', + ].includes(node.tagName) && attribute.name === 'src') || (node.tagName === 'video' && attribute.name === 'poster') ) references.push({ target: attribute.value, - image: !['a', 'area'].includes(node.tagName), + image: !['a', 'area', 'iframe'].includes(node.tagName), }); if ( ['img', 'source'].includes(node.tagName) && diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs index fb23d6960..fc7a6e36c 100644 --- a/tools/skills/validate-agent-guidance.test.mjs +++ b/tools/skills/validate-agent-guidance.test.mjs @@ -1170,3 +1170,24 @@ for (const suffix of [',then', ';then', ':then', '!then', '?then']) { ); }); } + +for (const target of ['docs/missing.html', 'docs/example.md#missing']) { + test(`iframe document target is validated: ${target}`, async (t) => { + assert.ok( + ( + await diagnostics(t, { + 'AGENTS.md': ``, + }) + ).length > 0 + ); + }); +} +test('valid iframe and inert iframe targets pass', async (t) => { + assert.deepEqual( + await diagnostics(t, { + 'AGENTS.md': + '\n', + }), + [] + ); +});