Audit Reference Originality
Meng ToCommunityCreated by Meng To
- Skill
- Review and audit
- Software development
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured methodology for auditing a website or digital experience against a corpus of reference materials. It focuses on evidence-based identification of potential originality risks, such as copied text, brand assets, imagery, video, and structural elements. The process emphasizes distinguishing between common design patterns and distinctive copying, requiring users to pair red flags with specific evidence from both the current site and the reference materials. It includes guidance on using automated inventory tools, manual inspection techniques, and a severity-based rubric to prioritize findings and propose concrete, non-cosmetic fixes.
What it does
- Compares current site output against a provided registry of reference materials.
- Identifies originality risks across text, brands, numbers, images, assets, video, and structure.
- Uses an evidence-based approach to flag potential issues without making legal claims.
- Provides a severity-based rubric to categorize findings from low-risk to blockers.
- Suggests concrete, substantive fixes for identified overlaps.
- Generates a comprehensive audit report including findings, history, and fix plans.
When to use it
- When verifying that a new digital experience is original and not inadvertently copying source materials.
- Before a major release to ensure no stale or unauthorized source content remains.
- When investigating potential plagiarism or brand identity overlap in a project.
- To audit historical site versions and repository artifacts for copied content.
Advantages
- Reduces risk of shipping unauthorized or copied content.
- Provides a clear, evidence-based framework for objective auditing.
- Distinguishes between standard design grammar and actual originality risks.
- Encourages substantive fixes rather than superficial masking.
- Supports both automated inventory and deep manual inspection.
Usage examples
- “Audit the current staging site against the provided brand guidelines and competitor reference pack.”
- The agent will inventory the staging site, compare it against the reference pack, and produce a report detailing any red flags, their severity, and recommended fixes.
- “Check if our new landing page copy or layout overlaps with the source project files.”
- The agent will perform a category-by-category comparison of text and structure, flagging any verbatim or near-verbatim overlaps with evidence.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/mengto/audit-reference-originality/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip audit-reference-originality.zip -d .agents/skills
Documentation by Meng To
Audit Reference Originality
Compare the shipped experience with the complete reference corpus. Treat the audit as an evidence exercise, not a vibe check.
Preserve the audit boundary
Audit only unless the user also asks for fixes.
Call findings
originality risks,overlaps, orred flags; do not declare legal plagiarism from visual similarity alone.Pair every red flag with exact current-site evidence and exact reference evidence.
Keep fact, inference, and unknown access separate.
Do not clear a site after checking only its homepage screenshot.
1. Build the source registry
Start with the materials explicitly supplied in the task or recorded by the project:
manifests, prompt packs, and originality matrices
representative stills, full-page captures, and section crops
MP4s and extracted motion frames
reference URLs and named creators
source brand assets, copy, numbers, screenshots, and downloads
project briefs,
IMAGE_CREDITS.md, licenses, and attribution files
Prefer local evidence captured at the time of the brief. Use a live reference only to fill a real gap, because it may have changed. Record each reference's path or URL, role, date when known, and which categories it can prove.
Stop and report an access gap when a promised reference is missing. Do not silently reduce a full-page or motion audit to one cover image.
2. Inventory the current site and its history
Inspect:
rendered text, metadata, navigation, calls to action, legal copy, and hidden accessible labels
brand names, wordmarks, logos, icons, people, companies, URLs, and product/interface data
prices, metrics, dates, counts, percentages, package names, and repeated proof claims
every rendered image, background, texture, screenshot, font, logo, icon, audio file, and downloadable asset
every video, poster, frame sequence, shot order, transition, duration, and playback treatment
layout hierarchy, section order, distinctive compositions, typography behavior, motion grammar, shaders, cursor effects, and interaction sequences
current source, built output when available, asset provenance, and repository history
Do not trust filenames as proof of originality. Inspect the bytes, visible result, and history. Search renamed, deleted, and replaced files with git log, git show, git log -S, and git log --all --name-status.
Run the deterministic inventory helper when local files are available:
python <skill-dir>/scripts/build_evidence_inventory.py \
--site <site-root> \
--reference <reference-file-or-directory> \
--reference <another-reference> \
--output <temporary-output.json>The helper finds current and historical exact-file matches, suspicious basename reuse, normalized text overlap, and repeated number tokens. Treat its output as leads for human review, not an automatic verdict.
3. Compare category by category
Read references/audit-rubric.md before judging findings.
Audit at least these categories:
Text — headlines, body copy, labels, CTAs, captions, legal text, alt text, metadata, and decorative wording.
Brands — names, marks, wordmarks, proprietary icons, people, companies, partnerships, URLs, and distinctive verbal identity.
Numbers — metrics, percentages, prices, dates, counts, plan structures, durations, and interface values.
Images — exact files, crops, generated derivatives, screenshots, people, poses, objects, signature compositions, and color treatment.
Assets — fonts, icons, logos, textures, mockups, downloads, code bundles, and third-party media with unclear provenance.
Videos — exact files, frames, shots, timing, camera moves, edit rhythm, transitions, overlays, posters, and audio.
Structure and motion — section order, unusual layout devices, pinned sequences, cursor interactions, shaders, and combinations of signature elements.
History — copied material that was later renamed, recolored, cropped, hidden, deleted, or replaced.
Common patterns such as black backgrounds, large sans-serif type, ordinary pricing tables, standard fade-ins, or a conventional footer are not red flags by themselves. Escalate combinations of distinctive elements or direct evidence.
4. Triangulate every red flag
For each candidate:
Identify the current-site artifact and location.
Identify the exact reference artifact and location.
State the observable overlap without guessing intent.
State what differs.
Assign severity using the rubric.
Propose the smallest fix that breaks the overlap while preserving the site's goal.
Use hashes for exact files, normalized excerpts for copy, side-by-side crops for imagery, and matched timestamps or frames for video. A source-brand string found only in a test that explicitly forbids it is not a shipped-copy violation; explain context.
5. Propose fixes
Prefer concrete replacements:
rewrite source-like copy from the new brand's audience, offer, and vocabulary
replace names, URLs, logos, people, metrics, dates, plan names, and legal text
regenerate or license new imagery with a materially different subject, composition, and motif arrangement
replace copied assets and document provenance
re-cut videos with new shots, timing, transitions, overlays, and audio
reorder or redesign distinctive section and motion sequences
remove stale source material from current output and, when required, repository history or published artifacts
Do not recommend cosmetic recoloring as a fix for copied identity, copy, media, or composition.
6. Report the result
Lead with one verdict:
Clear in checked scopeClear with low-risk similaritiesChanges recommendedBlock releaseBlocked by missing evidence
Then provide:
checked source registry
red-flag table ordered by severity
category pass list
history findings
access gaps and unproven areas
prioritized fix plan
Include a row even when a high-risk category could not be checked. Never turn missing evidence into a pass.
Completion checks
Every supplied reference form was inspected.
Rendered output and source were both checked.
Text, brands, numbers, images, assets, videos, structure/motion, and history were covered.
Every red flag cites two evidence locations.
Exact matches are distinguished from stylistic similarity.
Proposed fixes replace the copied element rather than disguising it.
The report states what remains unverified.
Files
- ATTRIBUTION.mdAdded by AutoGineer871 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md6.9 KiB
- agents/openai.yaml265 B
- references/audit-rubric.md5.3 KiB
- scripts/build_evidence_inventory.pyScript13.1 KiB
scripts/build_evidence_inventory.py
#!/usr/bin/env python3
"""Build deterministic originality-audit leads from a site and references."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import subprocess
import sys
from collections import Counter, defaultdict
from datetime import datetime, timezone
from pathlib import Path
from typing import Iterable
TEXT_EXTENSIONS = {
".css",
".csv",
".htm",
".html",
".js",
".json",
".jsonl",
".jsx",
".md",
".mdx",
".scss",
".svg",
".toml",
".ts",
".tsx",
".txt",
".xml",
".yaml",
".yml",
}
IMAGE_EXTENSIONS = {
".avif",
".bmp",
".gif",
".jpeg",
".jpg",
".png",
".svg",
".tif",
".tiff",
".webp",
}
VIDEO_EXTENSIONS = {".avi", ".m4v", ".mov", ".mp4", ".mpeg", ".mpg", ".webm"}
SKIP_DIRECTORIES = {
".git",
".next",
".turbo",
".wrangler",
"__pycache__",
"build",
"coverage",
"dist",
"node_modules",
}
MAX_TEXT_BYTES = 2_000_000
SHINGLE_SIZE = 8
NUMBER_PATTERN = re.compile(
r"(?<![\w.])(?:[$€£¥]\s*)?\d[\d,.]*(?:\s?%|\s?(?:ms|sec|secs|seconds|minutes|hours|days|weeks|months|years|k|m|b))?(?![\w.])",
re.IGNORECASE,
)
TOKEN_PATTERN = re.compile(r"[\w€£¥$%]+(?:['’.-][\w€£¥$%]+)*", re.UNICODE)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Inventory exact files, text overlap, number reuse, and Git-history matches."
)
parser.add_argument("--site", required=True, type=Path, help="Site project root")
parser.add_argument(
"--reference",
required=True,
action="append",
type=Path,
help="Reference file or directory; repeat as needed",
)
parser.add_argument("--output", type=Path, help="Write JSON to this path")
parser.add_argument(
"--no-history",
action="store_true",
help="Skip the Git-history blob check",
)
return parser.parse_args()
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def classify(path: Path) -> str:
suffix = path.suffix.lower()
if suffix in VIDEO_EXTENSIONS:
return "video"
if suffix in IMAGE_EXTENSIONS:
return "image"
if suffix in TEXT_EXTENSIONS:
return "text"
return "asset"
def iter_files(targets: Iterable[Path]) -> list[Path]:
found: list[Path] = []
for target in targets:
resolved = target.expanduser().resolve()
if not resolved.exists():
raise FileNotFoundError(f"Missing input: {resolved}")
if resolved.is_file():
found.append(resolved)
continue
for path in resolved.rglob("*"):
if not path.is_file():
continue
if any(part in SKIP_DIRECTORIES for part in path.parts):
continue
if path.name == ".DS_Store":
continue
found.append(path)
return sorted(set(found))
def relative_label(path: Path, roots: list[Path]) -> str:
for root in roots:
resolved = root.expanduser().resolve()
if resolved.is_file():
if path == resolved:
return resolved.name
continue
try:
return f"{resolved.name}/{path.relative_to(resolved)}"
except ValueError:
continue
return str(path)
def file_record(path: Path, roots: list[Path]) -> dict[str, object]:
return {
"path": relative_label(path, roots),
"bytes": path.stat().st_size,
"sha256": sha256(path),
"category": classify(path),
}
def read_text(path: Path) -> str | None:
if path.suffix.lower() not in TEXT_EXTENSIONS:
return None
if path.stat().st_size > MAX_TEXT_BYTES:
return None
try:
return path.read_text(encoding="utf-8", errors="ignore")
except OSError:
return None
def normalized_tokens(text: str) -> list[str]:
return [match.group(0).lower() for match in TOKEN_PATTERN.finditer(text)]
def shingles(tokens: list[str]) -> set[str]:
if len(tokens) < SHINGLE_SIZE:
return set()
return {
" ".join(tokens[index : index + SHINGLE_SIZE])
for index in range(len(tokens) - SHINGLE_SIZE + 1)
}
def text_overlap(
site_files: list[Path],
reference_files: list[Path],
site_roots: list[Path],
reference_roots: list[Path],
) -> tuple[list[dict[str, object]], list[dict[str, object]]]:
reference_index: dict[str, set[str]] = defaultdict(set)
reference_counts: dict[str, int] = {}
reference_numbers: dict[str, set[str]] = {}
for path in reference_files:
text = read_text(path)
if text is None:
continue
label = relative_label(path, reference_roots)
file_shingles = shingles(normalized_tokens(text))
reference_counts[label] = len(file_shingles)
reference_numbers[label] = {
match.group(0).strip().lower() for match in NUMBER_PATTERN.finditer(text)
}
for shingle in file_shingles:
reference_index[shingle].add(label)
overlaps: list[dict[str, object]] = []
number_overlaps: list[dict[str, object]] = []
for path in site_files:
text = read_text(path)
if text is None:
continue
site_label = relative_label(path, site_roots)
site_shingles = shingles(normalized_tokens(text))
matches: Counter[str] = Counter()
examples: dict[str, list[str]] = defaultdict(list)
for shingle in site_shingles:
for reference_label in reference_index.get(shingle, ()):
matches[reference_label] += 1
if len(examples[reference_label]) < 3:
examples[reference_label].append(shingle)
for reference_label, shared in matches.items():
if shared < 3:
continue
site_ratio = shared / max(1, len(site_shingles))
reference_ratio = shared / max(1, reference_counts.get(reference_label, 0))
if max(site_ratio, reference_ratio) < 0.12:
continue
overlaps.append(
{
"site_path": site_label,
"reference_path": reference_label,
"shared_8_word_sequences": shared,
"site_containment": round(site_ratio, 4),
"reference_containment": round(reference_ratio, 4),
"examples": sorted(examples[reference_label]),
}
)
site_numbers = {
match.group(0).strip().lower() for match in NUMBER_PATTERN.finditer(text)
}
for reference_label, values in reference_numbers.items():
shared_values = sorted(site_numbers & values)
if shared_values:
number_overlaps.append(
{
"site_path": site_label,
"reference_path": reference_label,
"values": shared_values[:50],
}
)
overlaps.sort(
key=lambda item: (
-int(item["shared_8_word_sequences"]),
str(item["site_path"]),
str(item["reference_path"]),
)
)
number_overlaps.sort(key=lambda item: (str(item["site_path"]), str(item["reference_path"])))
return overlaps, number_overlaps
def exact_matches(
site_records: list[dict[str, object]],
reference_records: list[dict[str, object]],
) -> list[dict[str, object]]:
by_hash: dict[str, list[dict[str, object]]] = defaultdict(list)
for record in reference_records:
by_hash[str(record["sha256"])].append(record)
matches: list[dict[str, object]] = []
for site_record in site_records:
for reference_record in by_hash.get(str(site_record["sha256"]), []):
matches.append(
{
"site_path": site_record["path"],
"reference_path": reference_record["path"],
"sha256": site_record["sha256"],
"category": site_record["category"],
"bytes": site_record["bytes"],
}
)
return matches
def basename_matches(
site_files: list[Path],
reference_files: list[Path],
site_roots: list[Path],
reference_roots: list[Path],
) -> list[dict[str, str]]:
reference_names: dict[str, list[Path]] = defaultdict(list)
for path in reference_files:
reference_names[path.name.lower()].append(path)
matches: list[dict[str, str]] = []
for site_path in site_files:
for reference_path in reference_names.get(site_path.name.lower(), []):
matches.append(
{
"site_path": relative_label(site_path, site_roots),
"reference_path": relative_label(reference_path, reference_roots),
"basename": site_path.name,
}
)
return matches
def run_git(site_root: Path, args: list[str], input_bytes: bytes | None = None) -> bytes:
return subprocess.check_output(
["git", "-C", str(site_root), *args],
input=input_bytes,
stderr=subprocess.DEVNULL,
)
def git_history_matches(
site_root: Path,
reference_files: list[Path],
reference_roots: list[Path],
) -> list[dict[str, object]]:
try:
run_git(site_root, ["rev-parse", "--is-inside-work-tree"])
objects_output = run_git(site_root, ["rev-list", "--objects", "--all"]).decode(
"utf-8", errors="replace"
)
except (subprocess.CalledProcessError, FileNotFoundError):
return []
historical_paths: dict[str, set[str]] = defaultdict(set)
for line in objects_output.splitlines():
oid, _, path = line.partition(" ")
if path:
historical_paths[oid].add(path)
matches: list[dict[str, object]] = []
for reference_path in reference_files:
try:
oid = run_git(
site_root,
["hash-object", "--stdin"],
input_bytes=reference_path.read_bytes(),
).decode("ascii").strip()
except (OSError, subprocess.CalledProcessError):
continue
paths = sorted(historical_paths.get(oid, set()))
if paths:
matches.append(
{
"reference_path": relative_label(reference_path, reference_roots),
"git_blob": oid,
"historical_site_paths": paths,
}
)
return matches
def category_counts(records: list[dict[str, object]]) -> dict[str, int]:
return dict(sorted(Counter(str(record["category"]) for record in records).items()))
def main() -> int:
args = parse_args()
site_root = args.site.expanduser().resolve()
reference_roots = [path.expanduser().resolve() for path in args.reference]
if not site_root.is_dir():
print(f"Site root is not a directory: {site_root}", file=sys.stderr)
return 2
try:
site_files = iter_files([site_root])
reference_files = iter_files(reference_roots)
except FileNotFoundError as error:
print(str(error), file=sys.stderr)
return 2
site_records = [file_record(path, [site_root]) for path in site_files]
reference_records = [
file_record(path, reference_roots) for path in reference_files
]
overlaps, number_overlaps = text_overlap(
site_files,
reference_files,
[site_root],
reference_roots,
)
report = {
"generated_at": datetime.now(timezone.utc).isoformat(),
"site_root": str(site_root),
"reference_roots": [str(path) for path in reference_roots],
"summary": {
"site_files": len(site_records),
"reference_files": len(reference_records),
"site_categories": category_counts(site_records),
"reference_categories": category_counts(reference_records),
},
"exact_file_matches": exact_matches(site_records, reference_records),
"historical_exact_matches": (
[]
if args.no_history
else git_history_matches(site_root, reference_files, reference_roots)
),
"basename_matches": basename_matches(
site_files,
reference_files,
[site_root],
reference_roots,
),
"text_overlap_leads": overlaps,
"number_overlap_leads": number_overlaps,
"notes": [
"Leads are not automatic plagiarism findings.",
"Review rendered context, licenses, visual composition, and video motion manually.",
"Generic numbers and common functional phrases may be false positives.",
],
}
output = json.dumps(report, indent=2, ensure_ascii=False) + "\n"
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(output, encoding="utf-8")
else:
sys.stdout.write(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python
Security
What our scanner noticed· Low risk
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Meng To
- Publisher
- Meng To (Community)
- Source
- MengTo/Skills · agent-skills/codex/audit-reference-originality · commit 5ea1661
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Meng To or Meng To.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download audit-reference-originality.zip