| name | crosscheck |
| description | Scan a knowledge base (filesystem docs, a repo, a Notion workspace, or anything reachable over MCP) for contradictions, inconsistencies, drift, and vagueness, then deliver an interactive HTML findings report. Use when the user asks to lint, audit, or check a knowledge base or docs for contradictions/inconsistencies, e.g. "run crosscheck on the notion workspace called Sales". |
crosscheck
You are running a knowledge-base lint. The argument is a natural-language sentence
describing the target corpus (e.g. run on the notion workspace called "Sales",
scan ./docs, check this repo's markdown). Follow these steps exactly.
1. Parse the target sentence
Extract from the sentence:
- source kind: filesystem path, current repo, Notion workspace/page, or another
MCP-reachable base (Slack canvases, Google Drive, …).
- mode override: if the sentence contains "one-shot"/"holistic" force holistic mode;
"map-reduce"/"full pipeline" forces map-reduce. Otherwise the router decides (step 3).
- scope hints: subfolder, page subtree, file types.
If the sentence does not identify a locatable source, ask the user — that is the one
thing you cannot guess.
2. Discover the corpus → inventory
Produce an inventory: [{ ref, title, sizeHint }] where ref is fetchable by a
subagent (absolute file path, Notion page URL/ID, …) and sizeHint is estimated tokens
(bytes / 4 for files; default 2000 when unknown).
- Filesystem / repo: enumerate documentation-like files (
.md, .mdx, .rst,
.txt, .adoc, .html) with find/Glob + sizes via Bash. Exclude vendored/build
dirs (node_modules, dist, .git). Do this inline; it's cheap.
- MCP sources (Notion etc.): load the needed tools via ToolSearch (one call, e.g.
select:mcp__claude_ai_Notion__notion-search,mcp__claude_ai_Notion__notion-fetch),
resolve the named workspace/teamspace, and enumerate its pages. If enumeration is
large or fiddly, delegate to the kb-discoverer agent and have it return the
inventory JSON. Keep its resolved and ambiguities fields — anything it reports as
inaccessible or ambiguous must appear in your final summary (step 6), never be
silently dropped.
Also write accessNotes: 1–3 sentences telling a fresh subagent exactly how to fetch
these refs (which tools to load via ToolSearch, or "Read the file paths directly").
If the inventory is empty, stop and tell the user what you looked for and where.
3. Route
Sum sizeHint. Total ≤ 120k tokens → mode: "holistic"; otherwise
mode: "mapreduce". Sentence overrides from step 1 win. Tell the user the doc count,
size estimate, and chosen mode before launching.
4. Run the workflow
Invoke the Workflow tool with the named workflow:
Workflow({ name: "crosscheck", args: {
targetDescription: "<human description of the corpus>",
accessNotes: "<how subagents fetch refs>",
mode: "holistic" | "mapreduce",
inventory: [ { ref, title, sizeHint }, ... ]
}})
The entire args value must be a JSON object, never a JSON-encoded string — do not
wrap the braces in quotes, and pass inventory as a real array. (The workflow tolerates
string args as a fallback, but don't rely on it.) The workflow returns
{ findings, stats } per the contract in DESIGN.md. If it returns empty findings,
check the run's journal before concluding the corpus is clean.
5. Render the report
- Read
templates/report.html (repo root).
- Build the data object:
{ target, generatedAt, stats, findings } — assign each
finding a stable id (f1, f2, …) if the workflow didn't. generatedAt is
today's date (YYYY-MM-DD).
- Replace the single placeholder
/*__DATA__*/null with the JSON, with every <
escaped as the JS string escape \u003c (i.e. the output of
JSON.stringify(data).replace(/</g, '\\u003c')). This is mandatory, not cosmetic:
findings quote corpus text verbatim, and a raw </script> inside the injected JSON
closes the template's script element and blanks the entire report.
- Write to
reports/crosscheck-<target-slug>-<YYYY-MM-DD>.html (create reports/).
- Send it with SendUserFile (
display: "render").
Digestibility contract: the template shows only each finding's one-line summary until
the reader clicks its "Deep dive" toggle (quotes, suggested fix, and verifier note live
there). If a workflow-returned summary runs past ~25 words, condense it faithfully
before injecting — never move evidence or advice up into the summary to compensate.
Also strip any leaked tool-call artifacts (</parameter>, </invoke>) from finding
text before injection.
6. Summarize
End with a short prose summary: how many findings by severity, the top 2–3 in one
sentence each, and anything skipped or unreachable during discovery. The HTML is the
deliverable; do not dump the full findings list as text.