---
active: false
description: "Reconcile newly-extracted playbooks against existing storage. Decides per-candidate whether each one unifies with one-or-more existing rows, is rejected as redundant against an existing row, should differentiate (refine both triggers), or is independent."
changelog: "v2.1.0: `unify` no longer emits a `polarity` field — the unified row's orientation is derived from its wording (recommendation vs avoidance) by the apply-path polarity validator. Same-trigger opposite-orientation rules still must not unify; express orientation through the rule wording. v2.0.0: collapsed 5-kind union → 4-kind. `unify` subsumes `duplicate`+`prefer_new`; `reject_new` replaces `prefer_existing`. Output schema is structurally incompatible with v1.x."
variables:
  - new_playbook_count
  - new_playbooks
  - existing_playbooks
---
You are reconciling a set of newly-extracted playbooks against the related existing playbook rows already in storage. For each new candidate, decide its relationship to the existing rows.

Each rendered row carries `Content`, `Trigger`, `Rationale`, `Polarity`, `Name`, `Source`, and `Last Modified`. The `Polarity` label (`positive` = a recommendation / do-this, `negative` = an avoidance / don't-do-this) is derived from the rule's wording. Use `Trigger` and `Polarity` as the primary keys for comparison.

[New playbooks (count: {new_playbook_count})]
{new_playbooks}

[Existing related playbooks]
{existing_playbooks}

# Decision kinds

Emit exactly one decision per NEW candidate. Each decision is one of:

- **unify** — the NEW is the same rule as one or more EXISTING rows (after merge), OR the NEW supersedes one EXISTING (stronger / broader / more specific evidence). Provide the final `content`, `trigger`, and `rationale`. List which EXISTING ids you're archiving in `archive_existing_ids` (use an empty list when no EXISTING rows are absorbed). The unified row carries a single orientation: word its `content` and `rationale` so they consistently recommend OR consistently warn against the behavior — never blend both. Every archived EXISTING must share that orientation; the apply-path polarity validator (which derives orientation from wording) will reject a mixed-orientation unify.

- **reject_new** — an EXISTING row already covers NEW or makes NEW redundant. Name the EXISTING id that wins via `superseded_by_existing_id`. Storage-stability tie-break: when same-trigger opposite-orientation evidence is balanced, default here.

- **differentiate** — both valid in distinct contexts (typically same trigger, opposite orientation, where the contexts differ). Set `refined_new_trigger` and `refined_existing_trigger` to be strictly narrower than the originals AND mutually exclusive.

- **independent** — different topic or trigger from any existing row. Insert NEW with no archive.

# Hard constraints

- A NEW + EXISTING pair with the same trigger and opposite orientation MUST route to `unify` (only if you can pick one shared orientation backed by evidence and word the unified row accordingly — the polarity validator will reject a mixed-orientation unify), `reject_new`, or `differentiate`. Never `independent`.
- Orientation is read from each row's wording (the derived `Polarity` label). A unified row's orientation follows the words you choose for its `content` and `rationale`.
- `differentiate.refined_new_trigger` and `refined_existing_trigger` MUST be non-empty and strictly narrower than the originals.
- When in doubt about a same-trigger opposite-orientation stalemate, default to `reject_new`. Storage stability wins ties.

Output strictly conforming to the `PlaybookConsolidationOutput` schema (a JSON object with a single key `decisions` whose entries each carry a `kind` discriminator).
