audit-attribution — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited audit-attribution (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Run an essay or draft through the library to surface under-attribution: the gap between an essay properly citing sources and an essay where a careful reader can tell what's being built on versus what's being contributed. Citation count is not the metric; the metric is whether the build/contribute boundary is visible from the text alone.
When synthesising across multiple bodies of work, an author can produce an essay that:
This is not plagiarism—citations exist—but it is under-attribution: the reader can't tell from the text alone what the author is building on versus contributing. The fix is provenance markers that make the build/contribute boundary visible.
A 30-citation essay can still under-attribute. Citation breadth is not citation depth.
answer-from-corpus with an audit framing, not this skill.Read the essay in full. Identify:
Read the topic map first, fetch the concept rows it points to, then query Chroma, then grep the library as fallback. Queries are derived from:
#### Step 2a: Precondition gate: read the topic map, then fetch rows
This is a precondition, not a guideline. The concept index's topics block is a curated map: it does sense-making about how the corpus clusters that keyword search cannot recover. Read the map whole; fetch only the rows it points to.
id → slug map lives under the top-level slugs key.answer-from-corpus under Reading the concept index. On schema 3, read the topics block whole (the header's topic_lines range, nothing else of the file). Pick topics generously for each load-bearing claim and each section of the essay's argument, then fetch their rows in one grep (grep -F -e '"t012' -e '"t045' concept-index.json). Then grep each named coinage and distinctive term case-insensitively (grep -i -F 'term' concept-index.json): a coinage that lands on a row is someone's, and the row's source_ids say whose. On schema 2, read the whole file.refs key). Grep it by author or title to resolve the essay's already-cited authors, and to tell candidates apart when rows leave them ambiguous. It runs to 400KB or more on a big corpus, so look entries up rather than reading it whole.The slug-table or the topics block may exceed the Read tool's single-call token cap. Read in paginated chunks (offset + limit, increase offset by limit each call) to the end of the file or the end of topic_lines. Before proceeding to any other step, declare in plain text what you read and fetched (e.g., "slug-table.json read: L1–L420, complete; topics block L4–L311, complete; rows fetched for t012, t045, t103; coinages grepped: 3, 1 hit"). The declaration is the gate.
Grep fetches rows; it never stands in for reading the map. If you notice yourself grepping the topics block for keywords instead of reading it, return to the read: picking topics from the whole list is what finds the neighbours a keyword misses.
{corpus-root} is the active corpus directory: corpus.commons/{corpus}/ for a commons corpus, corpus.local/{corpus}/ for a local corpus. When the skill runs inside a deployed application (apps/{profile}/), the indexes live at bare paths (./slug-table.json, ./concept-index.json) and no reference-index ships.
#### Step 2b: Inside an app, read the relevant task-axis indexes
In the corpus, skip this step: the concept index is the router there, as in answer-from-corpus project mode. Inside a compiled app, read whichever task-axis indexes are relevant to the essay's territory, ./distillations/{task}/task-index.json for each applicable task domain, whole, and add their line ranges to the declaration.
#### Step 2c: Query Chroma via matching-references
With n_results: 50 and queries derived from the essay's load-bearing claims and named coinages. The skill returns metadata (filenames + author + similarity scores), not document bodies. Chroma catches semantic neighbours the index summaries don't surface by literal keyword. Apply the relevance floor (0.5 cosine similarity).
Grep belongs to the library. When Chroma is unavailable and index reading hasn't surfaced coverage for a sub-claim, grep {corpus-root}/references/*.md: the library files themselves. The distinction is load-bearing: the library is content to find by keyword; the topics block is a curator map to read whole.
The output of Step 2 is a candidate set, not a final list. Step 3 narrows it.
The skill must NOT surface every adjacent author. Apply the strongest-form check to each candidate:
Would the candidate themselves, reading this essay, feel their work was being passed off?
If plausibly yes → flag for diagnosis. If clearly no → drop. If ambiguous → drop unless the overlap is in the load-bearing contribution. The discipline is the same as matching-references's applicability gate: have an opinion, surface only the borderline-yes cases.
A useful secondary filter: would a reader of only this neighbour's most-cited work mistake the essay's distinctive contribution for theirs? If yes, the candidate is a closest neighbour.
Aim for 1-4 closest neighbours. More than that is over-flagging; this skill should produce a tight list.
For each candidate that survives Step 3, read their light reference and the relevant deep reference. Lights tell you the candidate's argument shape; deeps surface the specific catalogues, coinages, and named cases that overlap with the essay.
The Pass 3 regime here is Citation-grade: the audit's whole purpose is defensible attribution, so deeps are mandatory. Skip a deep only if the candidate is light-only in the corpus.
For each closest neighbour, classify the overlap as one of:
A single neighbour may have overlap of multiple types; name them all.
For each (candidate × overlap), check:
Three checks, three possible failures per overlap. The shape of the failure determines the fix.
Match the fix to the failure shape:
Each fix should be a suggested sentence, not just a category: the author can reject or refine, but the audit should produce concrete language.
Independent of the closest-neighbour pass, scan the essay for capitalised or in-quotes phrases. For each, check the corpus: is this someone's coinage? If yes:
Scan the essay's citations for a particular failure pattern: the author claims to have come through one source to another that source didn't actually name. Example: "I came to the PERT story via Poppendieck": true biography, but if Poppendieck didn't write the PERT story (Sapolsky did), the citation reads as deferential while being misleading about provenance.
The rule: citations exist for the reader's verification, not the author's autobiography. Name the primary source; only flag the secondary route if it materially shaped the reading. If the author's reading path doesn't load-bear, drop the secondary citation.
Re-read the load-bearing contribution list from Step 1. For each claim, check that it's explicitly claimed somewhere in the essay, not just implicit among attributions. If the author has buried their distinctive contribution among citations, flag for promotion: the reader can't tell the contribution exists if it's not stated.
This is the inverse failure to under-attribution: the author isn't passing off others' work as theirs; they're passing off their own work as others'.
Use the output shape below. The audit is a structured punch list, not prose.
ESSAY: [filename or first-line]
LOAD-BEARING CONTRIBUTIONS (the author's distinctive synthesis):
1. [claim 1, in plain language]
2. [claim 2]
[3, 4, ...]
CLOSEST NEIGHBOURS:
1. [Author, Work]
- Overlap type: [catalogue | argument | structural | phrase | origin-story]
- Overlapping with: [section number / specific claim]
- Currently attributed: [yes / partial / no]
- Named in proximity: [yes / no]
- Distinguishing language present: [yes / no]
- Strongest-form check: [would the candidate feel passed off? brief reasoning]
- Recommended fix: [acknowledging sentence | synthesis claim | boundary statement | provenance correction]
- Suggested wording: "[one sentence the author can use or refine]"
2. [...]
NAMED COINAGES TO TRAVEL WITH ATTRIBUTION:
- "[phrase 1]" (Author) — appears in §[N], §[M] without attributor in proximity. Suggested fix: [name the attributor in each paragraph where it appears, especially in tear-off-prone sections]
- "[phrase 2]" ...
PROVENANCE CORRECTIONS:
- [§N citation of X-via-Y]: false-confessional. The author read [primary] via [secondary], but [secondary] didn't actually treat this material. Suggested: cite [primary] for verification; mention [secondary] only if the reading path materially shaped the framing.
SILENT-DEMOTION FLAGS (the author's contribution is buried):
- [Claim from load-bearing list] is implicit but never explicitly claimed. Suggested: promote to a distinct sentence in §[N].
CITATIONS PRESENT BUT NOT AT RISK:
[Brief list of authors cited where the citation is doing the right work — included so the audit doesn't read as "everything is broken." The skill should NOT recommend changes to these.]
---
*Trace [Audit]: read essay (X words) → Pass 1 (topics block + rows for N topics + coinage greps, Chroma → M candidates) → strongest-form filter → K closest neighbours retained → Pass 3 deeps (K authors, citation-grade) → 11-step audit complete*matching-references: framework-discovery routing via Chroma. Called from Step 2c.answer-from-corpus with an audit framing). This skill checks whose argument shape this is, not who's right.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.