---
name: transcript-timeline
description: Turn one interview transcript note into a themed timeline canvas, one row per theme, every extract a card placed at its timestamp and quoting the speaker verbatim with a link back to the source line. Use when the user invokes /transcript-timeline, says "map this interview", "timeline this transcript", "build a transcript timeline", "lay this call out over time", or names a TRANSCRIPT-N and asks to see what was said when. Triaging a transcript into work and attention ("triage this transcript") belongs to attention-planner, not this skill. Runs on the cnotes CLI and expects the cnotes skill to be installed.
metadata:
  short-description: One interview, themes as rows, quotes on a timeline
---

# Transcript timeline

The job: let someone who missed the interview see what was said, by whom, and
when, without reading the transcript. Each theme is a row. Time runs left to
right. Each card is one extract quoted word for word, and it links back to the
line it came from.

Felt from the chair: "I can see where the pain clustered and where they
contradicted themselves, and one click takes me to the exact line." Judge every
write by that sentence.

Everything runs through `cnotes`. The **cnotes skill** documents every command
used here (flags, JSON shapes, operations, relationship mentions, canvas
layout, the humanizer). Consult it rather than guessing. This skill adds no
commands.

## The model

| Concept | CreatorNotes shape |
|---|---|
| Source | One **Transcript** note holding a pasted Zoom transcript (`HH:MM:SS` or `MM:SS` stamps, speaker labels). Read it, never rewrite it |
| Extract | One note per extract, typed by its class (Observation, Claim, Problem, Idea, Requirement, Quote, Tension when the workspace has them). Body opens with the verbatim quote. Mapping and fallbacks in [references/classification.md](#classification) |
| Card | That note placed on the canvas at its timestamp. Shape in [references/card-format.md](#card-format) |
| Theme | A section frame spanning the full timeline width, one per theme, 3 to 6 per interview |
| Canvas | `<Interviewee or company> · Interview timeline`, one per transcript |
| Banner | Four short lines above row one. The last is a bare `Read left to right.` |

## Run

1. **Read the source.** `cnotes notes get TRANSCRIPT-N --content-only`. Note the
   first and last timestamps and the speakers. If the stamps are missing or
   not monotonic, stop and say so; a timeline without times is a guess.
2. **Read the rubrics.** `cnotes types list`, then `cnotes types show <Type>`
   for every type you will write. Author each note to its rubric. Never create
   a type; fall back as classification.md says.
3. **Extract.** Walk the transcript once. Each extract is the shortest run of
   the interviewee's own words that carries one point, with its start stamp
   and speaker. Keep the words exactly, including "um" when it changes the
   meaning; cut with `…` only at the ends. Interviewer lines are context, not
   extracts. Aim for 15 to 40 extracts in a 30-minute call.
4. **Classify, with guards.** Score every candidate class from 0.0 to 1.0 per
   [references/classification.md](#classification). For each
   extract record: the driving phrase (the exact words, copied from the
   transcript, that tipped the label), the top class and its score, and a
   one-line reason. If the top two scores are within 0.15 of each other, it is
   a **close call**: do not pick. Keep both classes on the card, tag it
   `needs-label-choice`, and let a person choose. Mark Contradiction only when
   two extracts from this call disagree, and keep both.
   Before writing, check every driving phrase is a verbatim substring of the
   transcript (`grep -F`); fix or drop any that is not.
5. **Theme.** Group extracts into 3 to 6 themes named in the interviewee's
   terms ("Month-end close", not "Process friction"). An extract sits in one
   theme only.
6. **Open a draft, then the run.** Build inside a private draft by default so a
   person reviews the board before it lands live:
   `cnotes draft open "<Interviewee> interview timeline" --intent "<what is missing now, in the reviewer's words>"`.
   The intent must name the problem, not the task, or the server rejects it.
   Then `cnotes operations begin --prompt "Mapped <interview> onto a timeline"`.
   Only work live when the user asks for it.
7. **Write the notes.** One `cnotes notes create --notes '<json array>'` with
   every extract, each body in the card format, each linking the source with
   `[TRANSCRIPT-N: <title>](relationship:derived-from)` and its timestamp.
   Contradictions also link each other with `relationship:contradicts`.
8. **Build the canvas.** Each edit is its own command:
   - `cnotes canvas create "<name>" --goal "Show what <who> said about <topic>, in order. Done when every extract sits in its theme row at its timestamp."`
   - the banner: `cnotes canvas add-text <canvasId> --content-file banner.md --size medium --x 0 --y 0`
   - one `cnotes canvas add-section <canvasId> --name "<theme>" --x 0 --y <rowY> --width <w> --height <h>` per row
     (add-section takes `--x/--y` directly; no follow-up `update-section` needed)
   - one `cnotes canvas add-node <canvasId> --note <ID> --x <x> --y <y>` per card, coordinates from [references/layout.md](#layout)
   - a contradiction pair gets `cnotes canvas add-edge <canvasId> --source <ID> --target <ID> --label "contradicts" --line-style dashed --arrow none`
     (contradiction has no direction; the default arrow is `end`)
   Raw `--x/--y` is deliberate here: position carries meaning. Run
   `cnotes canvas status <canvasId> --json` after the cards land and fix overlaps
   with `cnotes canvas move-node <canvasId> --node <items[].id> --type <note|richtext|section> --x <x> --y <y>`
   per layout.md (`--node` is `items[].id` from that JSON; the table's REF is a display ID, which move-node rejects), never `canvas place` or
   `canvas arrange`, which would throw away the time axis.
9. **Check.** `cnotes canvas read <canvasId>`: every extract appears under its
   theme, every card links its source. Then the three completion tests from
   the cnotes skill. End the run with `cnotes operations end` (add `--unsure "<call and alternative>"`
   where a theme or class was a judgement call, and list every close call). Then `cnotes draft status`: zero blocking checks.
   Do not ask, accept or merge; run `cnotes draft leave` so the shared CLI is not left checked out.
10. **Report.** The PROPOSAL ref and review link, the canvas ref and link, the count per theme, and any
    contradiction found. Nothing else.

## Optional: resolve and delegate

Only when asked ("resolve these", "who owns what"). Run it before step 9's
`operations end`, inside the same draft.

1. Group related extracts into 12 to 20 outcomes in the interviewee's own
   vocabulary: Opportunity, Need (a Requirement), Desire (an Idea, fallback
   Feedback), Pain point (a Problem, fallback Observation), Decision input (a
   Question), or Watch (a Risk). No tickets: those live in other systems. Read
   each type's rubric first; never create a type.
2. One `notes create` batch. Each outcome states the outcome, `## Owner` (a
   role: Product, Finance-ops research, the user, or an agent), `## Next
   action`, `## Driving quote` (verbatim, from one source extract),
   `## Confidence` (score and reason, same guard as step 4), and `## Derived
   from` with a `[ID: Title](relationship:derived-from)` per source extract. Questions also carry what they gate, a lean, and what
   would flip it.
3. Six sections to the right of the rows, top to bottom: Opportunities,
   Needs, Desires, Pain Points, Decision Inputs, Watch. One `cnotes canvas add-section <canvasId> --name "<section>" --notes <ids> --columns 3 --x <right of rows + 280> --y <below the previous section>` each.
4. At least one `becomes` edge into each Need, Desire and Pain Point card,
   three to five elsewhere: `canvas add-edge ... --label "becomes"` from a source extract
   to its outcome; pick short, non-crossing ones. Then `canvas status`: no
   overlaps.

## Writing rules

1. Quotes stay verbatim. Never tidy grammar, merge two turns, or paraphrase
   inside the quote block. Your own words go in the title only.
2. Titles say the point in the interviewee's direction: "Reconciling two
   ledgers eats the first Monday of every month".
3. Name notes with display IDs as link text, always as a relationship
   mention. Never bare prose IDs.
4. No caption or explainer slop. No "This card captures…", no "Key insight:",
   no recap text cards under rows. A row's frame name is its only label.
5. The banner is four short lines: what happened (dated, named), what the
   board is for, who acts on it, then `Read left to right.` and nothing more.
6. Run the humanizer from the cnotes skill on every title and the banner.
7. End-user PII stays out. Name the interviewee by role and company unless
   the transcript note already names them in its title.

## Invariants

1. The transcript note is never edited or tagged by this skill.
2. Every card is a note with a verbatim quote, a verbatim driving phrase, a
   confidence score with reason, and a `derived-from` link to the source,
   carrying its timestamp.
7. A close call (top two within 0.15) is never resolved by the skill. Both
   labels stay on the card, tagged `needs-label-choice`, until a person picks.
3. x is a pure function of the timestamp, the same scale for every row.
4. One theme per extract; one row per theme.
5. Each canvas edit is its own command.
6. Nothing is triaged, scheduled, or turned into an Attention card. That is
   attention-planner's job. The optional resolve step names owners and next
   actions only; it schedules nothing.

## Card format

Each extract is one note. The canvas card is that note.

### Shape

```markdown
# <Point in the interviewee's terms, under 12 words>

> <verbatim quote>

**<Speaker role, Company>** · <HH:MM:SS>

Source: [TRANSCRIPT-N: <transcript title>](relationship:derived-from) at <HH:MM:SS>

Driving phrase: "<exact words from the quote that drove the label>"

## Class
<Class> · confidence <0.00-1.00>: <reason, under 15 words>
```

Close call (top two within 0.15), replacing the Class block:

```markdown
## Class: needs your choice
- [ ] <Class A> · confidence <0.xx>: <reason>
- [ ] <Class B> · confidence <0.xx>: <reason>
```

- Driving phrase: verbatim substring of the quote, in straight double quotes.
- Confidence: two decimals, always with a reason.
- Close-call cards add the tag `needs-label-choice`.

### Outcome cards

```markdown
# <Outcome in the interviewee's terms>

<Two or three sentences: what it is and why it matters.>

## Driving quote
> <verbatim, from one source extract> (<HH:MM:SS>)

## Confidence
<0.xx>: <reason>   (or both labels with scores, as above, for a close call)

## Owner
<role>

## Next action
<one concrete step>

## Derived from
- [<ID>: <title>](relationship:derived-from)
```

- Title: your words, the point, no class prefix.
- Quote: exact words, `…` only at the ends.
- Contradiction cards add one line under Source:
  `Contradicts [OBSERVATION-M: <title>](relationship:contradicts)`.
- Tags: `transcript-timeline`, the theme slug.

### Filled example

```markdown
# Month-end close eats the first three days

> Honestly the close eats the first three days of every month, I basically don't see my kids.

**Finance lead, Harrowgate Bikes** · 00:06:42

Source: [TRANSCRIPT-31: Sample · Harrowgate Bikes interview](relationship:derived-from) at 00:06:42

Driving phrase: "eats the first three days"

## Class
Pain point · confidence 0.92: names a recurring cost in time
```

## Classification

One class per extract. When torn between two, pick the one that names what
the speaker would act on.

| Class | Definition | Tell-tale phrasing | Example | Note type (fallback) |
|---|---|---|---|---|
| Observation | Something the speaker saw or does, stated as fact about their world | "What we do is…", "Every month we…", "I noticed…" | "We export to Excel every Friday and fix the codes by hand." | Observation (Note) |
| Claim | An assertion about cause, value or the market that could be wrong | "The reason is…", "Nobody uses…", "It's always…" | "Honestly, nobody on the floor reads the dashboard." | Claim (Assumption) |
| Pain point | A cost they feel now: time, money, risk, embarrassment | "drives me mad", "we lose", "it takes forever", "I dread" | "Close eats the first three days of every month." | Problem (Observation) |
| Desire | Something they wish for, framed as a want, not a must | "I'd love…", "It'd be nice if…", "If I had a magic wand…" | "I'd love to see variance before the CFO does." | Idea (Feedback) |
| Need | A requirement without which they won't adopt or can't work | "We have to…", "It must…", "Without X we can't…", "Audit needs…" | "Anything we buy has to keep a full audit trail." | Requirement (Note) |
| Quote-worthy (optional) | A line worth repeating verbatim in a deck or pitch | Vivid, short, self-contained | "Spreadsheets are where our accuracy goes to die." | Quote (Note) |
| Contradiction (optional) | Two extracts from this call that cannot both be true | A later turn reverses an earlier one | "Nobody here touches Excel anymore." vs "We still export to Excel every Friday." | Tension (Insight); link the pair with `relationship:contradicts` |

### Rules

- Check `cnotes types list` before writing. Use the type in the last column if
  it exists, otherwise its fallback. Never create a type.
- Read each type's rubric with `cnotes types show <Type>` before authoring.

### Guards

1. **Driving phrase.** For each extract, name the shortest exact run of words
   inside the quote that tipped the label (usually a tell-tale from the table).
   It must be a verbatim substring of the transcript. Check with
   `grep -F "<phrase>"` on the saved transcript; no match means fix or drop.
2. **Confidence score, 0.0 to 1.0,** for the top class, with a reason under
   15 words.
   - 0.85 to 1.0: the driving phrase alone carries the class.
   - 0.6 to 0.84: the phrase points that way, context confirms it.
   - below 0.6: you are reading between lines. Still goes on the board.
   Also score the runner-up class.
3. **Close call.** If top minus runner-up is under 0.15, do not choose. The
   card shows both classes with scores, gets the tag `needs-label-choice`,
   and takes the note type of the higher one (ties: the one the speaker
   would act on). A person picks; the skill never resolves it later.

### Outcome sections

Outcomes use the interviewee's vocabulary, not delivery-system words.
Tickets belong to other systems and are not produced.

| Section | Note type (fallback) |
|---|---|
| Opportunities | Opportunity |
| Needs | Requirement (Note) |
| Desires | Idea (Feedback) |
| Pain Points | Problem (Observation) |
| Decision Inputs | Question |
| Watch | Risk |

Each outcome carries the same guards: a driving quote copied from one source
extract, a confidence score with reason, and both labels when it is a close call.
- Interviewer lines are never extracts.

## Layout

Position carries meaning, so cards use raw `--x/--y` from the math below.

### Constants

| Name | Value | Why |
|---|---|---|
| CARD_W | 340 | default note card width |
| CARD_H | 130 | default note card height |
| PX_PER_MIN | 120 | a 30-minute call is 3,600px wide |
| LEFT | 80 | inset inside a row frame |
| ROW_GAP | 80 | between frames |
| BANNER_H | 400 | space reserved above row one (a four-line medium text renders about 375px tall) |

### Timestamp to x

```
t   = seconds since the first stamp in the transcript
x   = LEFT + round(t / 60 * PX_PER_MIN)
W   = LEFT * 2 + ceil(T_end / 60 * PX_PER_MIN) + CARD_W   # every frame gets the same width
```

Calls over 60 minutes drop PX_PER_MIN to 60. One scale per canvas, all rows.

### Rows

- Row order: the theme whose first extract comes earliest goes on top. Ties go
  to the theme with more extracts.
- Each row frame: `x = 0`, `y = BANNER_H + sum(previous row heights + ROW_GAP)`,
  width `W`, height `lanes * (CARD_H + 20) + 60`.
- A card's y is `rowY + 40 + lane * (CARD_H + 20)`.

### Dense clusters

Two cards collide when their x differ by less than CARD_W. Put the later one
in the lowest lane whose last card ends at least CARD_W to its left (lane 0, 1, 2), at its true x. At four lanes, keep the x,
stop stacking, and merge nothing: grow the row instead. Never nudge x to make
room; a shifted card lies about when it was said.

### Banner

A medium text element at `x = 0, y = 0`. Four short lines, no colour:

```
# <Interviewee role> at <Company> · <date>
<Interviewer> interviewed them about <topic> for <N> minutes.
Rows are themes; each card quotes them at the moment they said it.
Read left to right.
```

Line 3 may instead name who acts on the board. The last line is always the
bare `Read left to right.`

### Optional time ruler

A small text element per 5 minutes above row one (`00:05`, `00:10`) at the
matching x. Skip it for calls under 15 minutes.

### Outcome sections (optional resolve step)

Right of the rows at `x = W + 280`, stacked top to bottom with ROW_GAP between:
Opportunities, Needs, Desires, Pain Points, Decision Inputs, Watch. Three
columns each (`--columns 3`). No Tickets section. Close-call cards render
taller (two Class lines); run `canvas status` and move overlaps down, never
sideways.
