视觉画布设计
canvas-design
基于设计原则创作静态视觉作品,用于海报与品牌图形。
verifying-skill-accuracy【文档与办公】Fact-check LLM-drafted technical content against primary sources — source hierarchy, programmatic existence probes for tool and plugin names, class-before-instance error triage, the truncated-negative trap, adversarial re-checking, and the verified-stamp discipline. Use when auditing a SKILL.md for factual errors, before stamping a skill verified, wh…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Skills in this repo are drafted with LLM assistance. The methodology in them is usually sound; the specifics are not trustworthy until checked. The measured rate from the first verification pass was 31 factual errors across 11 of 12 skills — about 2.6 per skill, with a single skill clean on both passes. Assume that rate applies to anything unstamped.
This skill is the procedure for driving that rate down, and for knowing when you are allowed to say a skill has been verified.
verified: frontmatter stampauthoring-security-skills, then
verify with this skill as the final gatepython3 scripts/validate.py --strict, which checks form onlyThe repo's CI is a closed loop. validate.py, sync_attack.py, and
run_evals.py check frontmatter shape, cross-reference integrity, ATT&CK-index
consistency, and routing against self-authored eval cases. Every one of
those can pass on a skill whose commands do not exist.
Green CI means the skill is well-formed. It says nothing about whether it is true. Never cite a passing test run as evidence of accuracy.
Once you identify an error class, check it everywhere before moving on — not just in the skill that surfaced it. Errors of a class cluster, and a per-skill pass leaves the collection internally inconsistent.
Two cases from this repo make the point. The legacy Sigma date format was
fixed in writing-sigma-rules, then turned up again in
engineering-detections — in a skill that had already been stamped, because
the per-skill inventory did not include a class fixed elsewhere. And
attacking-entra-id was still calling retired AzureAD PowerShell cmdlets
while investigating-m365-entra already used the modern Graph equivalents:
the collection contradicted itself, and only a cross-cutting grep showed it.
So: fix the instance, then immediately grep every skill for the pattern, and say in the commit which classes you swept. Classes worth sweeping in a security collection — tool renames and archived projects, CVE IDs, cloud metadata endpoints, removed API versions, default ports, spec version claims, and any behaviour gated on a platform version.
Do not start by checking facts one at a time. First ask: is there a single systematic claim whose failure invalidates everything below it?
In analyzing-memory-images, the skill invoked Volatility as volatility3.
The real entry point is vol. That one error made all 40 commands in the
file uncopyable — far more damage than the two nonexistent plugins found
afterwards, and fixable with one substitution.
Check in this order:
A class error is cheap to fix and expensive to miss. An instance error is the reverse.
Use the highest tier available, and record which tier you used.
| Tier | Source | Use for |
|---|---|---|
| 1 | Official docs for the specific version, upstream source, RFCs, the vendor's own reference | Command syntax, plugin names, API shapes, protocol details |
| 2 | Vendor KB, release notes, changelogs, official blog announcing a change | Defaults, retention windows, licence gates, deprecations |
| 3 | Maintainer-authored write-ups, conference material by the tool author | Intent, gotchas, why a thing behaves as it does |
| 4 | Community posts, tutorials, Stack Overflow | Leads to verify at tier 1 — never as the citation |
| — | Model memory | Nothing. Not one claim. |
Two rules on top of the table:
latest or stable doc branches, and note when the skill targets an older
pinned release.Each of these has produced a real error in this repo:
Pull documentation through defuddle.md, which strips a page to its main
content and returns Markdown with YAML frontmatter. Prefix any URL:
curl -sL "https://defuddle.md/learn.microsoft.com/en-us/purview/audit-log-retention-policies"
The scheme is optional in the path — defuddle.md/example.com/x and
defuddle.md/https://example.com/x both work.
Two reasons, and the second matters more than the first:
grep, instead of a model's summary of the page. That is
what makes it safe to draw a negative conclusion — see below.Use it for documentation, specs, vendor KB, and articles. Do not use it for:
A blocked fetch is not a negative result. Some hosts refuse the extractor
and return an error blob instead of the page — freedesktop.org answers with
{"error":"Failed to fetch: 418 I'm a teapot"}. Treat that as "I did not read
the page", never as "the page does not say this". It is the truncated-negative
trap wearing a different hat, and the fix is the same: re-fetch directly before
concluding anything.
Where a project publishes one doc page per module, existence is a status code, not a judgment call. This is the highest-confidence, lowest-effort check available, and it batches.
# One plugin: 200 = exists, 404 = does not
curl -s -o /dev/null -w "%{http_code}\n" \
"https://volatility3.readthedocs.io/en/latest/volatility3.plugins.windows.idt.html"
# Every plugin the skill references, in one pass
plugins=$(grep -oE '\b(windows|linux|mac)\.[a-z_]+' SKILL.md | sort -u)
for p in $plugins; do
code=$(curl -s -o /dev/null -w "%{http_code}" \
"https://volatility3.readthedocs.io/en/latest/volatility3.plugins.$p.html")
[ "$code" = "200" ] || echo "MISSING $code $p"
done
That pass settled 32 plugin references in seconds and found the two that do not exist. Adapt the URL pattern per project — most doc generators (Sphinx, mkdocs, pkg.go.dev, docs.rs, npm, PyPI) expose a per-symbol or per-package URL you can probe the same way.
When a project has no per-symbol docs, fall back to an exact-string search of
the upstream source — a raw file fetch and a grep for the literal
identifier. Both of these are deterministic. Prefer them over asking any model,
including yourself.
A summarizer saying "not found" is not evidence of absence.
Fetching a large index page and asking what it contains returned "pslist, pstree, psaux, sockstat: not listed" for the Volatility Linux plugin index. All four exist. The page had been truncated before the model saw those entries. Acting on that output would have introduced four errors into a correct section — verification making the file worse.
The asymmetry that matters:
So: never delete or rewrite content on a summarized negative. Promote every negative to a deterministic check before you touch the file.
The cheapest promotion is to stop summarizing. Fetch the page through
defuddle.md and grep the full Markdown yourself — the answer becomes a match
count rather than a model's recollection:
L="volatility3.readthedocs.io/en/latest/volatility3.plugins.linux.html"
for p in pslist pstree psaux sockstat; do
echo "$p: $(curl -sL "https://defuddle.md/$L" | grep -c "linux\.$p module")"
done
# pslist: 1 pstree: 1 psaux: 1 sockstat: 1 -- all four present
That is the exact check that refutes the summarizer's "not listed" on all four. Where a per-symbol URL exists, the status probe below is stronger still. If you can get neither, leave the content alone and flag it unconfirmed — an unverified line is recoverable, a confidently deleted correct one is not.
Not all errors cost the same. Spend effort where failure is silent.
| Failure mode | Example | Cost |
|---|---|---|
| Loud — fails on first run | Wrong CLI flag, nonexistent plugin | Minutes. The tool tells you. |
| Silent — produces a confident wrong answer | Misread event ID, wrong retention window, wrong artifact meaning | An incident timeline that lawyers read. |
Verify silent-failure claims first and hardest. In practice that means the defensive and forensic content — event IDs, log schemas, artifact semantics, retention — outranks offensive tool syntax, even though the offensive content looks more dangerous.
A concrete case: the M365 skill claimed 90 days of Unified Audit Log for E3. The real default has been 180 days since October 2023. An analyst trusting the skill reads an empty 90-day window as "no activity" and closes an investigation that had six months of history available.
defuddle.md so you are reading full text cheaply rather than
a summary.verified: Stampverified: 2026-07-26 # ISO 8601; validate.py parses and counts this
The stamp means: the whole checkable surface of this skill was driven to a primary source on that date. It does not mean the skill is good, current forever, or complete.
Rules:
validate.py treats a missing stamp as an
unverified draft, which is the documented default. A malformed date is an
error because it corrupts the count.Check the current position any time:
python3 scripts/validate.py --strict # prints "Fact-checked ...: N/72"
authoring-security-skills — writing a new skill; verify with this skill
before stampingCONTRIBUTING.md — the merge bar and house stylescripts/validate.py — structural checks and the verified-count report