answer-from-corpus-461d41 — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited answer-from-corpus-461d41 (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
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.
Source-grounded answer protocol for any substantive question the corpus may cover. It runs in one of two modes, set by where it runs. In the project, the corpus's reference tier is present, so the skill routes through the main concept index and reads references. Inside a compiled app, the reference tier doesn't travel, so the skill reads the distillations the app ships: each carries paraphrased prose with parenthetical attribution and verbatim quotations marked [V] (open and open-nc sources only; copyrighted, confidential and personal sources are paraphrased, and the build enforces it), each matching the converted source word for word, with evidence markers ([V] / [AP] / [AR] / [AE] / [BT]) preserved.
Invoke this skill to operationalise the bundled CLAUDE.md's grounding directive: CLAUDE.md sets the principle (search before reasoning from priors; cite or admit guessing), this protocol runs it as executable steps — shape-classification, traceability, citation density, corpus breadth.
Run mode: project or app. Resolve the corpus root first (see --corpus below), then pick the mode from what's there.
references/ and reference-index.json (a corpus under corpus.commons/ or corpus.local/). Retrieval routes through the corpus's main concept-index.json and reads the reference tier, as set out under Project-mode retrieval below. Project mode never reads a distillation or a task-index. Distillations are task projections built for the apps; an answer limited to one belongs inside that app, not in the project. (When the cheaper task-index → distillation route would be good enough is an open research question. Until an eval settles it, the concept index is the project-mode route.)distillations/ and the runtime JSON indexes but no references/ (a compiled app under apps/{profile}/, which ships distillations only). Retrieval runs the distillation path in Protocols N, D and S below.Name the mode in the trace footer.
Path convention. App-mode paths are written from the app root: ./distillations/{task}/, ./concept-index.json, ./lens-index.json, ./slug-table.json. Project-mode paths are written from the corpus root: {corpus}/concept-index.json, {corpus}/references/slug-table.json, {corpus}/reference-index.json, {corpus}/references/{slug}.md (light), {corpus}/references/{slug}-deep.md (deep), {corpus}/sources/converted/, {corpus}/lens-index.json.
`--corpus {slug}` flag. When the operator passes --corpus my-corpus in the invocation (e.g., /answer-from-corpus --corpus demo What is a definition of done?), the skill targets that named corpus. In the project it resolves to corpus.commons/{slug}/ or corpus.local/{slug}/; without the flag, use the corpus the working directory sits in, and if that's ambiguous (more than one corpus and none in the working directory), list the corpora and ask. In an app, the default is the only corpus the app ships.
Before any reads, classify the question:
When ambiguous, default to the more expansive protocol (S over N, D over N). Never ask the user to disambiguate unless genuinely unclear.
After classifying the query and before any reads, check whether a lens materially reweights what's salient for this query.
Skip this step for Protocol N. Named lookups answer what does this one source say; a lens would distort, not clarify.
For Protocols D and S:
lenses.{slug}). Each entry carries kind, spec_path, purpose, reach_for_when, and a structured salience block (notices_first, recedes, native_vocabulary). Read it the way the task-axis indexes are read: as a map, not a search target. Always read it for Protocols D and S.Ground every salience claim in the supplied material and nothing else. The supplied lens reads as if that person were reading; it doesn't put words in their mouth. It can run alongside an index lens (the author's voice lens and the reader's supplied lens, say). Never conclude that context about the asker is missing until the conversation and its attachments have been read for it. When the same person is likely to come up again, offer once to persist the lens with creating-lenses.
A query about a named reader (a memo for a named colleague, a brief for the CFO) is lens-shaped whenever the conversation carries material about that reader. If the query is lens-neutral and nothing was supplied, no lens applies, and the rest of the protocol runs unmodified.
salience block carries enough of the lens's vocabulary to apply at retrieval time. Read the full spec at lenses.{slug}.spec_path only when the query needs the lens's grounding contract, fire-list, or trust-breaking-failure-mode reasoning.salience block. In project mode, apply every lens at query time. Apply a supplied lens at query time in both modes.The trace footer names the lens used (or "no lens" when the deliverable was lens-neutral).
*Step 0.5 runs before sub-claim decomposition.* The lens shapes what the sub-claims should be: a PM-lens launch plan and a builder-lens runbook decompose the same source question into different sub-claim lists.
For Protocols D and S, before reading distillations, decompose the question into the 3-10 sub-claims the answer will need to make. Write the sub-claim list out explicitly. Examples:
The sub-claim list is the coverage map for retrieval. Stop reading when every sub-claim has at least one strong source AND the next read would only thicken existing coverage.
Skip decomposition for Protocol N: a named lookup has one sub-claim by construction.
The concept index comes in two shapes; the schema_version in its header says which.
Schema 3: topics, then concept rows. The file opens with a topics block, one topic per line as [id, name, synonyms, boundary, kind, broader, concepts, sources, positions?], and the header's topic_lines gives the block's line range. The block is the map of what the library covers.
topic_lines range (Read with offset and limit) and nothing else of the file. If it's already in context from an earlier question, don't read it again.boundary note that separates close neighbours. Include debate topics when the question touches a disagreement; their positions name the sides. The lookup is cheap, so over-include here and prune at the rows.grep -F -e '"t012' -e '"t045' concept-index.json. The prefix, with no closing quote, also returns the rows on each side of a debate (tagged t045.1, t045.2). Lines that start with ["t are topic lines; skip them. Each row is [name, kind, synonyms, source_ids, topics, contexts?]. kind is concept, or example for a case, document or person that grounds the topic.grep -i -F 'term' concept-index.json); names and synonyms sit in the rows.Schema 2 (or 1): concept rows only. Read the whole file: each row is [slug, name, aliases, source_ids, contexts?] (schema 1 keys concepts by slug with {name, aliases, sources}). Paginate to the end when it exceeds the Read cap.
Either way, a row's source_ids are the sources the index credits with that concept. Some are passing mentions, so read before you cite.
In project mode, Protocols N, D and S keep their shapes (sub-claims, triangulation, regimes, stopping rules), but every read lands on the reference tier. Where the protocol sections below say "distillation", read the reference instead; where they say "task-index", use the main concept index.
Pass 1: indexes.
{corpus}/concept-index.json, the main concept index and the router, read as set out under Reading the concept index, for Protocols D and S and for Protocol N when the user names a concept rather than a source. A schema-2 index is read whole every time, and on a large corpus that runs to hundreds of thousands of tokens (a 723KB index is about 310k). A schema-3 topics block is a small fraction of that, read once per session.{corpus}/references/slug-table.json for ID ↔ slug.{corpus}/reference-index.json, the catalogue (author, year, title, concept_tags, lines_light, lines_deep, scope). Read it whole when resolving a named author or title, or when concept rows leave candidates ambiguous. It's large on a big corpus (400KB or more), so don't load it by reflex.lens-index.json, per Step 0.5.concept-index-deep.json (too large to read whole; to land on a passage, grep the deep ref instead) and not any distillations/{task}/task-index.json.Protocol D routes like Protocol S in project mode: the task domain shapes the sub-claims, and the concept index does the routing. Protocol N resolves the source through the slug-table or reference-index, then reads its light and deep refs.
Pass 2: light refs. Triage on {corpus}/references/{slug}.md for every candidate above the relevance floor, with the same diversity-aware deduplication and triangulation stopping rule as Protocol S.
Pass 3: deep refs. Read {corpus}/references/{slug}-deep.md for every author doing real work in the answer, in full, or as a labelled partial read when the defensibility unit is one passage. Deeps can exceed the Read cap; size them with lines_deep or wc -l and use the EOF-successive-reads pattern. Evidence markers come from the deep ref's audited Pass D register.
Check every quote against the source. Before a verbatim quote ships, find it word for word in {corpus}/sources/converted/. The deep ref records what a source says; it isn't the source. When a source has no converted text (EPUB-only, say), convert the original with a deterministic tool (pandoc, markitdown) into scratch space and check it there. A quote you can't find doesn't ship; paraphrase it with attribution or drop it, and note it in the trace.
The read steps below are app mode. In project mode, follow Project-mode retrieval above.
./slug-table.json ({schema_version, corpus, generated, next_id, slugs}; id → slug under slugs). When the user names an author or title, scan the slugs for a match. If the user names a concept and not a specific source, find its row in ./concept-index.json (see Reading the concept index; on schema 3, grep the name) and route from concept → sources../distillations/{task}/{slug}-{task}.md. Distillations carry author attribution in the first paragraph, paraphrased claims with parenthetical citations, and verbatim blockquotes with evidence markers ([V] / [AP] / [AR] / [AE] / [BT]) for the load-bearing passages. The distillation is what you cite from.When the named source is referenced in the corpus but no distillation projects onto the user's task, surface that gap: "The corpus has [source] but no distillation projects it onto [task]; here's what the closest task projection says."
The read steps below are app mode. In project mode, follow Project-mode retrieval above.
./distillations/{task}/task-index.json, the slug-table at ./slug-table.json, and the concept axis at ./concept-index.json. Read the task index and slug-table whole (paginate with successive offset/limit Read calls if either exceeds the cap), and the concept index as set out under Reading the concept index.The task-axis index partitions the task into named phases (each a section) and lists which sources apply (rows as [need, slug-id, when] triples). A large axis ships a split index (schema_version 2): a section with a shard field keeps only its short form in task-index.json (listener triggers with their [D#] pointers; each named disagreement's number and question), and its full rows sit in the named task-index.shard-*.json file beside it. Read the main file whole, then load only the shard for the section in play. The slug-table resolves slug-IDs to distillation file paths: ./distillations/{task}/{slug}-{task}.md. The concept-index does concept routing — task-index rows carry slug-IDs but not concept pointers, so the concept-index is what lets you land on a named concept inside the source the task-index named. With a schema-3 index, also pick topics for the sub-claims the task index leaves thin: its phases carry the practitioner's situation, the topics carry what the literature says about it.
Stub-or-empty-task-index escape clause. If task-index.json is a stub (sections: [] or generated_from: "stub-*"), the task-axis projection has not been authored yet. In that case the corpus-level concept axis is doing all the routing work; proceed to step 3 using concept-index to identify candidate sources.
./distillations/{task}/{slug}-{task}.md are pre-projected for exactly this shape; trust the projection. Each distillation already carries verbatim citations + evidence markers in-band for the load-bearing claims — that is the citation tier in dist-only retrieval.The read steps below are app mode. In project mode, the three passes run as set out under Project-mode retrieval above; the cap-aware reading pattern, the triangulation stopping rule and the regime table apply in both modes.
Three explicit passes:
Pass 1: Routing. Decompose the question into 5-10 sub-claims. Then route via the runtime JSON indexes:
./slug-table.json: slug ↔ ID mapping; load once per session../concept-index.json: the concept axis, read as set out under Reading the concept index. The app's copy is filtered at build time to the sources whose distillations shipped, and carries no in-source (section, md_line) pointers — distillations are full-read at Pass 2, so the pointer is unnecessary at app runtime (the corpus-level concept-index-deep.json is where pointers live, for operators)../distillations/{task}/task-index.json per task domain in play.Cap-aware surfaces. Some runtime artefacts may exceed the Read tool's 25k single-call cap. For JSON indexes, read the whole object: if a single Read returns the entire file, you're done; if it exceeds the cap, paginate with successive offset/limit Read calls until EOF. Never use grep or python3 -c '... json.load ...' against a JSON index to extract a single key in lieu of reading the file. The one exception is a schema-3 concept index, where the topics block is read whole and rows are fetched by topic ID, as set out under Reading the concept index. Distillation files are small (typically 8-15KB each) and always fit in one Read.
EOF-successive-reads pattern (apply whenever an artefact exceeds the cap):
Read(file, offset=0, limit=400)
Read(file, offset=400, limit=400)
Read(file, offset=800, limit=400)
... continue until a Read returns fewer lines than `limit` (that's the EOF signal).Result: a candidate set of slug-IDs (author + title resolved from the first paragraph of each distillation; concept-axis sources from concept-index.json; task-bound rows from task-index.json). No file reads beyond the indexes in this pass.
Pass 2: Distillation read. Read the distillation for every candidate above the relevance floor, with diversity-aware deduplication (don't read three OpenStax distillations on related topics if one carries the load; spread across distinct authors and traditions). For each distillation, tag which sub-claim(s) it addresses.
Stopping rule is triangulation, not just coverage. Continue until every sub-claim has at least two sources you'd cite, AND you've identified at least one place where authors converge and one place where they disagree or emphasise differently.
Pass 3: Citation density. Distillations carry [V]-marked verbatim quotes for the load-bearing passages — that is Pass 3 in dist-only retrieval. A [V] quote in a distillation has been checked word for word against the converted source. Pull it from the distillation directly when the deliverable needs it; do not re-extract from anywhere else.
When a distillation lacks the in-band verbatim register (older distillations produced before the Pass G schema change), surface that in the trace: "distillation carries paraphrased citations only; older Pass G schema." Do not invent verbatim quotes.
Quote an author only from marked text. Put a passage in quotation marks and attribute it to an author only when the distillation marks it verbatim, with a [V] marker or as a blockquote. Distillations also put their own facilitation scripts and key terms in quotation marks, so unmarked quoted text may be the distillation's wording, not the author's. Paraphrase it with attribution instead.
Pick the regime by deliverable signal:
| Regime | Triggering signals | Pass 3 behaviour |
|---|---|---|
| Citation-grade | Regulated/scholarly/audit/disputed context; explicit "with citations"; defensibility-critical (legal review, manager pushback); user has named an author and expects rigour | Surface every in-band verbatim quote + evidence marker the distillation carries for the load-bearing claims. |
| Depth-over-breadth | Operator-bound action ("what should I do"); mechanism question ("how does X work"); disagreement adjudication; thesis-bearing prose under any length; anti-pattern detection | 2-4 load-bearing distillations read thoroughly; surface verbatim quotes where they sharpen the claim. |
| Orientation | Library-survey question ("what's in the corpus on X"); definition or one-liner; time-critical (live session); non-expert audience | Paraphrased citations sufficient; surface verbatim only when the user signals enumeration or rigour. |
Default to Depth-over-breadth when in doubt.
--with-images is passed)In project mode, the image library is at {corpus}/sources/converted/IMAGE-INDEX.yaml. In app mode, it's present only if the corpus operator opted to bundle it at ./sources/converted/IMAGE-INDEX.yaml; if it isn't shipped, name that in the trace footer (image-axis: not bundled in app).
Grep the YAML by source_ref for each cited source; don't full-read it (on a large corpus it can run past 1MB). A source_ref doesn't always match the reference slug (an image library may file a book under a title-first source_ref such as {title}-{author} while the reference slug is {author}-{topic}), so when the slug misses, list the distinct source_ref values and match on author or title. Look at each candidate image before using it.
Stay inside the context window with comfortable headroom for the answer-writing step (~200k tokens reserved). In app mode, distillations are small, so Pass 2 is cheap; a 50-distillation read still fits comfortably. In project mode, a schema-2 main concept index is the big fixed cost (about 310k tokens for a 723KB index), where a schema-3 topics block costs a fraction of that once per session; deep refs are the variable cost, so pick Pass 3 authors by the sub-claims they carry rather than by count.
Write in the form the user asked for (essay, list, table, etc.). Match the citation style to the form.
[V] verbatim, [AP] author paraphrase, [AR] author argument, [AE] author example, [BT] borrowed-through) where they sharpen a claim. Do not put relative file paths inline.Every quotation attributed to an author must be the author's own words. The usual slip is a summary layer's wording in quote marks: a distillation's list item, or a light reference's paraphrase, quoted as if the author wrote it. Before sending any answer that quotes a named author or work:
--input so quotes from it pass:python3 .claude/skills/answer-from-corpus/check_quotes.py ANSWER.md --input CASE.txt (or pipe the answer in with -).
LAYER and MISSING line. Either paraphrase it with attribution, or quote the passage's exact words: from the converted source in project mode, or from a [V] or blockquoted line of the distillation in an app. Run the check again until nothing is left to fix.quotes checked: N, fixed: M.Without a shell (an MCP deployment, or a sandbox with no Bash), check each attributed quote by searching for it instead: Grep sources/converted/ in project mode, or the distillations' [V] lines and blockquotes in an app. Paraphrase any quote you can't find word for word.
Full no-coverage. If the corpus does not cover the question (the indexes have no relevant entries, the candidate references or distillations don't apply) say so plainly. Do not fall back to training to fabricate a confident answer. "The corpus does not cover this; here's why, and here's what's adjacent that might help" is a valid response. Adjacent covers two distinct moves: (a) adjacent corpus material: sources already in the corpus that touch the topic obliquely, with the obliqueness named; (b) external-ingestion candidates: named authors or works the operator could ingest to extend the corpus. The second is licensed only as ingestion candidates, not citable claims — phrase as "Beck's Extreme Programming Explained would be a candidate ingestion", not as "Beck argues that...".
Partial coverage is the more common case. Where the acknowledgement lands depends on deliverable shape:
Lens-as-sole-grounded-source pattern. Real-person and role-bound lens specs sometimes cite primary sources that are not in the corpus. When the matrix axes carry no relevant material for the query but the lens spec carries the framings the query asks for, the lens spec serves as the citable reference for this run. Trace footer shape: *Trace [Synthesis, lens: chris-gagne-consultant-coach, mode: app, partial-matrix-coverage]: lens-index → spec read in full via spec_path; matrix axes carry no relevant material → answer grounded in lens spec citations*.
After the answer, append a one-line footer naming the protocol used and (for Protocols D and S) the sub-claim → source mapping.
Bracket-tag schema. The leading bracket carries:
lens: <slug>, lens: supplied:<person-slug> (<what was supplied>), both joined with +, or lens: none);mode: project or mode: app);corpus: <slug>;with-images when --with-images was passed);partial-matrix-coverage).Examples in increasing complexity:
---
*Trace [Named, lens: none, mode: app, corpus: demo]: slug-table → openstax-organizational-behavior → distillations/decision-making/openstax-organizational-behavior-decision-making.md*---
*Trace [Diagnostic, lens: builder, mode: app, corpus: demo]: stakeholder-engagement/task-index → sub-claims: identify positions/interests (openstax-OB), surface conflict safely (openstax-OB), structure conversation (liberating-structures), close to commitment (lfuo) → lens applied retrieval-time via builder.md salience-and-vocabulary*---
*Trace [Synthesis, Depth-over-breadth, lens: none, mode: app, corpus: demo]: Pass 1 (decision-making/task-index + stakeholder-engagement/task-index + concept-index) → sub-claims: framing (openstax-OB), method choice (barbrook-johnson), facilitation (liberating-structures), post-event (tc-25-20, lfuo) → Pass 2 (5 distillations read) → verbatim markers surfaced from in-band citations*The trace is mandatory unless the user explicitly requests "no trace" or "essay only".
Non-prose deliverable shapes (a list of N pull-quotes, a thread, a single-line position, "three sentences each, no synthesis") compress the trace to a one-line bracket-tag annotation in the chat scaffolding above the deliverable, not below it.
--dispatch. Parallel sub-claim retrieval to subagents. Default off.--with-images. Triggers image-axis discovery if the corpus ships the image index.matching-references first to check if you're not sure.matching-references: topic-to-source search across the corpus catalogue. Use it first if you're unsure whether the corpus covers the topic.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.