# Relationship map protocol

This is the canonical contract for `RELATIONSHIPS.md`, Scriveno's derived pairwise relationship map, including adapted output filenames such as `DEPENDENCIES.md` or `LINEAGES.md`. The goal is a single, always-complete answer to "who relates to whom, and how" -- including the pairs that have no relationship at all.

It is part of the trust layer, alongside the other derived and recorded surfaces:

- `CHARACTERS.md` -- the authored source of cast and relationships, resolved through `file_adaptations` for the work type
- `RELATIONSHIPS.md` -- the derived pairwise map, resolved through `file_adaptations` for the work type
- `STATE.md`, `RECORD.md`, `CONTEXT.md`, `PROGRESS.md`, `HISTORY.log` -- the rest of the integrity layer

The adapted cast surface is the source of truth; the adapted relationship surface is a derived view of it. They never disagree because the map is rebuilt from the cast entries, and `/scr:scan` is the trust check.

## What RELATIONSHIPS.md is for

Relationships are authored inside the adapted cast surface: each entry carries a "Relationships" summary and per-pair "Relationship-specific interactions" (trust posture, conflict pattern, speech shift, hidden agenda or fear). That is the right place to author them, but it scatters the relationship picture across entries. The adapted relationship surface is the consolidated, openable view: every pairing in one place, derived from those entries.

The template lives at `templates/RELATIONSHIPS.md`. The file is **derived**: never hand-edited, always rebuilt from disk. Like `CONTEXT.md` and `PROGRESS.md`, it is not created at `/scr:new-work`; it is generated the first time the cast reaches two entries and a relationship-changing command or a save runs.

## The completeness rule

For a cast of N entries there are N*(N-1)/2 unordered pairs. Every pair appears in the map. A pair with a described relationship shows that relationship; a pair with none shows `none`. There are no blanks: "no relationship" is a recorded value, not a gap. This is what makes the map trustworthy -- an absent pair means the derivation missed it (drift), not that the writer chose to leave it out.

## Derivation (source-first)

1. Read the cast and the relationship sections from the adapted cast surface for canonical `CHARACTERS.md`.
2. For each entry, parse the "Relationships" summary and the "Relationship-specific interactions" subsections into directed or mutual edges with a label and, where present, the interaction dynamics.
3. Enumerate every pair. Fill described pairs from step 2; mark every remaining pair `none` if the writer has established there is no relationship, or surface it as **undefined** if no one has said yet.
4. Render the pairwise matrix, the interaction notes for load-bearing pairs, and the list of undefined pairs.

Status (`deceased`, `absent`, `mentioned`) carries through from the cast entries.

## Undefined pairs and exploration

The map distinguishes `none` (the writer established there is no relationship) from **undefined** (no one has said yet). Undefined pairs are surfaced so the writer can explore them via `/scr:relationship-map --edit` or `/scr:character-touch`, or confirm `none`. The system never invents a relationship to fill a gap; it only surfaces the gap. This is the writer-in-the-loop rule the cast surfaces already follow.

## Who regenerates RELATIONSHIPS.md

The map is a safe local helper (see `docs/auto-invoke-policy.md`). It is rebuilt by the commands that change relationships or refresh derived state, never by a read-only command:

- `/scr:new-character` -- after a new cast entry and its relationships are added
- `/scr:character-touch` -- after the Relationships or Relationship-specific dimensions change
- `/scr:relationship-map --edit` -- after edits to pairings are written back to the adapted cast entries
- `/scr:save` -- alongside `CONTEXT.md` and `PROGRESS.md`, when the work type has cast and at least two entries are defined
- `/scr:scan --fix` -- when the map is missing or stale

`/scr:relationship-map` in display mode is read-only: it renders the map live from the adapted cast entries and points the writer at the adapted relationship surface, but the saved file is regenerated by the owners above.

## When this protocol does not apply

- **Work types without a cast surface.** Where `surface_applicability` marks the cast surface `not_applicable` (poetry, speech), there is no cast and no map. Skip silently.
- **Fewer than two cast entries.** A one-entry cast has no pairs; the map is not generated until a second entry exists.
- **Adapted names.** Sacred works derive `LINEAGES.md` from `FIGURES.md`; technical works derive `DEPENDENCIES.md` from `AUDIENCE.md`. The mechanism is identical; only the filenames change (see `file_adaptations`).

## Token reference

A regenerator fills these tokens in `templates/RELATIONSHIPS.md`:

| Token | Source |
|-------|--------|
| `{{LAST_UPDATED}}` | ISO 8601 timestamp of this regeneration |
| `{{LAST_COMMAND}}` | the command that triggered the rebuild |
| `{{TITLE}}`, `{{WORK_TYPE}}` | `config.json` |
| `{{CAST_COUNT}}` | number of entries in the adapted cast surface |
| `{{RELATIONSHIP_MATRIX}}` | the complete pairwise matrix |
| `{{INTERACTION_NOTES}}` | per-pair dynamics for load-bearing pairs |
| `{{UNDEFINED_PAIRS}}` | pairs with no described relationship and no `none` yet |
| `{{LAST_SCAN}}`, `{{LAST_SCAN_VERDICT}}` | `STATE.md` if recorded, else `never run` / `unknown` |

The protocol is a hint, not a hard gate. A command that cannot parse the adapted cast entries cleanly should say so and fall back to rendering what it can, rather than emit a wrong map.
