Process and summarize transcriptions from calls, meetings, or voice recordings. Use when user provides a transcription, mentions processing a transcript, or wants to summarize a recorded conversation.
Installs into .claude/skills of the current project.
Are you the author of Transcript?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/tandregbg-transcript)
---
name: transcript
description: Process and summarize transcriptions from calls, meetings, or voice recordings. Use when user provides a transcription, mentions processing a transcript, or wants to summarize a recorded conversation.
user-invocable: true
argument-hint: [transcription text or file path]
---
# Transcript Processing Skill
Process the provided transcription following these steps:
> **Råmaterial-spärr (raw-transcript lock):** Step 2.5 sparar alltid det inmatade råmaterialet tyst i en central `.transcripts/`-mapp i vault-roten. Filer i `.transcripts/` är ett **tyst arkiv**: de får ALDRIG läsas tillbaka, citeras, summeras om, eller matas in i `_insights.yaml`/sammanfattningar — om inte användaren EXPLICIT ber om råmaterialet (t.ex. "visa råtexten", "vad sa han ordagrant", "öppna råfilen"). Alla steg som vandrar mappar (Step 0.5 rules-walk, Step 3.5 insights) ska hoppa över `.transcripts/` precis som de hoppar över `.archive/`.
## Subcommands
### `/transcript <input>` (the default)
Process a transcript into the summary — the config-free form of `/ops process`. Everything below this section is that one action.
### `/transcript help`
Print usage: the input this skill takes, the file it writes (`YYMMDD-summary-<description>.md`), and when to use `/ops` instead — a folder with a config.
## String Resolution
Template strings marked as `{strings.section.key}` are resolved at runtime.
**Resolution order:**
1. Config `strings` section (if loaded)
2. Language-matched defaults from `base.yaml`:
- `swedish` -> `strings_sv`
- `english` -> `strings`
- `input` -> match detected transcript language
- `per_claude_md` -> look up the **target file path** in the project CLAUDE.md LANGUAGE POLICY table
3. Hardcoded strings already in template (fallback)
---
## Step 0.5: Load Applicable Rules (CR-013)
Before creating the summary, load any promoted **rules** from the `_insights.yaml` chain in scope:
1. **Walk up from CWD** collecting `_insights.yaml` files at each level (max depth 6, skip `.archive/`, `.transcripts/`, `.handoff/`, `clones/`).
2. **Filter to rules:** entries where `confidence: rule` AND `status: active`.
3. **Cap to 20 entries** — if more, prefer highest `confirmation_count`, ties broken by most recent confirmation date.
4. **Build a one-line-per-rule preamble** in the form `[type] summary`. Treat the preamble as additional standing instructions for this run.
**How rules influence transcript output:**
- `[preference]` rules guide tone, length, and format choices in the summary.
- `[decision]` rules guide naming, language, and structural defaults.
- `[pattern]` rules guide what to look for during extraction (e.g., "Bob raises customer-success topics last" → ensure that section is captured even if mentioned briefly).
Do not echo the preamble in the output. Rules influence content, not chrome.
**Scoping:** Only rules in the CWD's parent chain apply. **No rules found:** skip silently.
---
## Step 1: Create the Summary
### Heading Format
1. Check for date in filename or metadata (YYMMDD, YYYYMMDD, YYYY-MM-DD, etc.)
2. Format: `# Summary: [FILENAME] [DATE] - [MAIN TOPIC]`
3. If no date found: `# Summary: [FILENAME] - [MAIN TOPIC]`
### Structure (CR-006: action-first ordering)
**Critical: Action items first, decisions second, outcome third, discussion fourth, background last.**
A reader scanning a meeting summary at 08:30 the next morning needs to find what they own *first*, not a narrative recap. The canonical section order is:
1. **Nästa steg / Next Steps** -- Action items with owners and deadlines (table format, see Action Item Table below). This is what the reader scans first.
2. **Beslut / Decisions** -- What was decided in this meeting, with rationale. **Always include this section** even if there were no formal decisions -- in that case write `*(Inga formella beslut -- diskussionen var orienterande.)*` or the English equivalent. Honest absence is searchable; silent omission is not.
3. **Konklusion / Outcome** -- 1-2 sentences summarizing what changed as a result of this meeting. This is the wrap-up of the action items + decisions above, not a narrative of the discussion.
4. **Diskussion / Discussion** -- Notes, points raised, context. The narrative of what was talked about goes here, not in the Konklusion.
5. **Bakgrund / Background** -- Reference material moves to the bottom. Background is *reference*, not *navigation*.
**Canonical heading names (CR-006):**
| Concept | Swedish | English |
|---|---|---|
| Action items | `Nästa steg` | `Next Steps` |
| Decisions | `Beslut` | `Decisions` |
| Outcome | `Konklusion` | `Outcome` |
| Discussion | `Diskussion` | `Discussion` |
| Background | `Bakgrund` | `Background` |
| Blockers | `Blockers` | `Blockers` |
Pick **one Swedish + one English term per concept** and use it consistently. The following variants are **banished** -- do not produce them in new files: `Sammanfattning`, `Executive Summary`, `Summary`, `Action Items`, `Åtgärdspunkter`, `Huvudpunkter`, `Key Discussion Points`, `Decisions Made`. (Existing files keep their headings; readers recognise the old variants for read-only purposes.)
**Action Item Table format (CR-006):**
The Nästa steg / Next Steps section MUST use this 5-column table format:
```markdown
## Nästa steg
| # | Åtgärd | Ägare | Prio | Deadline |
|---|--------|-------|------|----------|
| 1 | Kontakta leverantören om data-export | Anna | P1 | 260410 |
| 2 | Skicka kravspec till designbyrån | Erik | P2 | 260415 |
```
If owner or deadline is unknown, write `?` rather than omitting the column. The visualisation app and `/tasks` parse this table -- variants break them silently.
**Owner attribution on undiarized input (CR-015):** an owner cell may hold a bare name **only** when an identifiable speaker explicitly takes the action. When the transcript has no speaker labels (see *Speaker attribution* below), a first-person cue alone ("jag skickar…", "då gör vi…") does **not** identify who - write `?` (or `Name?` for a likely-but-unconfirmed owner) rather than committing to a confident guess. Failing safe with `?` is correct; a confident wrong owner is not.
**Konklusion length:** there is **no minimum length** for Konklusion. A 96-word internal session can still have a 1-sentence outcome. Always include the section, however brief.
**Formatting rules:**
- Use `---` to separate main sections
- Use `##` for section headings (sentence case)
- Use bullet points for content within sections
- Adapt section length to information relevance:
- Short sections: as little as 1 point if relevant
- Central topics: as many points as needed
**Anti-pattern to avoid:** Writing a Konklusion that narrates the discussion ("Alex konfronterade Carol, Carol erkände delvis, Alex betonade...") instead of stating the outcome ("Carol ska boka uppföljningsmöte; Alex omplanerar samtalet."). The Konklusion is **outcome-tense** (decided / will / ska / shall), not narrative-tense (said / argued / discussed).
### Template Variants by Meeting Length (CR-006)
| Variant | When | Required sections |
|---|---|---|
| **Concise** | <30 min meetings, internal task sessions, dataspec reviews | Nästa steg, Beslut, Konklusion (skip Diskussion + Bakgrund unless substantive) |
| **Standard** | 30-90 min meetings, most contact samtal, standups | Full canonical structure (Nästa steg -> Beslut -> Konklusion -> Diskussion -> Bakgrund) |
| **Extended** | >90 min meetings, board, strategy, multi-topic | Full structure + per-topic numbered subsections under Diskussion + Strategic Alignment if applicable |
The skill picks a variant by estimating meeting duration from transcript metadata (timestamps, length, participant count). Default to **Standard**. Override with `/transcript --concise` or `/transcript --extended`.
**Template-contract check (CR-018):** as the last pre-save step (after Swedish-character validation), verify the summary against the matching shape contract from `workflows.meeting_templates` (else the `default` CR-006 contract): H2 heading sequence, action-table header row, and empty-Beslut marker. `warn` mode (default) saves + reports the diff + logs an `edge_case`; `strict` asks first. See ops-base "Template Contracts (CR-018)" for the registry format and rationale.
### Content Rules
- Identify and create headings for all distinct subject areas
- Exclude personal reflections, sensitive information, private discussions
- Prioritize concrete facts, business ideas, projects, technical details
- Preserve key terms and technical terminology
- Maintain original language throughout
### Name Resolution (critical)
**Never trust transcript spellings.** Transcription services often misspell names (e.g., "Andre" instead of "André", "Asa" instead of "Åsa"). Always resolve names before writing the summary:
1. Check the filename for correct spelling
2. Check config `people[]` roster for `canonical` + `aliases` (CR-017 -- covers non-contact persons: colleagues-of-counterparts, remote team members, recurring third parties)
3. Check `_contacts/*/_meta.yaml` for `display_name` and `aliases`
4. Check config `team[]` for canonical names
5. Use the resolved canonical name throughout the summary
This applies to ALL occurrences of the name -- not just filenames, but every mention in the summary content.
### Speaker attribution & undiarized transcripts (critical, CR-015)
Name Resolution above fixes *spelling*. It does **not** tell you *who said what*. Many sources (e.g. a Deep Thought paste, or any raw recorder export) arrive as a **single continuous stream with no speaker labels**. On such input, who-said-what and therefore **who owns each action item are inferred, not observed** -- and confident inference is the single most common attribution error this skill makes.
**Detect the condition first.** Before assigning any owner, decide whether the transcript is *diarized* (has speaker labels / turn markers like `Alex:` / `Speaker 1:`) or *undiarized* (one narrative blob). State the result to yourself; it changes the rules below.
**On undiarized transcripts:**
- Treat every owner as a hypothesis. A first-person cue ("jag ska skicka…", "då branchar vi ut…") tells you an action exists, **not who will do it** -- the speaker is unlabeled.
- Prefer **failing safe**: write the owner as `?`, or `Name?` when context makes one participant clearly likely but unconfirmed. Do **not** upgrade `Name?` to `Name` without explicit evidence.
- Use surrounding context to raise confidence where it is genuinely high (e.g. "du är ansvarig för X" addressed to a named participant, or an action only one party could own), but when two readings are equally plausible, choose `?`.
- This rule composes with the Action Item Table rule above -- they must agree.
**Final owner self-check (before save).** Re-read the `Nästa steg` table once. For each row ask: *was this owner explicitly claimed by, or explicitly assigned to, an identifiable speaker?* If not, downgrade the cell to `?` / `Name?`. A wrong-but-confident owner is worse than an honest `?`.
**Capture the gap.** When a transcript is undiarized, log it as an `edge_case` in Step 4.5 so the evolution loop can see how often this source shape occurs. If the user later corrects an owner, log that as a `correction` (Step 4.5).
**Root fix (out of skill scope, worth noting to the user).** The durable remedy is *diarized input* -- a speaker-labeled transcript. When one is available (e.g. a Fathom export, or once upstream diarization lands), use it as the source and this whole section becomes a no-op.
### Proper-noun verification (critical, CR-016)
Name Resolution above corrects the *spelling* of names it can **match**. It does not protect against the opposite failure: a proper noun the transcriber **garbled into something plausible that matches nothing**. Transcription services are unreliable on company and personal names in both directions -- a real surname becomes a different real-looking surname, a company name becomes a phonetic near-miss. These slip through *because* they read fine. Treat every betydelsebärande proper noun as **unverified until matched**, not as fact.
**Why this matters more than it looks (failure-mode principle).** The dangerous ASR errors are not the obvious garble -- those you catch on sight. They are the *plausible* substitutions that read cleanly and match nothing. A confident wrong name is worse than an honest `Name?`: it propagates downstream into every summary, extraction and deliverable built on this transcript. Spend scrutiny on proper nouns and semantic swaps, not on obvious noise.
**Build the known-entity set** from the sources the skill already uses:
- config `people[]` roster -- `canonical` + `aliases` (CR-017)
- config `team[]` -- `name` + `aliases`
- `_contacts/*/_meta.yaml` -- `display_name`, `aliases`, `company`
- config `terminology[].term`
- the filename
**For each person or company name in the summary:**
- **Matches the known set** -> use the canonical spelling (existing Name Resolution behavior).
- **Matches nothing** -> it is unverified. Do **not** silently commit it. Either mark the mention `Name?` (mirroring the CR-015 owner convention), or collect the unverified names into a short trailing note: `> ⚠ Namn att verifiera: <names>`. Prefer the inline `Name?` for a single uncertain mention; use the trailing note when several names are unverified.
- Never upgrade `Name?` to a bare name without explicit confirmation (filename, a known entry, or the user).
**Always correct company and personal names by hand in deliverables** -- this rule flags them; it does not guarantee them.
**Capture the gap.** When you flag one or more unverified proper nouns, log it as an `edge_case` in Step 4.5. If the user later corrects a name, log that as a `correction` (Step 4.5).
Default-on and additive: when every proper noun resolves against the known set, this section is a no-op.
### Committed-spelling consistency (critical, CR-017)
CR-016 verifies names against the *configured* known-entity set. It has no memory of what this pipeline **previously committed** -- so an ASR variant of an already-established name ("Robi" for a person the folder has called "Ravi" for months) sails through as a plausible new name, and the corpus ends up spelling one person three ways across consecutive documents of the same meeting series. A reader can see the inconsistency instantly; per-file processing cannot. This check adds the missing folder memory.
**Before saving the summary:**
1. **Extract person-like tokens** from the draft (names in the action table, participant list, and body).
2. **Scan folder precedent:** the target folder's ~10 most recent `YYMMDD-*.md` files plus its `CHANGELOG.md`, collecting previously committed person names.
3. **Near-miss detection:** for each draft name not already resolved via the known-entity set, compare against the precedent names (case-insensitive; edit distance ≤2 on tokens ≥4 chars; also catch initial-letter swaps like J/Y common in ASR).
4. **On a near-miss hit:**
- If the precedent spelling resolves via the `people[]` roster / known-entity set → **use the established spelling**, silently.
- If neither form is in any roster → do NOT silently introduce the new variant. Flag both forms in the `⚠ Namn att verifiera` note: `"Robi" -- tidigare skrivet "Ravi" (260630) -- samma person?` and use the *earlier* spelling in the body (precedent wins until the user says otherwise).
5. **Domain-term anomaly check (same mechanism, not just names):** when a token is a proper noun that is *contextually anomalous* (e.g. a geographic name committed where the series' precedent tables use a metrics term like *churn*), prefer the folder's precedent term and flag the substitution. This class is an ASR mishearing that lands on a real word -- invisible to spellcheck and entity matching alike, and the most expensive to catch late because it propagates into summaries, action tables, and CHANGELOGs looking perfectly plausible.
**Capture the gap:** log an `edge_case` when a near-miss is flagged, a `correction` when the user resolves one. **Closing the loop is `/insights compile`'s job, not this skill's (CR-095).** Log the `edge_case` and move on -- compile's name-candidate roll-up is what surfaces a name flagged more than once, across folders, to someone who can decide whether it belongs in `people[]`. Do not write a roster entry from inside a transcript run: at that point only one occurrence is visible, and one occurrence is not evidence of a recurring person. Once a name is in `people[]`, this section goes quiet for it.
**No-op conditions:** new folder with no precedent files; every name resolves via the known-entity set on first pass.
### Metadata Footer
End the summary with:
```markdown
*{strings.metadata.created}: [YYYY-MM-DD]*
```
### Output Filename
Default format: `YYMMDD-participant-topic-description.md`
Example: `250721-summary-Alex-Bob-strategisk-genomgång.md` (files written before v1.79.0 as `samtal` are never renamed and are still read)
**Participant name resolution:**
1. Match transcript speaker names against `_contacts/*/_meta.yaml` (display_name, aliases)
2. Match against config `team[]` (name, aliases) if loaded
3. Fallback: title-case the name from transcript
Use the resolved canonical name (with correct Swedish characters) in the filename. For example, if the transcript says "dave" but `_contacts/david-ekberg/_meta.yaml` has `display_name: "David Ekberg"`, the filename should use `David-Ekberg`.
**Slug contract (CR-021):** the filename follows the same Swedish-character rule as the content -- keep å/ä/ö in every slug token (`möte`, not `mote`/`m0te`; `utlösen`, not `utlosen`), run the driftword check against the slug before saving, always use the `YYMMDD-` prefix, and always include the English role keyword from `terms:` (`summary`, `agenda`, ... — CR-089) so the file's role is machine-readable. See ops-base General Naming Rules.
If the project has a CLAUDE.md that defines specific filename patterns (MEETING FILENAME FORMAT section), follow those conventions instead. For example, management 1-on-1s, weekly meetings, board meetings, and marketing meetings may each have their own naming pattern.
---
## Step 1.5: Link to Preparation (if exists)
After determining the target folder (from Step 2 context clues or participant detection), check if a preparation file exists for the same contact and date:
- Look for `YYMMDD-förberedelse-*.md` or `YYMMDD-preparation-*.md` in the target folder (where YYMMDD matches the transcript date)
- Also check for keyword `{strings_sv.filename_keywords.preparation}` and `{strings.filename_keywords.preparation}` to handle both languages
If found, add a cross-reference line near the top of the summary (after the H1 heading):
```markdown
**{strings.changelog.preparation_label}:** [filename](path/to/preparation)
```
This links the transcript back to the preparation that preceded it, completing the meeting lifecycle.
---
## Step 2: Ask About File Organization
After creating the summary content, ask the user:
**Where should I save this file?**
### Participant Name Resolution
Before presenting options, resolve participant names from the transcript:
1. Extract speaker names from transcript (speaker labels, names mentioned)
2. For each name, run the resolution algorithm:
- Check `_contacts/*/_meta.yaml` for `display_name` or `aliases` match
- Check config `team[]` for `name` or `aliases` match
- Matching is case-insensitive with Swedish character folding ("Andre" matches "André")
3. Use resolved canonical names in:
- Filename generation
- Summary content (replace messy transcript names)
- CHANGELOG entries
### Routing Options
Check for context clues and present options:
1. **CLAUDE.md routing (if available):** Consult the project CLAUDE.md's MEETING ROUTING table and suggest the appropriate folder based on participants and topic detected in the transcript
2. **Participant folder (if detected):** If a `_contacts/` directory exists at the vault root, match the resolved participant name against folders inside it. Also check for contact folders (`participant_name/`) in the current directory.
3. **Save to a specific path:** Let user specify
4. **Just output the summary:** Don't save to file
If both CLAUDE.md routing and contact folders are available, present both suggestions and let the user choose.
### Creating _meta.yaml (optional)
When routing to a contact folder that lacks `_meta.yaml`, offer to create one:
```
Contact folder lacks metadata. Create _meta.yaml with:
display_name: "[Resolved Name]"
aliases: ["[transcript name]"]
? [yes] no
```
See [Contact Metadata Schema](../ops-config/contact-meta-schema.md) for full specification.
If a `CHANGELOG.md` exists in the target location, update it with the new entry.
If NO `CHANGELOG.md` exists in the target location, create one automatically. The CHANGELOG serves as an index of all transcripts and documents in the folder. When creating a new CHANGELOG, scan the target folder for existing transcript/document files and include them as entries (oldest first within each year section, newest year section first).
---
## Step 2.5: Archive the Transcript (silent, always)
**Run the RAW SOURCE ARCHIVE contract in `ops-base` (CR-085).** It is the single definition —
location, filename, frontmatter, back-link, the one confirmation line, the read-back lock, the skip
conditions, and the shape for one input feeding several summaries.
**Do not restate it here.** This step used to hold the contract itself, and `/ops` — documented as a
superset of this skill — was built without ever inheriting it, because there was no single definition
to inherit. One definition, two callers.
---
## Step 3: CHANGELOG Update (always)
Always update or create the CHANGELOG. This step is not optional.
If the project has domain-specific CHANGELOGs (consult CLAUDE.md), update the relevant one rather than a root CHANGELOG.
### Entry Format
```markdown
- **YYMMDD: {strings.changelog.transcript_label} Participant(s)** - ONE sentence summary of main topics. *(keyword1, keyword2, ... max 15 keywords)* -> [filename.md]
```
### Rules
- Add new entry at top of appropriate year section
- Maximum 15 keywords in parentheses
- ONE concise sentence (max 2 if absolutely necessary)
- Include link to actual transcript file
- Organize chronologically (newest first)
---
## Structured Extraction (for Domain Skills)
When invoked by a domain skill (management-ops, marketing-ops, bravo-ops, project-ops), transcript can output structured data for further processing.
### Extraction Format
```yaml
extraction:
date: YYYY-MM-DD # From filename or content
time: HH:MM # If mentioned
duration: minutes # If mentioned
language: detected # en/sv/mixed
participants:
- name: string # Resolved canonical name (from _meta.yaml or team[])
transcript_name: string # Original name as it appeared in transcript
role: string # If detected
speaking_share: percentage # Estimated
content:
topics:
- title: string
summary: string
participants: [names]
importance: high/medium/low
decisions:
- decision: string
rationale: string # If provided
owner: string # If assigned
deadline: string # If mentioned
action_items:
- action: string
owner: string
deadline: string # If mentioned
priority: P0-P3 # If mentioned
context: string # Brief context
issues:
- issue: string
reported_by: string
severity: string # If mentioned
status: string # open/resolved/blocked
metrics:
- name: string
value: string
context: string
trend: up/down/stable # If mentioned
blockers:
- blocker: string
owner: string
dependency: string # External dependency
raw_summary: |
# Traditional markdown summary
(Full formatted summary as per Step 1)
```
### Usage by Domain Skills
Domain skills can:
1. Invoke transcript for extraction
2. Receive structured YAML
3. Apply domain-specific formatting
4. Add domain-specific sections
5. Execute configured workflows
This separation allows:
- Consistent extraction logic across all skills
- Domain skills focus on formatting and output
- Easier testing and maintenance
---
## Language
### Language Determination
When a config is loaded with `language: per_claude_md`, the output language is determined by the **target file path** -- not the transcript language. Look up the target path in the project CLAUDE.md LANGUAGE POLICY table. For example, a Swedish transcript from a mobile standup saved to `projects/acme-mobile-v3/meetings/` must produce an English summary if the LANGUAGE POLICY says that path is English.
When no config exists, or when `language: input`, maintain the same language as the original transcription.
### Swedish Character Enforcement
When the output is in Swedish, ALL Swedish words MUST use correct å, ä, ö characters. Never substitute with a or o. This applies to the summary, CHANGELOG entry, and any other output.
Common mistakes to avoid:
- "pagaende" -> "pågående", "foretag" -> "företag", "fran" -> "från"
- "fore" -> "före", "nasta" -> "nästa", "behovs" -> "behövs", "kravs" -> "krävs"
- "anvandning" -> "användning", "mote" -> "möte", "losning" -> "lösning"
- "manader" -> "månader", "forberedelse" -> "förberedelse", "karnteam" -> "kärnteam"
If the organization config has `swedish_chars: strict`, this enforcement is mandatory for all Swedish text regardless of the `language` setting.
---
## Step 3.5: Knowledge Extraction (silent)
After creating the summary and updating the CHANGELOG, scan the summary for durable insights worth accumulating. This step writes to `_insights.yaml` in the same folder as the CHANGELOG -- it is a silent accumulation layer that never surfaces in any skill output.
### What to extract
| Type | Captures | Example |
|------|----------|---------|
| `decision` | Choice + rationale | "Chose Flutter over native for shared codebase" |
| `preference` | Working style | "Prefers short standups over long weekly meetings" |
| `learning` | What worked/didn't | "Two-week sprints too long -- switch to weekly" |
| `opportunity` | Ideas not yet actioned | "Could build SaaS from internal tool" |
| `pattern` | Recurring themes | "Budget discussion deferred three meetings in a row" |
| `quote` | Memorable verbatim quote | "Ska du lyckas i affär så måste du gneta. I varenda liten del av businessen." |
| `metric` | A measurement where the number **is** the insight | "Trial-to-paid conversion rose from 4.1% to 6.8%" |
### Threshold
Only extract insights that are:
- **Non-obvious** -- not something anyone in the meeting would already know
- **Durable** -- likely still relevant in 3+ months
- **Specific** -- includes enough context to be useful without the source
**Metrics (CR-046):** Extract a measurement only when **the number is the claim** -- the test is to
remove the number and see what survives. *"Trial-to-paid conversion rose from 4.1% to 6.8%"* leaves
nothing behind and is a `metric`; *"Estimates made without a baseline run systematically high (here
by a third)"* still stands without the figure and is a `learning` that cites evidence. A `metric` carries
five optional fields so the value stays machine-readable instead of being trapped in prose:
```yaml
- id: 12
type: metric
date: 260415
summary: "Trial-to-paid conversion up two thirds after the new onboarding"
value: 6.8
unit: "%"
baseline: 4.1 # the comparison the number is meaningful against
period: "260401-260430"
trend: up # up | down | stable
rationale: "Measured on the Acme cohort after the guided-setup rollout."
```
All five are optional; an entry with only `summary` is valid. This is the landing place for durable
entries from the `metrics:` block in *Structured Extraction* below -- a measurement worth remembering
with its source, **not** a time series. Trend queries across periods belong in `/analytics`.
**Quotes:** Extract memorable, pithy statements that capture a philosophy, a hard-won lesson, or a strong opinion. The `summary` field should contain the verbatim quote (cleaned up for readability but preserving the speaker's voice). The `rationale` field provides context (who said it, what triggered it). Keep the original language -- if said in Swedish, store in Swedish.
If fewer than 1 insight meets the threshold, skip this step silently. No output, no mention.
### Reusability and language rules for _insights.yaml
**Reusability (CR-020 -- replaces the former privacy rule):** Names are **allowed** in insight entries. The vault is private, and `quote` entries need attribution by construction. (The old rule -- "never include personal or company names in `summary`, `rationale`, or `tags`" -- was retired in the 2026-04-07 audit and formally removed by CR-020.) What remains is a *reusability preference*: when an insight generalizes beyond the person who sparked it, prefer name-free phrasing in `summary` and `tags` so the entry reads well in cross-folder views -- the `context` field and `source.file` already carry the who/what. Names in `rationale` are always fine (that is where quote attribution lives). Classification-aware rendering is the visualisation app's job (CR-009), not the writer's.
Generalizes -- prefer name-free:
```yaml
summary: "Switched from hourly billing to t-shirt-sized estimates to handle AI-driven efficiency gains"
tags: [pricing, billing, t-shirt-sizing, business-model]
```
Person-specific -- names are fine:
```yaml
summary: "Prefers supporting plans over driving them"
rationale: "Tim, reflecting on his role in the partner discussion"
```
**Swedish characters:** When writing Swedish content in `_insights.yaml`, ALL Swedish words MUST use correct å, ä, ö characters. YAML files are equally prone to missing Swedish characters as markdown files. Verify before writing: "prissättning" not "prissattning", "övergång" not "overgang", "förändring" not "forandring", etc.
### Process
1. Scan the summary for decisions with rationale, stated preferences, learnings, opportunities, and patterns
2. For each qualifying insight, create an entry following the `_insights.yaml` format (see below)
3. If `_insights.yaml` already exists in the target folder, add the entries **inside the `insights:` list, before the top-level `next_id` key** (never at the end of the file -- see the guard below), and increment `next_id`
4. If it does not exist, create it with `version: 1` and `context` set to the contact/project name
5. **Dedup:** Check existing entries by `source.file` -- if insights from this transcript file already exist, skip to avoid duplicates
### `_insights.yaml` format
Schema version 2 (CR-013) adds the optional `confidence`, `confirmation_count`, `confirmations[]`, and `contradicted_by[]` fields. v1 readers ignore them.
```yaml
version: 2
last_updated: YYMMDD
context: "contact_or_project_name"
insights:
- id: 1
type: decision # decision | preference | learning | opportunity | pattern | quote
date: YYMMDD
summary: "One sentence"
rationale: "One sentence why/context"
source:
file: "YYMMDD-summary-*.md"
section: "Section name"
tags: [max, five, keywords]
status: active # active | superseded | archived
superseded_by: null
# CR-013 lifecycle (all optional; default confidence: hypothesis)
confidence: hypothesis # hypothesis | rule
confirmation_count: 1
confirmations: [] # [{source, date}], populated on promotion
contradicted_by: [] # [{source, date}], populated on demotion
next_id: 2
```
**When `/transcript` writes new insights:** always emit them as `confidence: hypothesis` with `confirmation_count: 1`. Promotion to `rule` happens later via `/insights compile`. Do not promote inline — Step 3.5 only extracts; lifecycle transitions are batched.
### Write-time vocabulary guard (CR-020)
Before writing any entry to `_insights.yaml`, verify:
- `type` is one of: `decision | preference | learning | opportunity | pattern | quote | metric | edge_case | correction | skill_pattern`. Never invent variants (`decision-pattern`, `principle`, `outcome`, `design` are known past drift -- map to the nearest canonical type instead).
- `confidence` (when present) is `hypothesis` or `rule` -- never `high`/`medium`/`confirmed`/`supported`. A re-confirmed insight keeps `confidence: hypothesis` and bumps `confirmation_count`; it does not rename its confidence.
- `date` is `YYMMDD` (never ISO `YYYY-MM-DD`), `id` is an integer, and `next_id` equals `max(id)+1` after the write.
- `tags` has at most 5 entries.
- **Append inside the list, never at the end of the file (CR-096).** `next_id` is a top-level key that
follows `insights:`. New entries belong **before** it, at the list's indentation. Written at
end-of-file they land outside the list -- which either breaks the parse, or worse, parses cleanly
while every reader skips them.
- **Re-read after writing (CR-096).** Parse the file back and assert that the entries just written are
in `insights` and that `len(insights)` grew by the number written. It is the only check that
catches a positional error, and it costs one load per write. On failure, repair before moving on
and say so -- never leave a file that parses with entries outside the list.
Fix silently when unambiguous (e.g. ISO date → YYMMDD); ask the user when not. This guard applies wherever insights are written: `/transcript` Step 3.5/4.5, `/ops` Step 5.5, and all `/insights` subcommands.
### Output
Brief confirmation only:
```
Extracted [N] insights to _insights.yaml:
- [type] summary sentence
- [type] summary sentence
```
### Skip conditions
- No CHANGELOG.md in the target folder (no established project context)
- Pure standup transcript (short status updates without decisions)
- User explicitly said "skip insights"
---
## Step 4: Task Import (if applicable)
After saving the summary, check if action items were extracted for the user (typically the person running the skill).
### Detection
Look for action items in:
1. The "Nästa steg" or "Next Steps" section of the summary
2. The "Åtgärder" or "Action Items" section
3. Any `- [ ]` checkboxes assigned to the user
### Task Import Offer
If action items are found, target the **local `_tasks.yaml`** in the contact/project folder where the transcript was saved (not vault root). Create with v2 schema if missing:
```
Found [N] action items for [User]:
1. [Task description]
2. [Task description]
...
Add to task tracker? [yes] no select
```
**Options:**
- `yes` -- Import all action items
- `no` -- Skip task import
- `select` -- Choose which items to import
- `triage` -- (CR-022) append the items to the vault's registered triage doc instead, as open bullets under its INKORG section with a source link to the transcript. Offer this alternative when the items are personal/ad-hoc (no natural org/project folder) and a triage doc is registered (`_inbox/` working document, `type: working_doc` + tag `do-not-process` -- see `docs/schemas/inbox.md`). One item = one home: triage doc OR `_tasks.yaml`, never both.
### Import Process
For each task to import:
1. **Find local `_tasks.yaml`** in the folder where the transcript was saved. If none exists, create with v2 schema:
- `version: 2`
- `context`: derived from folder name (e.g., `erik-lindgren_techco`)
- `scope`: `project`
- `tasks: []`
- `next_id: 1`
2. **Assign defaults:**
- `priority`: P1 (default for meeting action items)
- `source.type`: `meeting`
- `source.file`: Relative path to the saved transcript
- `source.section`: Section where task was found
- `created`: Today's date (YYMMDD)
- `status`: `pending`
- `private`: `false` (meeting tasks are shared)
3. **Get next ID** from `next_id` field
4. **Append task** to `tasks` array
5. **Increment `next_id`**
6. **Write updated `_tasks.yaml`**
### Import Confirmation
After import:
```
Tasks #[start]-#[end] created:
- #52 Initiera brand/webb-möte (P1, acme)
- #53 Planera Carols vecka (P1, acme)
...
Source: [transcript.md](path/to/transcript.md)
```
### Skip Conditions
Do NOT offer task import if:
- No action items found for the user
- User declined import previously in same session
### Integration with /tasks skill
This step integrates with the `/tasks` skill:
- Same `_tasks.yaml` format
- Same source linking convention
See `~/.claude/skills/tasks/SKILL.md` for the complete task schema.
---
## Step 4.5: Execution Feedback (silent)
If `workflows.knowledge_extraction.evolution.enabled` is true (check `base.yaml` or config), silently log noteworthy execution events to `_insights.yaml` in the target folder. This step produces no visible output.
**What to capture:**
| Type | When | Example |
|------|------|---------|
| `edge_case` | Ambiguous input required guessing or user disambiguation | Name matched multiple contacts, language detection uncertain, unclear target folder, transcript lacks speaker labels (owners inferred -- CR-015), unresolved proper noun flagged for verification (CR-016), near-miss variant of a previously committed name or domain term (CR-017) |
| `correction` | User corrected or overrode skill output | Changed save location, corrected participant name, modified summary content |
**When NOT to capture:** Normal, successful execution. Most invocations produce zero feedback entries. Only log when something unexpected happened or the user intervened.
**Entry format:**
```yaml
- id: [next_id]
type: edge_case # or correction
date: YYMMDD
summary: "One sentence describing what happened"
detail: "How it was resolved"
source:
file: "YYMMDD-summary-*.md"
skill: transcript
step: "step number where it occurred"
tags: [max, five, keywords]
status: active
```
Follow the same `_insights.yaml` format, dedup rules, vocabulary guard, and reusability rules as Step 3.5 knowledge extraction.
---
## Related: Task Tracking
Action items extracted from transcripts can be tracked in the central task system (`_tasks.yaml`). The `/tasks` skill provides:
- Task viewing and filtering (`/tasks list`)
- Manual task addition (`/tasks add`)
- Completion tracking (`/tasks done`)
- Weekly reviews (`/tasks weekly`)
---
## Related: Preparation Documents
Meeting agendas follow the naming convention `YYMMDD-agenda-*.md` in every language (CR-089; older files named `förberedelse`/`preparation` are still found). Use `/preparation <contact>` to create them, or create them conversationally. They are saved to the relevant contact folder inside `_contacts/`.
### Meeting lifecycle
```
/preparation david -> YYMMDD-agenda-*.md
|
[meeting happens]
|
/transcript -> YYMMDD-summary-*.md (links back to the agenda)
| |
|
+-> offers task import -> _tasks.yaml
```
When a transcript is saved to the same folder as an agenda for the same date+contact, Step 1.5 adds a wikilink cross-reference.
Action items extracted during transcript processing can be imported to `_tasks.yaml`, where `/tasks list` shows them.