Snippet schema

Maintainer reference for the curated guide builder. Source file: docs/SNIPPET_SCHEMA.md

# Curated guide composer — schema and maintenance

This document describes how **Situation First** turns visitor selections into a printable guide **without AI**: enumerated inputs, tagged snippets, deterministic merge.

## Dimensions (enums)

All values are lowercase slugs used in URLs and forms.

| Dimension   | Slug key     | Allowed values |
|------------|--------------|----------------|
| You        | `you`        | `ic`, `manager`, `founder`, `freelancer`, `student` |
| Them       | `them`       | `peer`, `boss`, `buyer`, `investor`, `public` |
| Objective  | `objective`  | `meeting`, `budget`, `close`, `align`, `teach` |
| Context    | `context`    | `cold`, `warm`, `live`, `async`, `stage` |
| Risk       | `risk`       | `low`, `career`, `revenue`, `reputation` |

Human labels live in [`src/lib/guide-schema.ts`](../src/lib/guide-schema.ts) (`LABELS`).

## Output sections (fixed order)

1. `recap` — generated sentence from labels (not from the snippet library).
2. `structure` — talk track / doc outline.
3. `openings` — 1–3 opener lines.
4. `questions` — discovery and alignment prompts.
5. `objections` — objection → response pairs (plain text).
6. `close` — explicit next step matched to objective.
7. `checklist` — printable bullets (`☐` via CSS).

Order is defined by `SECTION_ORDER` in `guide-schema.ts`.

## Snippet record

Each snippet is a row in [`src/lib/snippets.ts`](../src/lib/snippets.ts):

| Field     | Required | Description |
|-----------|----------|-------------|
| `section` | yes      | One of the section keys above (except `recap`, which is computed). |
| `lines`   | yes      | Array of strings; rendered as list items (or paragraph for recap). |
| `match`   | no       | If omitted, snippet applies to **all** values for every dimension. If present, **each** listed dimension constrains that dimension only: the visitor’s value must appear in the array for that key. |
| `weight`  | no       | Numeric tie-break for sort order before concatenation. Lower runs first; higher-specificity snippets often use higher weights so they appear after general scaffolding. |

### Matching rules

- **AND across dimensions**: a snippet matches only if **every** key in `match` is satisfied.
- **OR within a dimension**: use multiple entries in the same array (e.g. `context: ["warm", "live"]` means warm **or** live).

### Anti-patterns

- Do not add free-text fields to `match` until you have a moderation and review story.
- Avoid duplicating entire guides per tuple `(you, them, objective)`; prefer small tagged blocks.

## Merge algorithm

Implemented in [`src/lib/guide-merge.ts`](../src/lib/guide-merge.ts):

1. Build `recap` from `Selections` + `LABELS`.
2. Filter snippets with `matches(match, selection)`.
3. Sort matched snippets by `weight` ascending.
4. For each section, concatenate `lines` in sort order (later snippets append).

## Stateless deep links

The guide page accepts GET query parameters (`?you=ic&them=buyer&...`). Valid unknown values are ignored; missing keys use `DEFAULT_SELECTIONS` in `guide-schema.ts`.

## Editorial checklist when adding a snippet

- [ ] Does it read well **after** more general snippets in the same section?
- [ ] Is `match` the **smallest** constraint set that makes the advice true?
- [ ] Did you avoid absolute claims and tone-deaf pressure language?
- [ ] If it mentions pricing or legal topics, is it clearly generic education?

## Future (optional)

- **Tone packs** (`direct` vs `diplomatic`): add a dimension or tag family, then namespace snippets.
- **Specificity score**: auto-sort by count of constrained dimensions instead of manual `weight`.
- **Per-section caps**: e.g. max 7 questions, max 3 openings, to keep printouts short.

← Guide builder