---
name: graph-curator
title: "Graph Curator (engine substrate — entity-dedup auto-apply)"
description: "Engine substrate. Applies safe entity-merge proposals (default: comma-prefix) on a schedule so the user never has to click through hundreds of trivial duplicates. Embedding-cosine pairs stay in the user-review queue."
category: meta
status: active
priority: 0
tools_granted:
  - list_merge_proposals
  - apply_merge_proposal
---

## Purpose

Comma-prefix entity duplicates (e.g. `"Lucy García Montes, gobernadora de Sucre"` + `"Lucy García Montes"` → propose merge) accumulate naturally during knowledge-base ingestion. They are mechanical to resolve — the shorter name wins, the longer gets retired, every triple gets repointed, the audit row makes the merge reversible.

A user clicking `Apply` 100+ times on these is the exact "agent should handle this" pattern Rule 12 calls out. This skill is the substrate that handles it.

## How it works

Two paths, one set of guarantees (audit + decision + activity log on every merge):

1. **Tier-B deterministic auto-apply** — a heartbeat-driven path calls `GraphCuratorService.auto_apply()` on the cadence configured in `config/curator.yaml`. The service reads `auto_apply_rules`, filters proposals to that rule-set, and applies them via `KnowledgeGraph.merge_entities_batch`. No LLM in the loop — the rule itself is the strategy.

2. **Tier-A chat-driven path** — the `list_merge_proposals` and `apply_merge_proposal` tools let the agent run the same flow in response to a user request like "audit my graph for duplicates". A specialist (librarian / philosopher) carrying this skill in `tools_granted` can list, walk, and apply one-by-one.

## Why comma-prefix is auto-applied but embedding-cosine is NOT

Documented in `memory/project_entity_dedup.md` (the user's own memory file): high-cosine pairs in the 0.85–0.95 band carry too much risk of false positives — distinct people / organizations / concepts with similar names show up in that band routinely. Auto-classifying them is the failure mode of session 107 the user explicitly rejected.

Comma-prefix is the opposite: deterministic, structural, false-positive rate near zero. Auto-apply is restricted to rules with that property.

If a future rule wants to be added to `auto_apply_rules`, the test is simple: can a non-expert verify the rule is correct from the rule alone, without inspecting individual pairs? If yes, it's safe to auto-apply. If no, it stays user-reviewed.

## Configuration

All knobs live in [`config/curator.yaml`](config/curator.yaml):

| Knob | Meaning |
|------|---------|
| `enabled` | Master toggle. Default `true`. |
| `auto_apply_rules` | List of proposal rules eligible for auto-apply. Default `[comma_prefix]`. Adding `embedding_cosine` here is allowed but discouraged — see above. |
| `max_per_run` | Cap on merges per heartbeat tick. Default `50`. Keeps any single run bounded. |
| `cadence_hours` | Min interval between heartbeat-driven runs. Default `24`. |
| `dry_run` | When `true`, logs what WOULD be applied without mutating. |

## Reversibility

Every auto-applied merge writes one row to `entity_resolution_audit` with the full BEFORE snapshot. The user can retract any merge from the Graph page's "Recent merges" panel (`POST /api/graph/merge-audit/{id}/retract`), which calls `KnowledgeGraph.unmerge_entities()` to resurrect the destroyed entity at its original slug and walk every repointed triple back. This is the same path user-initiated merges already use — the curator is just another caller.

## Decision log

Every auto-applied merge records a `decision_log` row with `actor="curator"`, `action_type="entity_merged"`, `contract_action="auto_merge_entity"`, full kept/removed slugs, repointed triple count, and rule. The `evidence` field on the audit row carries `auto_applied: true` so the Recent merges panel can distinguish curator merges from user-initiated ones.

## What this skill does NOT do

- It does not resolve embedding-cosine pairs. Those stay in the user-review queue.
- It does not delete entities outside the merge-proposal pipeline.
- It does not modify triples directly — only via the existing `merge_entities` / `merge_entities_batch` paths.
- It does not propose new rules. The rule-set is fixed by `find_merge_proposals`.

## Owner

`librarian` and `philosopher` both carry this skill on their team. Either can run the chat-driven path on user request. The Tier-B heartbeat path runs without any agent dispatch — it's the service calling the existing graph methods directly.
