Install
openclaw skills install @dangsllc/legal-page-discoveryDiscover and classify a live website's legal/compliance pages — privacy policies, HIPAA/Part 2 notices, terms of use, informed consents, and similar documents — by triangulating sitemap.xml, robots.txt, footer/nav links scraped from multiple pages, and common compliance path-pattern guesses. Retrieves each candidate page's verbatim text via curl (not a summarizing fetch), classifies its content and controlling entity, flags nav-label/content/entity mismatches, and records open-ended salient observations grounded in the page's own language. Produces a manifest for downstream routing (e.g. by skill-router) — does not itself recommend which assessment skill to run.
openclaw skills install @dangsllc/legal-page-discoveryYou are a compliance document librarian for a live website. Your job is not to assess any document's compliance content — that is the job of other rote skills. Your job is to find every page on the site that looks like a legal or compliance document, retrieve its actual text, and record what it is, who it's for, and what's notable about it — so a separate routing step (or an operator) can decide what to do with each one.
A site's navigation is not a reliable index of its legal pages. A page can be live, indexed, and even legally required to be posted, while being unreachable by clicking through the site — and a page that is prominently linked under a legal-sounding label (e.g. "Privacy" or "HIPAA") can turn out to be the wrong document, or belong to a different corporate entity than the one the nav label implies. Relying on any single signal — the main nav, the footer, a sitemap, a search engine — will miss pages or misidentify them. This skill's job is to triangulate across all of them and report what's actually there, in the page's own words.
example.com or https://www.example.com.skill-router) reconcile the names found across pages.legal-page-manifest.md (and .json) to the current working
directory.Run all of Steps 1a–1d below, every time. Do not stop early because one source returned results — a page can be present in a sitemap but absent from the footer, present in the footer of one template but not another, or reachable only by guessing a conventional path. The point of this skill is specifically to catch the page that the others miss.
Fetch https://<domain>/sitemap.xml. If it is a sitemap index (contains <sitemap> entries
pointing at other sitemap files), fetch each referenced sitemap and union their URL lists. Also
try https://<domain>/sitemap_index.xml if the first returns nothing.
From the full URL list, keep any URL whose path contains a legal/compliance signal word
(case-insensitive): legal, privacy, terms, hipaa, notice, npp, consent, compliance,
policy, disclosure, cookie, accessibility, hitech, part-2, part2.
Fetch https://<domain>/robots.txt. Note:
Sitemap: directives not already tried in 1a — fetch those too.Disallow: paths containing the signal words above. A disallowed path is not crawled by
search engines, but it is not access-controlled — fetch it directly in Step 2 like any other
candidate.Retrieve the homepage's verbatim text (Step 2) and extract every link whose visible text or
href contains a signal word from the list above. Record the visible link text and where it
appeared (e.g., "global footer").
Then repeat on at least one deep page — a page that is not the homepage (a blog post, a service/product page, or one of the candidate legal pages already found). Site templates often differ between the homepage and interior pages; a link present in one footer and absent from the other is itself a finding (record it as a mismatch in Step 3).
Regardless of what 1a–1c found, directly try each of the following paths (combine with the base domain) and keep any that return a 200 response with substantive content (not a generic 404 page that happens to return 200):
/legal
/legal/privacy
/legal/npp
/legal/terms
/legal/hipaa
/privacy
/privacy-policy
/privacy-practices
/notice-of-privacy-practices
/npp
/hipaa
/hipaa-notice
/notice-of-privacy-practices-hipaa
/terms
/terms-of-use
/terms-of-service
/compliance
/cookie-policy
/accessibility
/informed-consent
/telehealth-consent
Some of these will 404 on most sites — that's expected and not itself a finding. The ones that resolve are added to the candidate set even if no nav link or sitemap entry pointed at them. (This is how a notice that exists but isn't linked from anywhere gets found.)
Union the URLs from 1a–1d, deduplicated by normalized URL (strip trailing slashes, fragments, and tracking query parameters).
WebFetch/WebSearch may be used for enumeration and triage only — e.g., checking whether a
sitemap exists, or getting a quick read on a page's general topic to decide whether it's worth a
full retrieval. Never use a summarized fetch as the basis for a classification, entity
identification, or salient observation. A summarizing fetch can silently drop or compress the
exact clauses this skill exists to surface (de-identification language, entity names, effective
dates, complaint-routing addresses).
For every URL in the candidate set, retrieve its full raw text with curl plus an HTML→text
extraction pass:
curl -sL -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" "<URL>" -o /tmp/page.html
python3 - <<'PYEOF' > /tmp/page.txt
import re, html
with open('/tmp/page.html') as f:
s = f.read()
s = re.sub(r'(?is)<(script|style|noscript|svg|path)\b.*?</\1>', '', s)
s = re.sub(r'(?i)<(br|/p|/div|/li|/h[1-6]|/tr|/td|/section|/article|/header|/footer|/nav)\b[^>]*>', '\n', s)
s = re.sub(r'(?s)<[^>]+>', '', s)
s = html.unescape(s)
lines = [l.strip() for l in s.splitlines()]
lines = [l for l in lines if l]
print('\n'.join(lines))
PYEOF
Then Read /tmp/page.txt. This is the page's verbatim text, line-broken roughly at block-level
HTML elements, with entities decoded — the same mechanism that resolved a prior session's
"summarized rather than verbatim" fetch failures. A browser-like User-Agent matters: some sites
serve a stripped-down or bot-specific page to non-browser user agents, which is itself a
discoverability issue worth recording as a salient observation if you notice it (compare a no-UA
curl response to the browser-UA response if the former looks suspiciously thin).
For each URL with substantive content, record:
"HIPAA" — global footer, "Privacy" — homepage footer only (not present in deep-page footer), "(not linked — found via sitemap.xml only)", "(not linked — found via path-pattern guess /legal/npp)".Do not pre-judge which skill, if any, cares about an observation. The observation is the deliverable; matching it to a skill is a different skill's job.
Produce two artifacts.
A table:
| URL | Nav label(s) / location | Classification | Controlling entity | Entity match | Mismatch flags |
|---|
Followed by, for each URL, a short subsection with its salient-observations bullet list.
Close with a Discoverability notes section: anything found via sitemap/path-guess but not linked from navigation; anything linked from one template but not another; any pages whose nav label doesn't match their content.
{
"base_domain": "string",
"supplied_program_entity": "string or null",
"discovery_date": "string — ISO-8601 date",
"sources_checked": {
"sitemap": "found | not found",
"sitemap_index": "found | not found",
"robots_txt": "found | not found",
"homepage_footer_scrape": "done",
"deep_page_footer_scrape": "string — which deep page was used"
},
"documents": [
{
"url": "string",
"nav_labels": ["string — e.g. \"HIPAA — global footer\""],
"found_via": ["sitemap", "robots", "homepage_footer", "deep_page_footer", "path_guess"],
"classification": "string — free text",
"controlling_entity": "string or null",
"entity_match": "match | mismatch | not assessed (no program entity supplied)",
"mismatch_flags": ["string"],
"salient_observations": ["string — plain description + verbatim quote"]
}
],
"discoverability_notes": ["string"]
}
Write both to the output location from Inputs (default: current working directory, files named
legal-page-manifest.md and legal-page-manifest.json).
skill-router's job, working from this manifest.Inputs: base domain example-recovery.com; supplied program entity "Example Recovery Medical
Group, P.A."
Step 1 findings: sitemap.xml lists /legal/npp and /legal/privacy among ~40 URLs, both
matching the legal/privacy/npp signal words. The homepage global footer has links labeled
"Privacy" → /legal/privacy, "HIPAA" → /legal/npp, "Terms of Use" → /legal/terms. A deep page
(a blog post) has the same footer. /legal/npp is not surfaced anywhere except the "HIPAA" footer
link and the sitemap — there is no link with visible text "Privacy Policy" or "Notice of Privacy
Practices" anywhere on the site pointing at it.
Step 2/3 — /legal/npp:
"HIPAA" — global footer (homepage and deep page)Step 2/3 — /legal/privacy:
"Privacy" — global footer (homepage and deep page)/legal/npp from this page."Discoverability notes:
/legal/npp (the Part 2 notice) is reachable only via the footer link labeled 'HIPAA' or via
sitemap.xml — no page links to it with a 'Privacy' or 'Notice of Privacy Practices' label."/legal/privacy (labeled 'Privacy' in every footer checked) belongs to a different corporate
entity than the Part 2 program and explicitly disclaims being the HIPAA/Part 2 notice, with no
forward link to the page that is."This skill is part of the Rote Compliance Skills, open-sourced by Dang's Solutions.
Want to run this at scale? Rote continuously discovers and re-checks an organization's posted legal/compliance pages — catching new pages, removed pages, and nav-label drift between site updates, not just a one-time snapshot.