fix: harden docs map heading rendering (#99099)

Summary:
- The PR changes the docs map generator to escape HTML-significant heading text, regenerates `docs/docs_map.md`, and adds a Vitest regression for HTML-like headings.
- PR surface: Tests +13, Docs 0, Other +10. Total +23 across 3 files.
- Reproducibility: yes. Current main has source headings such as `docs/cli/agents.md:164` with `<id>`, while `docs/docs_map.md:1231` omits it and `scripts/generate-docs-map.mjs:90` strips `<...>` text.

Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.

Validation:
- ClawSweeper review passed for head 261466a2a8.
- Required merge gates passed before the squash merge.

Prepared head SHA: 261466a2a8
Review: https://github.com/openclaw/openclaw/pull/99099#issuecomment-4866739708

Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Approved-by: hxy91819
This commit is contained in:
Mason Huang
2026-07-03 15:36:11 +00:00
committed by GitHub
co-authored by Mason Huang
parent f3eccb0dfd
commit 0d2aeb2a1e
3 changed files with 44 additions and 21 deletions
+19 -19
View File
@@ -289,7 +289,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- Route: /channels/channel-routing
- Headings:
- H1: Channels & routing
- H1: Channels &amp; routing
- H2: Key terms
- H2: Outbound target prefixes
- H2: Session key shapes (examples)
@@ -1228,7 +1228,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- H3: agents bindings
- H3: agents bind
- H3: agents unbind
- H3: agents delete
- H3: agents delete &lt;id&gt;
- H2: Identity files
- H2: Set identity
- H2: Related
@@ -1420,13 +1420,13 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- H1: openclaw devices
- H2: Commands
- H3: openclaw devices list
- H3: openclaw devices remove
- H3: openclaw devices remove &lt;deviceId&gt;
- H3: openclaw devices clear --yes [--pending]
- H3: openclaw devices approve [requestId] [--latest]
- H2: Paperclip / openclawgateway first-run approval
- H3: openclaw devices reject
- H3: openclaw devices rotate --device --role [--scope ]
- H3: openclaw devices revoke --device --role
- H3: openclaw devices reject &lt;requestId&gt;
- H3: openclaw devices rotate --device &lt;id&gt; --role &lt;role&gt; [--scope &lt;scope...&gt;]
- H3: openclaw devices revoke --device &lt;id&gt; --role &lt;role&gt;
- H2: Common options
- H2: Notes
- H2: Token drift recovery checklist
@@ -1508,7 +1508,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- H3: gateway status
- H3: gateway probe
- H4: Remote over SSH (Mac app parity)
- H3: gateway call
- H3: gateway call &lt;method&gt;
- H2: Manage the Gateway service
- H3: Install with a wrapper
- H2: Discover gateways (Bonjour)
@@ -1749,11 +1749,11 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- H3: JSONL
- H3: YAML
- H2: Subcommand reference
- H3: resolve
- H3: find
- H3: set
- H3: validate
- H3: emit
- H3: resolve &lt;oc-path&gt;
- H3: find &lt;pattern&gt;
- H3: set &lt;oc-path&gt; &lt;value&gt;
- H3: validate &lt;oc-path&gt;
- H3: emit &lt;file&gt;
- H2: Exit codes
- H2: Output mode
- H2: Notes
@@ -2041,12 +2041,12 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- H3: wiki status
- H3: wiki doctor
- H3: wiki init
- H3: wiki ingest
- H3: wiki okf import
- H3: wiki ingest &lt;path-or-url&gt;
- H3: wiki okf import &lt;path&gt;
- H3: wiki compile
- H3: wiki lint
- H3: wiki search
- H3: wiki get
- H3: wiki search &lt;query&gt;
- H3: wiki get &lt;lookup&gt;
- H3: wiki apply
- H3: wiki bridge import
- H3: wiki unsafe-local import
@@ -4494,7 +4494,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- H3: Volume (required)
- H3: Variables
- H2: Connect a channel
- H2: Backups & migration
- H2: Backups &amp; migration
- H2: Next steps
## install/raspberry-pi.md
@@ -5132,7 +5132,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- Route: /platforms/mac/voicewake
- Headings:
- H1: Voice Wake & Push-to-Talk
- H1: Voice Wake &amp; Push-to-Talk
- H2: Requirements
- H2: Modes
- H2: Runtime behavior (wake-word)
@@ -8549,7 +8549,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- H3: 3.6 Discovery (AML.TA0008)
- H4: T-DISC-001: Tool Enumeration
- H4: T-DISC-002: Session Data Extraction
- H3: 3.7 Collection & Exfiltration (AML.TA0009, AML.TA0010)
- H3: 3.7 Collection &amp; Exfiltration (AML.TA0009, AML.TA0010)
- H4: T-EXFIL-001: Data Theft via webfetch
- H4: T-EXFIL-002: Unauthorized Message Sending
- H4: T-EXFIL-003: Credential Harvesting
+12 -2
View File
@@ -84,14 +84,20 @@ function stripFrontmatter(raw) {
return raw;
}
function escapeMarkdownHtmlText(value) {
return value.replace(/&/gu, "&amp;").replace(/</gu, "&lt;").replace(/>/gu, "&gt;");
}
function cleanHeadingText(value) {
return value
const normalized = value
.replace(/\s+#+\s*$/u, "")
.replace(/<[^>]+>/gu, "")
.replace(/\[([^\]]+)\]\([^)]*\)/gu, "$1")
.replace(/[*_~`]/gu, "")
.replace(/\s+/gu, " ")
.trim();
// Docs map is Markdown consumed by humans and agents. Escape HTML instead of
// trying to strip tags so malformed source headings cannot reintroduce markup.
return escapeMarkdownHtmlText(normalized);
}
function extractHeadings(raw) {
@@ -198,3 +204,7 @@ const isMain = process.argv[1] ? fileURLToPath(import.meta.url) === process.argv
if (isMain) {
main();
}
export const testing = {
cleanHeadingText,
};
+13
View File
@@ -0,0 +1,13 @@
import { describe, expect, it } from "vitest";
import { testing } from "../../scripts/generate-docs-map.mjs";
describe("generate docs map", () => {
it("renders heading HTML as text", () => {
expect(testing.cleanHeadingText("`API` <script>alert(1)</script>")).toBe(
"API &lt;script&gt;alert(1)&lt;/script&gt;",
);
expect(testing.cleanHeadingText("<scr<script>ipt>alert(1)</script>")).toBe(
"&lt;scr&lt;script&gt;ipt&gt;alert(1)&lt;/script&gt;",
);
});
});