Ingest meeting transcripts from ANY meeting recorder into brain pages with attendee enrichment, entity propagation, and timeline merge. One unified pipeline: normalize the source into a standard transcript record, split multi-meeting recordings, resolve speakers by evidence, create the page, pass every surprising claim through the consistency check (transcript + brain + plausibility), enrich every entity, then run the verification checklist — substance AND sequence. A meeting is NOT fully ingested until the enrich skill has processed every entity AND the verification checklist passes, including the sequence verify (PASS or explicit user waive).
60
72%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Fix and improve this skill with Tessl
tessl review fix ./skills/meeting-ingestion/SKILL.mdFiling rule: Read
skills/_brain-filing-rules.mdbefore creating any new page.
Convention: See
skills/conventions/quality.mdfor Iron Law back-linking, andskills/conventions/brain-first.mdfor the lookup chain — resolve every name against the brain BEFORE reaching for external lookups.
This skill guarantees:
Every attendee and company mentioned MUST get a back-link from their page to the meeting page. An unlinked mention is a broken brain.
Meeting content arrives from many sources: an AI notetaker (Granola and Circleback are common examples), a phone voice memo, a video-call transcript export, or a transcript the user pastes directly. Do NOT build per-vendor pipelines or paraphrase this skill in ad-hoc instructions — normalize whatever the source provides into the transcript record below, then run the shared phases. Source-specific logic ends at normalization.
Before running the pipeline, reduce the input to this shape (mentally or as a scratch file — it does not get written to the brain as-is):
source: "<recorder name, or 'manual'>"
source_id: "<unique recording id from the source, if any>"
title: "Meeting Title"
date: YYYY-MM-DD
time: "HH:MM TZ" # null if unknown
duration: "45m" # null if unknown
attendees: # the SOURCE'S notion of who was there —
- name: "..." # may need correction during speaker resolution
email: "..." # only if the source provides it
role: "..." # only if known
transcript_segments: # structured form when the source diarizes
- speaker: "..." # resolved name OR "UNKNOWN_N" if unresolved
speaker_raw: "..." # the source's raw speaker label, for traceability
text: "..."
raw_transcript_text: "..." # the complete transcript. NEVER truncate.
source_summary: "..." # the recorder's AI summary if present — a CLAIM, not a FACT
source_url: "..." # link back to the source platform, if anyInvariants:
raw_transcript_text is complete and untruncated. Always.attendees is a claim by the source. People invited ≠ people present.source_summary is TWO lossy layers deep (speech-to-text, then AI
summarization). Both layers confabulate. Verify before writing anything
from it into the brain.Retain the raw transcript when the source provides one: file it as a sidecar
page (e.g. meetings/YYYY-MM-DD-{slug}-transcript) or keep the source file
reachable, and link it from the meeting page. The transcript is the canonical
evidence for every quote and claim check downstream.
Redact before you retain. A raw transcript routinely captures pasted
secrets and PII (a read-aloud API key, a screen-shared token, a private phone
number). Before writing the sidecar, scan for secret-shaped strings (sk-…,
ghp_…, AKIA…, bearer tokens, long hex/base64 blobs) and PII, and redact
matches to labeled placeholders — same deterministic deny-list /
runPrivacyLint model as conversation-archive. "Untruncated" means the
transcript's substance, never a live credential.
Build the transcript record from whatever arrived. If the input is malformed (empty transcript, summary-only payload with no transcript, in-progress recording), STOP — do not create a meeting page from a summary alone. Surface the problem to the user.
For raw transcript files with no structure at all, gbrain capture is the
preferred entry (it handles dedup and frontmatter routing); this pipeline is
for building structured meeting pages.
A single recording is often several distinct meetings stitched together (a recorder left running across back-to-back sessions). Detect this BEFORE page creation, so each real meeting becomes its own page and dedupes/enriches correctly.
Split signals (one is enough to investigate; two or more = split almost certainly):
When a split is detected:
source_id so dedup never re-merges them.Borderline judgment: same people + one flowing conversation that wanders topics = ONE meeting; don't over-split. The test is roster + hard context break, not "the topic changed." If you genuinely cannot tell, surface the boundary to the user rather than guessing.
Users increasingly run two recorders at once as a backup. Before creating a
page, check whether the same meeting already exists:
gbrain search "{title or attendee names}", then match by date ± 1 day +
attendee overlap ≥ 50% + similar title.
Recorders ship anonymous labels (UNKNOWN_N, Participant 2, microphone)
and sometimes confidently WRONG names. Resolve by evidence:
[Room] or UNKNOWN and flag it.
A wrong attribution is worse than no attribution.gbrain search "{name}" for each candidate;
read their page before accepting an identification.high (roster-confirmed), medium
(named unambiguously in the transcript), low (inferred from content —
flag explicitly).# {Meeting Title} — {Date}
**Attendees:** {list with links to people pages}
**Date:** {YYYY-MM-DD}
**Duration:** {if available}
## Summary
{3-5 bullet key outcomes}
## Key Decisions
{Decisions with context. If none: _No decisions — discussion only._}
## Action Items
{Tasks with owners and deadlines. If none: _None — exploratory conversation._}
## Notable Quotes
{Verbatim from the transcript, attributed, `>` blockquotes.
If none: _No notable quotes — operational/logistics meeting._}
## Discussion Notes
{Structured notes by topic}The four required sections are Summary, Key Decisions, Action Items, and
Notable Quotes — additional sections (Discussion Notes, a link to the
transcript sidecar) are additive, never replacements. An empty section always
carries an explicit reason; a bare - None. is a dodge, not an answer.
Quotes are VERBATIM. Write what was said the way it was said — a paraphrase in a blockquote is a fabricated quote.
Recorder summaries inject false facts: speech-to-text garbles proper nouns, and AI summaries turn banter into commitments. Before writing ANY of the following claim types to a person/company page (compiled truth, frontmatter, or timeline), verify:
| Claim type | Verification bar |
|---|---|
| Relationship/role change ("joined as cofounder", "became CTO", "left widget-co") | Find the verbatim transcript lines. The claim must be EXPLICIT in what was said, not an inference from enthusiasm. |
| Ownership/attribution ("her project", "his company") | A speaker saying a word ≠ owning the thing. Require explicit ownership language or brain corroboration. |
| New proper nouns (project/company/product names not already in the brain) | Search the brain and the web for the canonical spelling first. If unresolvable, annotate (unverified spelling) — never write it bare. |
| Major life/deal events (raised, acquired, hired, shut down) | Verbatim transcript support required. These propagate the furthest and are the most expensive to be wrong about. |
Consistency check — transcript support alone is NOT sufficient. A claim can be faithfully transcribed and still wrong. Every claim that passes the transcript bar ALSO gets:
gbrain query "{entity}" and read the
relevant pages. Does the new claim CONTRADICT established brain truth?
When it does, the ESTABLISHED truth wins by default — flag the conflict to
the user, don't silently overwrite. New claims override old truth only with
explicit, verbatim, unambiguous transcript support, and even then the
change is flagged in the ingest report.Downgrade protocol: if the transcript supports only an inference, record it as an explicitly-uncertain note on the meeting page — never in an entity page's compiled truth or frontmatter.
Propagation rule: a claim that fails verification must not fan out. Do not copy it to other entity pages or timeline entries. A false claim written to five pages costs five corrections.
For EACH attendee:
gbrain search "{name}" — does a people page exist?skills/enrich/SKILL.md). Every
person who was actually IN the meeting gets a page, even a thin one. Skip
only ephemeral third-party mentions (a name invoked about someone not
present, with no standalone context) and non-participants (a server taking
orders).gbrain timeline-add {person-slug} {date} "Attended {meeting-title}"Back-link known people who are MENTIONED or SPEAK in the transcript too, not just attendees — but high-confidence identifications only. Never backlink a garbled name or a low-confidence guess; a wrong backlink pollutes the graph worse than a missing one.
Note: Once the meeting page is written via gbrain put, the auto-link
post-hook automatically creates attended links from the meeting to each
attendee whose page is referenced as [Name](people/slug). You don't need to
call gbrain link for attendees. You DO still need gbrain timeline-add for
dated events (auto-link only handles links, not timeline entries).
For each company, project, or concept discussed:
gbrain search, then gbrain get).Timeline merge: the same event appears on ALL mentioned entities' timelines. If alice-example met charlie-example at acme-example, the event goes on alice-example's page, charlie-example's page, AND acme-example's page. For a multi-company session (e.g. group office hours), disaggregate the feedback per company — each company's timeline entry carries its own content, not a blob about the whole session.
If the meeting contains original thinking worth extracting beyond the page
itself, chain into skills/signal-detector/SKILL.md after ingestion.
gbrain sync to update the index.
The write phases do the work; this phase verifies the work was actually done. Run the checklist on the finished page — every item, every meeting, including "quick" logistics meetings. Never report a meeting as ingested until every item passes. Saying "ingested" first and fixing later is a contract violation; a false completion report is worse than an honest partial one.
V1 — Required sections have substance.
## Summary carries real outcomes (2+ bullets or a few substantive
sentences), not one vague line.## Key Decisions, ## Action Items, and ## Notable Quotes each have
real content OR an explicit reason (_None — exploratory conversation._).- None. or _n/a_ written to silence the checklist is a violation.
Before writing "none", confirm against the transcript that there truly were
no decisions/commitments/quotes worth keeping.V2 — Every people/companies slug has a page AND a timeline backlink. For each person/company slug referenced by the meeting page:
gbrain get people/{slug} # page exists?
gbrain timeline people/{slug} # has an entry pointing back at this meeting?A slug with no page means Phase 7/8 was skipped — go do it. A page with no timeline entry for this meeting means the merge was incomplete — add it.
V3 — Speaker map resolved.
No Participant N / UNKNOWN_N / raw recorder labels remain in the page
without either a resolution or an explicit uncertainty flag ([Room],
⚠️ attribution uncertain). Every named speaker carries a confidence from
Phase 4. An unflagged anonymous label means speaker resolution was skipped.
V4 — Every quote grounded VERBATIM in the transcript.
> blockquote,
verify its contiguous span appears in the transcript sidecar. Filler words
(like, you know, I mean) may be stripped from both sides; a genuine
quote still shares a long contiguous run of content words, a fabricated one
does not.
gbrain get meetings/{date}-{slug}-transcript # then locate each quote spanV5 — Fabricated-attendee sanity checks. Recorders confidently invent names and emails for unlabeled speakers.
V6 — Sequence verify (order, not substance). A meeting page can be right on depth and wrong on order — they are independent failure axes, and V1–V5 never look at order. Verify the narrated sequence:
{phase, place, people} — where phase is its
position relative to the meeting's central event (before / during / after)
as the PROSE claims it.gbrain day {date}sequence: WAIVED by user — {contradiction} stands.The loop: fix → re-check → fix, until every item passes (or V6 is explicitly waived). Only then report.
If the title or transcript signals legal or deeply personal content (deposition, attorney, counsel, privileged, health): keep the page minimal and factual, do not extract biographical color into other pages, and prefer restraint on back-links. When in doubt about whether content should propagate, ask the user.
Meeting page created AND the verification checklist passed. Report: "Meeting ingested: {N} attendees enriched, {N} entities updated, {N} action items captured. Verification: passed. Sequence: PASS." If the sequence check was waived, say so explicitly: "Sequence: WAIVED by user — {contradiction} stands (acknowledged, not resolved)." If the recording was split, report one line per resulting meeting page. If a claim was withheld or a contradiction flagged by Phase 6, list each flag — the user resolves them, not silence. If any checklist item cannot be made to pass, report the meeting as NOT ingested and name the failing item.
[Room]/UNKNOWN and flagging- None. under a required section to silence the checklist without
confirming against the transcript055ac6c
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.