agentleFS
tandregbgtandregbg/core-claude-skills/skills/transcript/SKILL.md

transcript

Skill4 starsChanged 8 days agoMITView on GitHub

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.

11 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Show its rules and text

We split this skill into the rules it gives an agent

69 rules. 0 are common, 31 are distinctive, and 38 are very specific to this project.

  • 0

    Common

    Most of the 7 edited versions we read keep these.

  • 31

    Distinctive

    This author's own choices. Worth a look.

  • 38

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Before creating the summary, load any promoted rules from the _insights.yaml chain in scope:

    line 43
  2. Distinctive:

    Do not echo the preamble in the output.

    line 55
  3. Distinctive:

    Scoping: Only rules in the CWD's parent chain apply.

    line 57
  4. Distinctive:

    Check for date in filename or metadata (YYMMDD, YYYYMMDD, YYYY-MM-DD, etc.)

    line 88
  5. Very specific:

    If no date found: # Summary: [FILENAME] - [MAIN TOPIC]

    line 90
  6. Very specific:

    If owner or deadline is unknown, write ?

    line 130
  7. Very specific:

    Use --- to separate main sections

    line 137
  8. Very specific:

    Use ## for section headings (sentence case)

    line 138
  9. Distinctive:

    Use bullet points for content within sections

    line 139
  10. Distinctive:

    Check the filename for correct spelling

    line 169
  11. Very specific:

    Check config people[] roster for canonical + aliases (CR-017 -- covers non-contact persons: colleagues-of-counterparts, remote team …

    line 170
  12. Very specific:

    Check _contacts/*/_meta.yaml for display_name and aliases

    line 171
  13. Very specific:

    Check config team[] for canonical names

    line 172
  14. Distinctive:

    Use the resolved canonical name throughout the summary

    line 173
  15. Very specific:

    Detect the condition first: Before assigning any owner, decide whether the transcript is *diarized* (has speaker labels / turn markers like …

    line 181
  16. Distinctive:

    Treat every owner as a hypothesis.

    line 184
  17. Very specific:

    Prefer failing safe: write the owner as ?, or Name?

    line 185
  18. Very specific:

    Use surrounding context to raise confidence where it is genuinely high (e.g.

    line 186
  19. Very specific:

    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 …

    line 191
  20. Very specific:

    Never upgrade Name? to a bare name without explicit confirmation (filename, a known entry, or the user).

    line 211
  21. Very specific:

    Capture the gap: When you flag one or more unverified proper nouns, log it as an edge_case in Step 4.5.

    line 215
  22. Distinctive:

    Default-on and additive: when every proper noun resolves against the known set, this section is a no-op.

    line 217
  23. Very specific:

    If the precedent spelling resolves via the people[] roster / known-entity set → use the established spelling, silently.

    line 229
  24. Very specific:

    If neither form is in any roster → do NOT silently introduce the new variant.

    line 230
  25. Very specific:

    Use the resolved canonical name (with correct Swedish characters) in the filename.

    line 254
  26. Distinctive:

    If the project has a CLAUDE.md that defines specific filename patterns (MEETING FILENAME FORMAT section), follow those conventions instead.

    line 261
  27. Distinctive:

    After determining the target folder (from Step 2 context clues or participant detection), check if a preparation file exists for the same …

    line 267
  28. Very specific:

    Look for YYMMDD-förberedelse-*.md or YYMMDD-preparation-*.md in the target folder (where YYMMDD matches the transcript date)

    line 269
  29. Distinctive:

    If found, add a cross-reference line near the top of the summary (after the H1 heading):

    line 272
  30. Distinctive:

    After creating the summary content, ask the user:

    line 284
  31. Distinctive:

    Before presenting options, resolve participant names from the transcript:

    line 290
  32. Very specific:

    Check _contacts/*/_meta.yaml for display_name or aliases match

    line 294
  33. Very specific:

    Check config team[] for name or aliases match

    line 295
  34. Distinctive:

    Use resolved canonical names in:

    line 297
  35. Distinctive:

    Check for context clues and present options:

    line 304
  36. Very specific:

    Participant folder (if detected): If a _contacts/ directory exists at the vault root, match the resolved participant name against folders …

    line 307
  37. Distinctive:

    Just output the summary: Don't save to file

    line 309
  38. Distinctive:

    If both CLAUDE.md routing and contact folders are available, present both suggestions and let the user choose.

    line 311
  39. Very specific:

    When routing to a contact folder that lacks _meta.yaml, offer to create one:

    line 315
  40. Very specific:

    If a CHANGELOG.md exists in the target location, update it with the new entry.

    line 326
  41. Very specific:

    If a client already archived the recording (the inbox item's raw_transcript:), that step adopts the file instead of writing a second one …

    line 338
  42. Distinctive:

    Always update or create the CHANGELOG.

    line 352
  43. Distinctive:

    If the project has domain-specific CHANGELOGs (consult CLAUDE.md), update the relevant one rather than a root CHANGELOG.

    line 354
  44. Distinctive:

    Add new entry at top of appropriate year section

    line 362
  45. Distinctive:

    When invoked by a domain skill (management-ops, marketing-ops, bravo-ops, project-ops), transcript can output structured data for further …

    line 372
  46. Very specific:

    When no config exists, or when language: input, maintain the same language as the original transcription.

    line 453
  47. Distinctive:

    When the output is in Swedish, ALL Swedish words MUST use correct å, ä, ö characters.

    line 457
  48. Very specific:

    If the organization config has swedish_chars: strict, this enforcement is mandatory for all Swedish text regardless of the language setting.

    line 465
  49. Distinctive:

    After creating the summary and updating the CHANGELOG, scan the summary for durable insights worth accumulating.

    line 471
  50. Distinctive:

    Only extract insights that are:

    line 499
  51. Very specific:

    All five are optional; an entry with only summary is valid.

    line 523
  52. Distinctive:

    If fewer than 1 insight meets the threshold, skip this step silently.

    line 529
  53. Very specific:

    Swedish characters: When writing Swedish content in _insights.yaml, ALL Swedish words MUST use correct å, ä, ö characters.

    line 547
  54. Very specific:

    If _insights.yaml already exists in the target folder, add the entries inside the insights: list, before the top-level next_id key (never …

    line 553
  55. Very specific:

    If it does not exist, create it with version: 1 and context set to the contact/project name

    line 554
  56. Very specific:

    Dedup: Check existing entries by source.file -- if insights from this transcript file already exist, skip to avoid duplicates

    line 555
  57. Very specific:

    Themes (CR-124, only with themes.enabled): read <project>/_themes.yaml first and reuse before create: a new theme only when no active …

    line 598
  58. Very specific:

    When /transcript writes new insights: always emit them as confidence: hypothesis with confirmation_count: 1.

    line 604
  59. Very specific:

    Before writing any entry to _insights.yaml, verify:

    line 608
  60. Distinctive:

    No CHANGELOG.md in the target folder (no established project context)

    line 640
  61. Distinctive:

    After saving the summary, check if action items were extracted for the user (typically the person running the skill).

    line 648
  62. Distinctive:

    Look for action items in:

    line 652
  63. Very specific:

    If action items are found, target the local _tasks.yaml in the contact/project folder where the transcript was saved (not vault root).

    line 659
  64. Distinctive:

    Do NOT offer task import if:

    line 714
  65. Distinctive:

    No action items found for the user

    line 715
  66. Very specific:

    If workflows.knowledge_extraction.evolution.enabled is true (check base.yaml or config), silently log noteworthy execution events to …

    line 731
  67. Very specific:

    Follow the same _insights.yaml format, dedup rules, vocabulary guard, and reusability rules as Step 3.5 knowledge extraction.

    line 757
  68. Very specific:

    step is an identifier, not a description (CR-178): Write the step id alone -- "1", "3.5", "4.5" -- from feedback_steps in ecosystem.yaml, …

    line 759
  69. Distinctive:

    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.

    line 802

“Common” is measured: kept by at least 6 in 10 of the 7 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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. consensus is never loaded (CR-123): it is knowledge about a conversation (claims several guests made), not an instruction to this session. A value this step does not know is read as hypothesis, never as rule.
  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

Profile first (CR-123). Resolve the target folder's profile (python3 ~/.claude/skills/ops-config/profiles.py <folder> --text; no profile: means meeting and everything below applies unchanged). Under the podcast profile:

  • the unit is an episode; summaries go to the profile's summary_folder (episodes/) and the filename role keyword is episode (YYMMDD-episode-<description>.md, CR-089, forward only);
  • section 1 is Follow-up (strings.transcript.follow_up: Follow-up / Uppföljning) instead of Next Steps: a plain list of announcements -- the guest's own plans, the next episode, the host's housekeeping notes, calls to action to listeners. No owner column and no task import (Step 4 is skipped for it). A deliverable of the host's with real consequence goes to the editorial backlog (/tasks, with a kind), not to Follow-up;
  • the Decisions section is still written, and is usually the empty-marker form;
  • voices are the profile's (host, guest): attribute what is said to the voice, by name where the transcript gives one.

Optional metadata lines (every profile), directly under the heading:

  • **Originally published:** YYYY-MM-DD (strings.transcript.originally_published) -- for re-published or back-catalogue material whose true date differs from the date in the file name. The file name keeps the processing/publication date; anything time-based (reading order, confirmation order, theme quarters) uses this date when present.
  • **Themes:** T07 (primary), T03 (strings.transcript.themes) -- only when the project enables themes (CR-124, see Step 3.5). Two to six themes, at most two primary.

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):

ConceptSwedishEnglish
Action itemsNästa stegNext Steps
DecisionsBeslutDecisions
OutcomeKonklusionOutcome
DiscussionDiskussionDiscussion
BackgroundBakgrundBackground
BlockersBlockersBlockers

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:

## 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)

VariantWhenRequired sections
Concise<30 min meetings, internal task sessions, dataspec reviewsNästa steg, Beslut, Konklusion (skip Diskussion + Bakgrund unless substantive)
Standard30-90 min meetings, most contact samtal, standupsFull canonical structure (Nästa steg -> Beslut -> Konklusion -> Diskussion -> Bakgrund)
Extended>90 min meetings, board, strategy, multi-topicFull 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:

*{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.

Links (CR-122): a Markdown link target that contains (, ) or a space is written percent-encoded (%28, %29, %20); the link text stays readable. /ops check reports raw ones.

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):

**{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 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.

If a client already archived the recording (the inbox item's raw_transcript:), that step adopts the file instead of writing a second one (CR-128). When the user has not named the summary, name it from the inbox item's dt_filename mapped to YYMMDD-summary-<description>.md (its date prefix replaced by the recording's YYMMDD); recorded_at, duration_sec, language and speakers fill the summary header instead of being re-derived. Missing keys: as before.

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

- **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

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

TypeCapturesExample
decisionChoice + rationale"Chose Flutter over native for shared codebase"
preferenceWorking style"Prefers short standups over long weekly meetings"
learningWhat worked/didn't"Two-week sprints too long -- switch to weekly"
opportunityIdeas not yet actioned"Could build SaaS from internal tool"
patternRecurring themes"Budget discussion deferred three meetings in a row"
quoteMemorable verbatim quote"Ska du lyckas i affär så måste du gneta. I varenda liten del av businessen."
metricA measurement where the number is the insight"Trial-to-paid conversion rose from 4.1% to 6.8%"

The list above is the meeting profile's (ecosystem.yaml insight_types.content). Under the podcast profile (CR-123) the types are insight_types.podcast:

TypeCapturesExample
claimSomething asserted to be true"Most early hires fail on pace, not skill"
practiceA way of working someone uses"Writes the press release before the product"
tool_tipA concrete tool or technique"Records every customer call and rereads the first minute"
predictionA statement about what will happen"Voice interfaces replace forms within five years"
opportunityAn idea not yet acted onas above
quoteA memorable verbatim statementas above

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:

- 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:

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:

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. Schema version 3 (CR-123) adds voice, independent_count, owner_count, confirmations[].voice, confirmations[].note, original_date and themes, and the confidence consensus; full rules in the insights skill, "Insights schema 3".

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
    # CR-123 schema 3 (optional; written when the profile declares voices)
    voice: guest              # who raised it in THIS summary: a profile voice (host | guest | both)
    original_date: YYMMDD     # when the summary has an Originally published line
    themes: [T07]             # CR-124, when the project enables themes

next_id: 2

Confirming an existing insight (every profile, CR-123): append one item to confirmations[] ({source, date, voice, note}, where note is one line saying how this unit said it) and bump confirmation_count. Never extend rationale to record a confirmation -- appended clauses turn a rationale into a log. A unit that disagrees goes to contradicted_by[] the same way. Under a profile with voices, also recount independent_count and owner_count (distinct units, the origin included; both counts for each), or leave them for /insights compile, which recounts.

Themes (CR-124, only with themes.enabled): read <project>/_themes.yaml first and reuse before create: a new theme only when no active theme or alias fits and it should plausibly recur in three or more units. Write the summary's Themes: line, the insight's themes, and in the same step the registry (units[] entry with role and a one-line note, the insight ids in insight_ids, last_seen), by targeted edit. No personal or company names in any theme field.

When /transcript writes new insights: always emit them as confidence: hypothesis with confirmation_count: 1. Promotion to rule (or, under the podcast profile, consensus) 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 the resolved profile's list (ecosystem.yaml insight_types.<profile insight_types>) or the evolution types: meeting decision | preference | learning | opportunity | pattern | quote | metric, podcast claim | practice | tool_tip | prediction | opportunity | quote, evolution 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, rule or consensus (CR-123; never written by this step) -- 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.
  • voice (when present) is one of the profile's voices; never written under a profile that declares none.
  • Edit, never re-dump (CR-122). Insert or change lines in place; never load the whole file and write it back. A re-dump reorders keys, drops comments and escapes å/ä/ö.
  • 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
  • The profile's follow_up is announcements (CR-123): Follow-up rows are announcements, never tasks. A host deliverable with real consequence is offered as an editorial-backlog task with a kind from the profile's task_kinds, one at a time and only when the user confirms

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:

TypeWhenExample
edge_caseAmbiguous input required guessing or user disambiguationName 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)
correctionUser corrected or overrode skill outputChanged 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:

- 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: "1"               # the id alone, from feedback_steps (CR-178)
  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.

step is an identifier, not a description (CR-178). Write the step id alone -- "1", "3.5", "4.5" -- from feedback_steps in ecosystem.yaml, and put what the entry is about in tags, which is the field that exists for it. The id names where the problem occurred, not where this entry is being written: an attribution failure logged here is step: "1", because Step 1 is where the parsing happened.

Measured before the rule existed: 120 distinct spellings of a handful of steps across 362 entries -- 1, Step 1, Step 1 -- speaker attribution, step 1 / CR-016, 1 (speaker attribution). The cost was not untidiness. /insights compile Pass 1 is specified to cluster feedback by skill + step, and with 120 values it cannot: the 261105 run clustered on tag families instead and the grouping the field exists for was never performed. Existing entries are read, never rewritten (CR-127) -- the id is recoverable from the first token of every spelling observed.


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.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.