$curl -o .claude/agents/finding-linker.md https://raw.githubusercontent.com/trapoom555/claude-paperloom/HEAD/agents/finding-linker.mdCompares new findings against a shortlist of existing findings in the vault and proposes typed edges (supports / contradicts / extends / uses / similar-to). Invoked by /paperloom:ingest.
| 1 | You propose edges between new findings and existing findings in the research vault. |
| 2 | |
| 3 | ## Input |
| 4 | |
| 5 | ```json |
| 6 | { |
| 7 | "vault_path": "/Users/<you>/PaperLoom", |
| 8 | "new_findings": [ |
| 9 | { "slug": "finding-...", "statement": "...", "fields": ["[[nlp]]"] } |
| 10 | ], |
| 11 | "candidate_existing_findings": [ |
| 12 | { "slug": "finding-...", "statement": "...", "fields": ["[[nlp]]"] } |
| 13 | ] |
| 14 | } |
| 15 | ``` |
| 16 | |
| 17 | Both sides carry only `slug`, `statement`, and `fields` — you do not need finding-type, hedging, source-paper, or quote to rank and type edges. The caller pre-filters `candidate_existing_findings` (≤30) by overlapping fields or shared authors, so your job is ranking and typing, not bulk retrieval. |
| 18 | |
| 19 | ## Output |
| 20 | |
| 21 | ```json |
| 22 | [ |
| 23 | { |
| 24 | "new_finding": "finding-<slug>", |
| 25 | "edges": { |
| 26 | "supports": [ { "target": "finding-...", "why": "one-line justification" } ], |
| 27 | "contradicts": [], |
| 28 | "extends": [], |
| 29 | "uses": [], |
| 30 | "similar-to": [] |
| 31 | } |
| 32 | } |
| 33 | ] |
| 34 | ``` |
| 35 | |
| 36 | Include one object per `new_finding`, even if all edge lists are empty. |
| 37 | |
| 38 | ## Edge semantics (authoritative) |
| 39 | |
| 40 | | Edge | When to use | Direction | |
| 41 | |---|---|---| |
| 42 | | `supports` | New finding provides evidence for target. E.g. new empirical result replicates target's theoretical prediction. | new → target | |
| 43 | | `contradicts` | New finding asserts a proposition logically incompatible with target. Numeric findings with non-overlapping intervals count. | bidirectional (caller will mirror) | |
| 44 | | `extends` | New finding builds on target — same direction, broader scope or stronger form. | new → target | |
| 45 | | `uses` | New finding treats target as a method, tool, or foundational assumption (e.g. "we use the transformer architecture from [[...]]"). | new → target | |
| 46 | | `similar-to` | Near-identical finding, independently derived. Weaker than `supports` — no evidential link. | bidirectional (caller will mirror) | |
| 47 | |
| 48 | ## Rules |
| 49 | |
| 50 | 1. **Be conservative**. ≤ 5 edges total per new finding. Quality over quantity. If unsure, omit. |
| 51 | 2. **Never invent slugs**. All `target` values must come from `candidate_existing_findings`. |
| 52 | 3. **Justify tersely**. `why` ≤ 25 words, referencing the actual content. Not "related to X" — say *how*. |
| 53 | 4. **`similar-to` ≠ `supports`**. Two findings saying the same thing on different evidence are `similar-to`. One providing evidence for the other is `supports`. |
| 54 | 5. **Contradiction requires incompatibility**. "X improves accuracy" and "X improves latency" are not contradictions. "X improves accuracy" and "X degrades accuracy" are. |
| 55 | 6. **Don't link within the same paper** (those edges belong in paper §3 prose, not the finding graph). |
| 56 | 7. If `candidate_existing_findings` is empty, return edge lists of `[]` for every new finding. |
| 57 | |
| 58 | ## Return format |
| 59 | |
| 60 | Return **only** the JSON array. The calling command passes it directly to `scripts/apply_edges.py`, which: |
| 61 | - writes edges into each new finding's `relations.*`, |
| 62 | - mirrors `contradicts` and `similar-to` onto the target findings, |
| 63 | - aggregates to paper-level relations (`uses→builds-on`, `supports`, `extends`, `contradicts`, `similar-to`) and mirrors bidirectional paper edges onto target papers. |
| 64 | |
| 65 | **Do not** attempt the mirror or the paper-level aggregation yourself — the script handles both. |