diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md
index 7e76b8665..77dcf800a 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.
+URL attributes discard surrounding ASCII control/space characters before resolution.
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.
@@ -70,7 +71,8 @@ Declared scoped dependencies, scope wildcards and matching TypeScript path alias
are recognized as package/alias mentions. Traversal and document-file imports are
rejected before those exemptions, including document paths with fragments or queries.
All recognized Markdown extensions share the document-import guard; reStructuredText
-and AsciiDoc documents are also excluded from package exemptions. URL-encoded
+and AsciiDoc, PDF, Word, OpenDocument, RTF, Org and TeX documents are also excluded
+from package exemptions. URL-encoded
paths do not receive package exemptions. TypeScript configuration is parsed as JSONC.
Declared packages also permit safe subpaths; exact aliases stay exact.
Declared package mentions may include a version (including semver comparators and wildcard ranges) or dist-tag qualifier.
diff --git a/tools/skills/agent-guidance-markdown.mjs b/tools/skills/agent-guidance-markdown.mjs
index 4484bd3ef..997b96476 100644
--- a/tools/skills/agent-guidance-markdown.mjs
+++ b/tools/skills/agent-guidance-markdown.mjs
@@ -83,7 +83,10 @@ function htmlNavigation(html, inspect = () => {}) {
(node.tagName === 'video' && attribute.name === 'poster')
)
references.push({
- target: attribute.value,
+ target: attribute.value.replace(
+ /^[\u0000-\u0020]+|[\u0000-\u0020]+$/gu,
+ ''
+ ),
image: !['a', 'area', 'iframe'].includes(node.tagName),
});
if (
diff --git a/tools/skills/validate-agent-guidance.mjs b/tools/skills/validate-agent-guidance.mjs
index 0f138f1e3..92eb20abe 100644
--- a/tools/skills/validate-agent-guidance.mjs
+++ b/tools/skills/validate-agent-guidance.mjs
@@ -130,7 +130,9 @@ async function packageMentions(rootDir) {
if (
/%[\da-f]{2}/iu.test(token) ||
MARKDOWN_EXTENSION.test(path) ||
- /\.(?:txt|json|ya?ml|html?|rst|rest|adoc|asciidoc)$/iu.test(path)
+ /\.(?:txt|json|ya?ml|html?|rst|rest|adoc|asciidoc|pdf|docx?|odt|rtf|org|tex)$/iu.test(
+ path
+ )
)
return false;
if (packages.includes(token)) return true;
diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs
index 1fb25caea..ddce49e73 100644
--- a/tools/skills/validate-agent-guidance.test.mjs
+++ b/tools/skills/validate-agent-guidance.test.mjs
@@ -1266,3 +1266,29 @@ for (const extension of ['rst', 'rest', 'adoc', 'asciidoc']) {
);
});
}
+
+for (const extension of ['pdf', 'doc', 'docx', 'odt', 'rtf', 'org', 'tex']) {
+ test(`document format is not a package exemption: ${extension}`, async (t) => {
+ assert.ok(
+ (
+ await diagnostics(t, {
+ 'package.json': JSON.stringify({
+ dependencies: { '@angular/core': '*' },
+ }),
+ 'CLAUDE.md':
+ '@AGENTS.md\n\nRead @angular/core/docs/guide.' +
+ extension,
+ })
+ ).some((message) => message.includes('additional or inline'))
+ );
+ });
+}
+for (const html of [
+ 'Guide',
+ '',
+ '
',
+]) {
+ test(`HTML URL edge whitespace is ignored: ${html}`, async (t) => {
+ assert.deepEqual(await diagnostics(t, { 'AGENTS.md': html }), []);
+ });
+}