diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md
index 051e2d2e6..7e3e9232f 100644
--- a/docs/development/agent-workflow.md
+++ b/docs/development/agent-workflow.md
@@ -62,6 +62,7 @@ ASCII control/space characters before resolution.
Iframe/embed sources and object data attributes are document references and retain Markdown-target anchor checks.
Inline iframe srcdoc documents are traversed too, with their own fragment anchors.
The first active HTML base href sets reference resolution, including nested srcdoc bases.
+Resolved file URLs use native filesystem conversion, including Windows drive paths.
Explicit srcset attributes must contain at least one parsed candidate.
Image and media references require nonempty targets that resolve to files, not directories.
Image references check file existence without interpreting image fragments as
diff --git a/tools/skills/validate-agent-guidance.mjs b/tools/skills/validate-agent-guidance.mjs
index cdf1621fa..73c42b072 100644
--- a/tools/skills/validate-agent-guidance.mjs
+++ b/tools/skills/validate-agent-guidance.mjs
@@ -41,12 +41,14 @@ async function validateReference(
if (unresolvedReference !== undefined)
return `${source}: unresolved Markdown reference "${unresolvedReference}"`;
if (image && !target) return `${source}: empty media target`;
+ let resolvedBasePath;
if (bases?.length) {
try {
let base = pathToFileURL(resolve(rootDir, source));
for (const href of bases) base = new URL(href, base);
const url = new URL(target, base);
if (url.protocol !== 'file:') return;
+ resolvedBasePath = fileURLToPath(url);
target = url.pathname + url.search + url.hash;
embeddedAnchors = undefined;
} catch {
@@ -69,9 +71,14 @@ async function validateReference(
return !anchor || embeddedAnchors.includes(anchor)
? undefined
: `${source}: missing anchor "${anchor}" in iframe srcdoc`;
- const absolute = path
- ? resolve(literal ? rootDir : dirname(resolve(rootDir, source)), path)
- : resolve(rootDir, source);
+ const absolute =
+ resolvedBasePath ??
+ (path
+ ? resolve(
+ literal ? rootDir : dirname(resolve(rootDir, source)),
+ path
+ )
+ : resolve(rootDir, source));
if (!within(rootDir, absolute))
return `${source}: referenced path escapes repository root: ${target}`;
try {
diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs
index 81fb15915..e5f4ec852 100644
--- a/tools/skills/validate-agent-guidance.test.mjs
+++ b/tools/skills/validate-agent-guidance.test.mjs
@@ -1478,3 +1478,14 @@ test('srcdoc bases retain remote and repository containment rules', async (t) =>
).some((message) => message.includes('escapes repository'))
);
});
+
+test('local HTML base uses native filesystem paths', async (t) => {
+ assert.deepEqual(
+ await diagnostics(t, {
+ 'AGENTS.md':
+ 'Guide',
+ 'docs/native #%.md': '# Heading',
+ }),
+ []
+ );
+});