[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6N0A8SWCBA8FV066VB7DJHX":3,"skill-same:01M6N0A8SWCBA8FV066VB7DJHX":109},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":46,"linkOnly":47,"markdownContent":48,"markdownBody":49,"frontMatter":50,"refName":81,"commitSha":82,"skillDirectory":83,"forksCount":84,"repositoryHtmlUrl":85,"repositoryHomepageUrl":6,"ownerHtmlUrl":86,"ownerBlogUrl":87,"ownerDescription":88,"updatedAt":89,"install":90,"resources":102},"01M6N0A8SWCBA8FV066VB7DJHX","meeting-ingestion",null,"Ingest meeting transcripts from ANY meeting recorder into brain pages with\nattendee enrichment, entity propagation, and timeline merge. One unified\npipeline: normalize the source into a standard transcript record, split\nmulti-meeting recordings, resolve speakers by evidence, create the page,\npass every surprising claim through the consistency check (transcript +\nbrain + plausibility), enrich every entity, then run the verification\nchecklist — substance AND sequence. A meeting is NOT fully ingested until\nthe enrich skill has processed every entity AND the verification checklist\npasses, including the sequence verify (PASS or explicit user waive).","garrytan\u002Fgbrain","garrytan","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F19957?v=4","01M6DJCCDDG53MTYYRTXAXFG17","Garry Tan","01M6MZQZCEN3RRMD33E1X9ZZR9",32683,"MIT","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Fplugin\u002Fskills\u002Fmeeting-ingestion\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000034","行政与办公人员","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],"meeting ingestion","meeting","ingestion","claim","consistency check","when","sequence","recording","enrich","people","brain","get","merge","query","pipeline","audit","search","verification","transcript","run","create","notes","2026-11-10 10:12:30",false,"---\nname: meeting-ingestion\nversion: 2.2.0\ndescription: |\n  Ingest meeting transcripts from ANY meeting recorder into brain pages with\n  attendee enrichment, entity propagation, and timeline merge. One unified\n  pipeline: normalize the source into a standard transcript record, split\n  multi-meeting recordings, resolve speakers by evidence, create the page,\n  pass every surprising claim through the consistency check (transcript +\n  brain + plausibility), enrich every entity, then run the verification\n  checklist — substance AND sequence. A meeting is NOT fully ingested until\n  the enrich skill has processed every entity AND the verification checklist\n  passes, including the sequence verify (PASS or explicit user waive).\ntriggers:\n  - \"meeting transcript\"\n  - \"process this meeting\"\n  - \"meeting notes\"\n  - \"meeting recorder\"\n  - \"ingest this recording\"\n  - \"capture meetings\"\n  - \"audit this meeting\"\n  - \"check the sequence\"\n  - \"did I get the order right\"\n  - meeting transcript received\ntools:\n  - search\n  - query\n  - get_page\n  - put_page\n  - add_link\n  - add_timeline_entry\n  - get_timeline\n  - chronicle_day\nmutating: true\nwrites_pages: true\nwrites_to:\n  - meetings\u002F\n  - people\u002F\n  - companies\u002F\nupstream:\n  - meeting-ingestion@fc834ee\n  - meeting-gold-standard@fc834ee\n  - chronology-guard@fc834ee\nwhen_to_use: \"Use when the user asks: \\\"meeting transcript\\\", \\\"process this meeting\\\", \\\"meeting notes\\\", \\\"meeting recorder\\\", \\\"ingest this recording\\\".\"\n---\n\n# Meeting Ingestion Skill — Unified Pipeline\n\n> **Filing rule:** Read `skills\u002F_brain-filing-rules.md` before creating any new page.\n\n> **Convention:** See `skills\u002Fconventions\u002Fquality.md` for Iron Law back-linking, and\n> `skills\u002Fconventions\u002Fbrain-first.md` for the lookup chain — resolve every name\n> against the brain BEFORE reaching for external lookups.\n\n## Contract\n\nThis skill guarantees:\n- Works with ANY meeting recorder — AI notetaker export, webhook payload, share\n  link, raw audio transcription, or manual paste. The normalized transcript\n  record (below) is the contract; per-source fetch\u002Fparse is the host agent's job\n- One brain page per REAL meeting — multi-meeting recordings are split first\n- Meeting page created with attendees, summary, key decisions, action items,\n  notable quotes\n- Speakers resolved by evidence, never by guess\n- Recorder auto-summaries treated as CLAIMS, not facts — every surprising claim\n  passes the consistency check before it touches an entity page\n- EVERY attendee gets a people page (created or updated)\n- EVERY company discussed gets entity propagation\n- Timeline entries on ALL mentioned entities (timeline merge)\n- Back-links created bidirectionally\n- Meeting is NOT fully ingested until enrich runs for every entity\n- The meeting is never REPORTED as ingested until the verification checklist\n  (below) passes — every quote grounded, every slug backed by a page and a\n  timeline backlink, every speaker resolved or flagged\n- The narrated SEQUENCE is verified independently of substance: a sequence\n  contradiction BLOCKS ingestion until fixed or explicitly waived by the user\n\nEvery attendee and company mentioned MUST get a back-link from their page to\nthe meeting page. An unlinked mention is a broken brain.\n\n## Any recorder, one pipeline\n\nMeeting content arrives from many sources: an AI notetaker (Granola and\nCircleback are common examples), a phone voice memo, a video-call transcript\nexport, or a transcript the user pastes directly. Do NOT build per-vendor\npipelines or paraphrase this skill in ad-hoc instructions — normalize whatever\nthe source provides into the transcript record below, then run the shared\nphases. Source-specific logic ends at normalization.\n\n## The normalized transcript record\n\nBefore running the pipeline, reduce the input to this shape (mentally or as a\nscratch file — it does not get written to the brain as-is):\n\n```yaml\nsource: \"\u003Crecorder name, or 'manual'>\"\nsource_id: \"\u003Cunique recording id from the source, if any>\"\ntitle: \"Meeting Title\"\ndate: YYYY-MM-DD\ntime: \"HH:MM TZ\"               # null if unknown\nduration: \"45m\"                # null if unknown\nattendees:                     # the SOURCE'S notion of who was there —\n  - name: \"...\"                # may need correction during speaker resolution\n    email: \"...\"               # only if the source provides it\n    role: \"...\"                # only if known\ntranscript_segments:           # structured form when the source diarizes\n  - speaker: \"...\"             # resolved name OR \"UNKNOWN_N\" if unresolved\n    speaker_raw: \"...\"         # the source's raw speaker label, for traceability\n    text: \"...\"\nraw_transcript_text: \"...\"     # the complete transcript. NEVER truncate.\nsource_summary: \"...\"          # the recorder's AI summary if present — a CLAIM, not a FACT\nsource_url: \"...\"              # link back to the source platform, if any\n```\n\n**Invariants:**\n- `raw_transcript_text` is complete and untruncated. Always.\n- `attendees` is a claim by the source. People invited ≠ people present.\n- `source_summary` is TWO lossy layers deep (speech-to-text, then AI\n  summarization). Both layers confabulate. Verify before writing anything\n  from it into the brain.\n\nRetain the raw transcript when the source provides one: file it as a sidecar\npage with `type: source` at `sources\u002Fmeetings\u002FYYYY-MM-DD-{slug}-transcript`\n(the default pack files raw evidence as `source` under `sources\u002F`; never\ninvent `meeting-transcript`, and don't write the `transcript` alias) or keep\nthe source file reachable, and link it from the meeting page. The transcript is the canonical\nevidence for every quote and claim check downstream. Put\n`facts_backstop: false` in the sidecar's frontmatter: automatic fact\nextraction would otherwise mine the raw transcript, garbles and banter\nincluded, beside the verified meeting page (Phase 5).\n\n**Redact before you retain.** A raw transcript routinely captures pasted\nsecrets and PII (a read-aloud API key, a screen-shared token, a private phone\nnumber). Before writing the sidecar, scan for secret-shaped strings (`sk-…`,\n`ghp_…`, `AKIA…`, bearer tokens, long hex\u002Fbase64 blobs) and PII, and redact\nmatches to labeled placeholders — same deterministic deny-list \u002F\n`runPrivacyLint` model as `conversation-archive`. \"Untruncated\" means the\ntranscript's substance, never a live credential.\n\n## Phases\n\n### Phase 1: Normalize the input\n\nBuild the transcript record from whatever arrived. If the input is malformed\n(empty transcript, summary-only payload with no transcript, in-progress\nrecording), STOP — do not create a meeting page from a summary alone. Surface\nthe problem to the user.\n\nFor raw transcript files with no structure at all, `gbrain capture` is the\npreferred entry (it handles dedup and frontmatter routing); this pipeline is\nfor building structured meeting pages.\n\n### Phase 2: Split detection — one recording is not always one meeting\n\nA single recording is often several distinct meetings stitched together (a\nrecorder left running across back-to-back sessions). Detect this BEFORE page\ncreation, so each real meeting becomes its own page and dedupes\u002Fenriches\ncorrectly.\n\n**Split signals** (one is enough to investigate; two or more = split almost\ncertainly):\n- **Roster shift** — a new person arrives mid-transcript (a greeting deep into\n  the file), or the speaker set in the back half differs from the front\n- **Topic hard-cut** with no continuity between the halves\n- **Context reset** — \"ok, next one\", a fresh intro round, a restart phrase\n- **The source's own title\u002Fagenda names multiple sessions**\n\n**When a split is detected:**\n1. Find the boundary segments — the exact points where roster\u002Ftopic flips.\n2. Partition the segments into N contiguous chunks, one per real meeting.\n   Never drop or duplicate a segment; the union must equal the original, in order.\n3. Run the remaining phases once per chunk → N separate meeting pages, each\n   with its own corrected attendees, title, and time.\n4. Cross-link the sibling pages (\"same recording, session k of N\") and note\n   the shared `source_id` so dedup never re-merges them.\n\n**Borderline judgment:** same people + one flowing conversation that wanders\ntopics = ONE meeting; don't over-split. The test is roster + hard context\nbreak, not \"the topic changed.\" If you genuinely cannot tell, surface the\nboundary to the user rather than guessing.\n\n### Phase 3: Dedup across recorders\n\nUsers increasingly run two recorders at once as a backup. Before creating a\npage, check whether the same meeting already exists:\n`gbrain search \"{title or attendee names}\"`, then match by date ± 1 day +\nattendee overlap ≥ 50% + similar title.\n\n- If a page exists, MERGE into it instead of creating a duplicate: build from\n  the RICHER transcript, record both source ids in frontmatter.\n- Source *priority* (which diarization to trust) is not source *completeness*\n  (which transcript is fuller). One recorder may capture 20% of a session the\n  other captured fully. Compare lengths; keep the fuller transcript as the\n  grounding evidence.\n\n### Phase 4: Speaker resolution — before writing the page\n\nRecorders ship anonymous labels (`UNKNOWN_N`, `Participant 2`, `microphone`)\nand sometimes confidently WRONG names. Resolve by evidence:\n\n1. Start from the source's attendee list, corrected by any roster evidence the\n   user can provide (an invite list, an event page, \"it was just me and\n   charlie-example\").\n2. **Never guess.** When uncertain, write `[Room]` or `UNKNOWN` and flag it.\n   A wrong attribution is worse than no attribution.\n3. Cross-reference the brain: `gbrain search \"{name}\"` for each candidate;\n   read their page before accepting an identification.\n4. Assign each named speaker a confidence: `high` (roster-confirmed), `medium`\n   (named unambiguously in the transcript), `low` (inferred from content —\n   flag explicitly).\n5. **Content-identity check:** for every named speaker who claims a\n   role\u002Fcompany\u002Fproduct in their own words (\"I founded X\", \"at my company Y\n   we…\"), verify the claim against that person's brain page. If the named\n   person's established identity CONTRADICTS what the speaker says about\n   themselves, the recorder substituted the wrong person — resolve by\n   identity, not by name, and reattribute the whole track.\n6. **Phantom-speaker check:** if the recorder reports MORE distinct speakers\n   than the known attendee count, suspect over-splitting — one real voice\n   diarized into two labels. Tells: two labels never address each other, or\n   hand off mid-thought. Collapse phantoms into the real attendee.\n7. **User ground truth overrides everything.** If the user states who was in\n   the room, that beats the recorder's diarization AND the roster. Reattribute,\n   fix the page, and never re-litigate a room the user has confirmed.\n8. Speech-to-text garbles names constantly. Before creating a NEW person page\n   from a transcript-only name, search the brain for plausible spelling\n   variants of the surname; default assumption is that a near-miss IS the\n   existing person with a mangled name. Update the existing page and record\n   the variant as an alias rather than creating a duplicate.\n\n### Phase 5: Create meeting page\n\n```markdown\n---\ntype: meeting\nattendees: [{comma-separated slugs of the same people, e.g. people\u002Falice-example}]\nfacts_backstop: false\n---\n\n# {Meeting Title} — {Date}\n\nAttendees: {comma-separated links to the people pages of everyone in the room}\n**Date:** {YYYY-MM-DD}\n**Duration:** {if available}\n\n## Summary\n{3-5 bullet key outcomes}\n\n## Key Decisions\n{Decisions with context. If none: _No decisions — discussion only._}\n\n## Action Items\n{Tasks with owners and deadlines. If none: _None — exploratory conversation._}\n\n## Notable Quotes\n{Verbatim from the transcript, attributed, `>` blockquotes.\nIf none: _No notable quotes — operational\u002Flogistics meeting._}\n\n## Discussion Notes\n{Structured notes by topic}\n```\n\nThe `Attendees:` line is the page's attendance record, and the link extractor\nreads it literally. Write it as one line that starts with `Attendees:` (no\nbold markup) and holds only links to people pages, one per person who was in\nthe room, separated by commas (no \"and\"). Link a person only when the\nidentification is high or medium confidence (Phase 4). Put a company, role,\nspeaker confidence, a low-confidence guess, or an unresolved speaker such as\n`UNKNOWN_2` in Discussion Notes instead: any extra text on the line stops the\nextractor from reading it as the attendance record, and a line wrapped onto a\nsecond line loses everyone after the break. Leave people who were only\ninvited or mentioned off the line. The `attendees:` frontmatter lists\nexactly the same people by slug; extraction reads it as a second attendance\nrecord, so the two must agree.\n\nThe four required sections are Summary, Key Decisions, Action Items, and\nNotable Quotes — additional sections (Discussion Notes, a link to the\ntranscript sidecar) are additive, never replacements. An empty section always\ncarries an explicit reason; a bare `- None.` is a dodge, not an answer.\n\nQuotes are VERBATIM. Write what was said the way it was said — a paraphrase in\na blockquote is a fabricated quote.\n\nTimeline events (`life\u002Fevents\u002F`) are extracted from the saved meeting page in\nthe background (Life Chronicle, on by default; one paid chat call per page).\nCheck the write receipt: `chronicle_backstop.pending: \"next_cycle\"` means the\nnext cycle extracts it (`gbrain dream --phase chronicle` runs it now, paid), and\n`chronicle_backstop.skipped` names the reason and its fix. Never hand-write\n`life\u002Fevents\u002F` pages; edit the meeting page and extraction updates its events.\nSee `docs\u002Fguides\u002Flife-chronicle.md`.\n\nFacts (what `recall` returns) are also extracted from saved pages in the\nbackground, on by default, one paid chat call each time a page's body changes.\nExtraction files each fact on the entity page it names, so it reaches people\nand company pages without passing Phase 6, and it never takes back facts it\nfiled from an earlier version of the page. That is why the template drafts\nthe meeting page with `facts_backstop: false` (the receipt reads\n`facts_backstop: { skipped: \"opted_out\" }`): nothing is extracted while the\npage is still being corrected. When the verification checklist passes, save\nthe page once more without that line; the receipt then reads\n`facts_backstop: { queued: true }`. Everything on the verified page is\nextraction input, so an uncertain note kept under the downgrade protocol\nbecomes a lower-confidence fact. The transcript sidecar keeps its\n`facts_backstop: false`. Don't call `extract_facts` for the meeting or its\ntranscript.\n\n### Phase 6: Claim verification + consistency check (gate for every entity write)\n\nRecorder summaries inject false facts: speech-to-text garbles proper nouns,\nand AI summaries turn banter into commitments. Before writing ANY of the\nfollowing claim types to a person\u002Fcompany page (compiled truth, frontmatter,\nor timeline), verify:\n\n| Claim type | Verification bar |\n|---|---|\n| Relationship\u002Frole 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. |\n| Ownership\u002Fattribution (\"her project\", \"his company\") | A speaker saying a word ≠ owning the thing. Require explicit ownership language or brain corroboration. |\n| New proper nouns (project\u002Fcompany\u002Fproduct 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. |\n| Major life\u002Fdeal events (raised, acquired, hired, shut down) | Verbatim transcript support required. These propagate the furthest and are the most expensive to be wrong about. |\n\n**Consistency check — transcript support alone is NOT sufficient.** A claim\ncan be faithfully transcribed and still wrong. Every claim that passes the\ntranscript bar ALSO gets:\n\n1. **Brain contradiction check.** `gbrain query \"{entity}\"` and read the\n   relevant pages. Does the new claim CONTRADICT established brain truth?\n   When it does, the ESTABLISHED truth wins by default — flag the conflict to\n   the user, don't silently overwrite. New claims override old truth only with\n   explicit, verbatim, unambiguous transcript support, and even then the\n   change is flagged in the ingest report.\n2. **Logic\u002Fplausibility check.** Is the claim POSSIBLE given what else is\n   known? Two people can't both independently \"start\" the same project; a\n   company founded last year can't have been acquired five years ago. A\n   logical impossibility means a probable garble — investigate before writing.\n3. **Surprise = signal.** If a claim would make the user say \"wait, what?\",\n   that surprise is exactly when these checks are mandatory. Boring claims\n   (\"discussed metrics\", \"attended\") pass on transcript verification alone;\n   surprising claims need every layer. Don't rationalize surprise into a story\n   that fits — surface it as a question.\n\n**Downgrade protocol:** if the transcript supports only an inference, record\nit as an explicitly-uncertain note on the meeting page — never in an entity\npage's compiled truth or frontmatter.\n\n**Propagation rule:** a claim that fails verification must not fan out. Do not\ncopy it to other entity pages or timeline entries, and keep it off the\nverified meeting page too: fact extraction carries that page's claims to entity\npages (Phase 5). A false claim written to\nfive pages costs five corrections.\n\n### Phase 7: Attendee enrichment (MANDATORY)\n\nFor EACH attendee:\n1. Does a people page exist? Only an exact match counts: one match from\n   `gbrain whois \u003Cemail> --json`, else from `gbrain whois \"\u003Cname>\" --json`\n   ([identity rule](..\u002Fconventions\u002Fbrain-first.md#identity-exact-match)).\n   A search's top hit is a candidate to check, never the attendee.\n2. If NO → create via the enrich skill (`skills\u002Fenrich\u002FSKILL.md`). Every\n   person who was actually IN the meeting gets a page, even a thin one. Skip\n   only ephemeral third-party mentions (a name invoked about someone not\n   present, with no standalone context) and non-participants (a server taking\n   orders).\n3. If YES → update compiled truth with meeting context (subject to Phase 6).\n4. Add a timeline entry on the person's page:\n   `gbrain timeline-add {person-slug} {date} \"Attended {meeting-title}\"`\n\nBack-link known people who are MENTIONED or SPEAK in the transcript too, not\njust attendees — but high-confidence identifications only. Never backlink a\ngarbled name or a low-confidence guess; a wrong backlink pollutes the graph\nworse than a missing one.\n\n**Note:** Once the meeting page is written via `gbrain put`, the auto-link\npost-hook reads attendance from the page. Where the active schema pack does\nnot override attendance (gbrain-base-v2, which `gbrain init` sets), each\nperson on the `Attendees:` line and in `attendees:` frontmatter (Phase 5) gets a\n`person --attended--> meeting` edge, and people linked anywhere else on the\npage are not recorded as attendance; a pack that overrides attendance, such as\nthe older `gbrain-base`, sets its own rule and direction. Leave attendance to\nauto-link rather than `gbrain link` or `add_link`: a hand-written `attended`\nedge can point the wrong way. Over MCP, `put_page` does not auto-link inline\n(the receipt says `auto_links.skipped: remote`). It queues plain `mentions`\nedges to pages that already exist (`auto_links.mention_links: queued`), but\nnever the typed `attended` edges. Those come from a maintenance pass: a stdio\n`gbrain serve` runs it on its startup and idle sweeps; behind\n`gbrain serve --http` the host runs `gbrain sweep --once` or\n`gbrain extract links --source db`. No MCP tool runs that pass for you, so\nover HTTP ask the host operator, and don't hand-write `attended` with\n`add_link`.\n\nA missing `attended` edge has one of two causes. Either the attendee record\nbreaks a Phase 5 rule, or it names a person whose page did not exist when the\nmeeting page was written; auto-link then reports an error and writes none of\nthe page's links. This skill creates new people pages in Phase 7, after the\nmeeting page, so once Phase 7 is done run `gbrain extract --stale` (over MCP,\nthe sweep above) to link the page. You DO still need `gbrain timeline-add` for\ndated events (auto-link only handles links, not timeline entries).\n\n### Phase 8: Entity propagation + timeline merge (MANDATORY)\n\nFor each company, project, or concept discussed:\n1. Check the brain for an existing page (`gbrain search`, then `gbrain get`).\n2. Create\u002Fupdate as needed (claims subject to Phase 6).\n3. Add a timeline entry referencing the meeting.\n4. Back-link from entity page to meeting page.\n\n**Timeline merge:** the same event appears on ALL mentioned entities'\ntimelines. If alice-example met charlie-example at acme-example, the event\ngoes on alice-example's page, charlie-example's page, AND acme-example's page.\nFor a multi-company session (e.g. group office hours), disaggregate the\nfeedback per company — each company's timeline entry carries its own content,\nnot a blob about the whole session.\n\nIf the meeting contains original thinking worth extracting beyond the page\nitself, chain into `skills\u002Fsignal-detector\u002FSKILL.md` after ingestion.\n\n### Phase 9: Sync\n\nThe pages are already in the brain; this step catches the index up to the\nbrain repo checkout. Use the form that matches the brain:\n\n- **Managed brain** (managed persistence on): `gbrain sync --source \u003Cid> --no-pull`.\n  A managed checkout moves only through `gbrain sources refresh \u003Cid>`, never\n  through sync, so a bare `gbrain sync` there would pull the checkout behind\n  the coordinator's back.\n- **Unmanaged brain:** `gbrain sync`.\n\nIf you can't tell which, use `gbrain sync --source \u003Cid> --no-pull`: it is\ncorrect on both and never moves the checkout.\n\n## Verify before declaring done (HARD GATE)\n\nThe write phases do the work; this phase verifies the work was actually done.\nRun the checklist on the finished page — every item, every meeting, including\n\"quick\" logistics meetings. **Never report a meeting as ingested until every\nitem passes.** Saying \"ingested\" first and fixing later is a contract\nviolation; a false completion report is worse than an honest partial one.\n\n**V1 — Required sections have substance.**\n- `## Summary` carries real outcomes (2+ bullets or a few substantive\n  sentences), not one vague line.\n- `## Key Decisions`, `## Action Items`, and `## Notable Quotes` each have\n  real content OR an explicit reason (`_None — exploratory conversation._`).\n- A bare `- None.` or `_n\u002Fa_` written to silence the checklist is a violation.\n  Before writing \"none\", confirm against the transcript that there truly were\n  no decisions\u002Fcommitments\u002Fquotes worth keeping.\n\n**V2 — Every people\u002Fcompanies slug has a page AND a timeline backlink.**\nFor each person\u002Fcompany slug referenced by the meeting page:\n```bash\ngbrain get people\u002F{slug}          # page exists?\ngbrain timeline people\u002F{slug}     # has an entry pointing back at this meeting?\n```\nA slug with no page means Phase 7\u002F8 was skipped — go do it. A page with no\ntimeline entry for this meeting means the merge was incomplete — add it.\n\n**V3 — Speaker map resolved.**\nNo `Participant N` \u002F `UNKNOWN_N` \u002F raw recorder labels remain in the page\nwithout either a resolution or an explicit uncertainty flag (`[Room]`,\n`⚠️ attribution uncertain`). Every named speaker carries a confidence from\nPhase 4. An unflagged anonymous label means speaker resolution was skipped.\n\n**V4 — Every quote grounded VERBATIM in the transcript.**\n- **Deterministic check (transcript retained):** for each `>` blockquote,\n  verify its contiguous span appears in the transcript sidecar. Filler words\n  (`like`, `you know`, `I mean`) may be stripped from both sides; a genuine\n  quote still shares a long contiguous run of content words, a fabricated one\n  does not.\n  ```bash\n  gbrain get sources\u002Fmeetings\u002F{date}-{slug}-transcript   # then locate each quote span\n  ```\n- **Prompt checklist (no transcript retained):** re-read the source notes and\n  attest that each quote traces to them word-for-word.\n- When a quote fails grounding, the fix is almost always to restore the spoken\n  phrasing (or pick a cleaner contiguous span) — NOT to delete the quote, and\n  never to keep the paraphrase inside the blockquote.\n\n**V5 — Fabricated-attendee sanity checks.**\nRecorders confidently invent names and emails for unlabeled speakers.\n- An attendee name that appears NOWHERE in the transcript or roster evidence\n  did not survive the evidence — treat it as a guess. Identify the real person\n  from in-call tells (companies, shared history) or remove the name.\n- An attendee email whose domain doesn't match the person's claimed org is a\n  fabrication suspect (recorders commonly grab the host's domain). Verify the\n  real address or clear the field.\n- When either fires: do NOT auto-rename or auto-fill. Read the transcript,\n  resolve by evidence, correct the page + frontmatter + backlinks, then re-run\n  this checklist.\n\n**V6 — Sequence verify (order, not substance).**\nA meeting page can be right on depth and wrong on order — they are independent\nfailure axes, and V1–V5 never look at order. Verify the narrated sequence:\n\n1. **Extract event atoms.** Walk the page body in document order and list each\n   narrated sub-event as `{phase, place, people}` — where `phase` is its\n   position relative to the meeting's central event (before \u002F during \u002F after)\n   as the PROSE claims it.\n2. **Deterministic checks** (agent-executed, mechanical — no judgment needed):\n   - **PHASE_INVERSION (hard):** an atom narrated as \"before\" appears after\n     the central event in the document's sequence (or vice versa). Example: the\n     page narrates the debrief of alice-example's pitch, then narrates the\n     pitch itself as if still upcoming.\n   - **TELEPORT (hard):** the same person is placed in two non-adjacent\n     locations with no transit or movement narrated between them. Example:\n     alice-example is in the car en route in one paragraph and already inside\n     the acme-example office in the next, with nothing connecting the two.\n3. **Day-timeline corroboration (soft):**\n   ```bash\n   gbrain day {date}\n   ```\n   assembles that day's events and timeline entries across the whole brain.\n   - **DAY_TIMELINE_GAP:** a narrated participant who never appears in the\n     day's assembled timeline is a SIGNAL, not a hard fail — most often it\n     means their timeline entry was never written (go fix Phase 7\u002F8), and\n     occasionally it means the narration names someone who wasn't there.\n     Investigate; don't auto-block.\n4. **Verdict — PASS or BLOCK.**\n   - No hard contradiction → **PASS**. Proceed to report.\n   - Any PHASE_INVERSION or TELEPORT → **BLOCK**. The meeting is NOT ingested.\n     Fix the narration (re-read the transcript for the true order) and re-run\n     V6 — or, if the user explicitly says the order is fine as written, record\n     a **waive**. A waive is logged in the report as acknowledged, NOT\n     resolved: `sequence: WAIVED by user — {contradiction} stands`.\n   - Genuinely ambiguous order (flashbacks, prose that implies but doesn't\n     state a sequence) is a judgment call, not a deterministic class — flag it\n     in the report, don't block on it.\n\n**The loop:** fix → re-check → fix, until every item passes (or V6 is\nexplicitly waived). Then save the meeting page without the draft\n`facts_backstop: false` line (Phase 5), and report.\n\n## Sensitive meetings\n\nIf the title or transcript signals legal or deeply personal content\n(deposition, attorney, counsel, privileged, health): keep the page minimal and\nfactual, keep its `facts_backstop: false` unless the user agrees to fact\nextraction, do not extract biographical color into other pages, and prefer\nrestraint on back-links. When in doubt about whether content should propagate,\nask the user.\n\n## Output Format\n\nMeeting page created AND the verification checklist passed. Report: \"Meeting\ningested: {N} attendees enriched, {N} entities updated, {N} action items\ncaptured. Verification: passed. Sequence: PASS.\" If the sequence check was\nwaived, say so explicitly: \"Sequence: WAIVED by user — {contradiction} stands\n(acknowledged, not resolved).\" If the recording was split, report one line per\nresulting meeting page. Name the final meeting-page save's `facts_backstop`\nreceipt: `queued`, or `opted_out` with the reason extraction stays off (a\nsensitive meeting the user did not clear). If a claim was withheld or a contradiction flagged by\nPhase 6, list each flag — the user resolves them, not silence. If any\nchecklist item cannot be made to pass, report the meeting as NOT ingested and\nname the failing item.\n\n## When it fails\n\nFollow the [agent operator protocol](..\u002Fconventions\u002Fagent-operator-protocol.md) for any gbrain error `code`, exit code, `[AGENT]` block or notice block. Specific to this skill:\n\n- A contradiction with an existing page blocks ingestion until the user fixes or waives it: show both sources and wait.\n- `add_link` \u002F auto-link reports an error after the meeting page was written: the page is saved but the links are not; list the failed links and add them after fixing slugs.\n- `put_page` returns `revision_conflict` on an attendee page: re-read and merge; never overwrite a person page from an old read.\n\n## Anti-Patterns\n\n- Creating the meeting page without enriching attendees\n- Skipping entity propagation (\"I'll do that later\")\n- Not merging timelines across all mentioned entities\n- Creating attendee stubs without meaningful content\n- Filing meeting pages without cross-linking to all participants\n- Building a per-vendor pipeline or paraphrasing this skill in ad-hoc\n  instructions instead of normalizing to the transcript record\n- Treating a recorder auto-summary name or claim as fact without transcript\n  verification\n- Writing a summary's relationship\u002Frole\u002Fownership claim into an entity page\n  without finding the verbatim transcript line\n- Writing a garbled proper noun bare instead of resolving canonical spelling\n- Fanning an unverified claim out to multiple entity pages\n- Silently overwriting established brain truth with a new meeting claim\n  instead of flagging the contradiction\n- Guessing a speaker identity instead of writing `[Room]`\u002F`UNKNOWN` and flagging\n- Truncating a transcript, or paraphrasing inside a quote blockquote\n- Ingesting one page for a recording that contains two meetings\n- Reporting \"ingested\" before the verification checklist passes\n- Writing `- None.` under a required section to silence the checklist without\n  confirming against the transcript\n- Skipping the checklist because \"it's just a quick logistics meeting\"\n- Declaring a page ingested while a sequence contradiction stands unresolved\n  and unwaived\n- Treating a user waive as a resolution — a waive is an acknowledgment; the\n  contradiction is still in the page\n- Passing a page that puts a person in two non-adjacent places with no\n  transit between them\n- Re-checking substance in the sequence pass (or order in V1–V5) — the axes\n  are orthogonal by design\n","# Meeting Ingestion Skill — Unified Pipeline\n\n> **Filing rule:** Read `skills\u002F_brain-filing-rules.md` before creating any new page.\n\n> **Convention:** See `skills\u002Fconventions\u002Fquality.md` for Iron Law back-linking, and\n> `skills\u002Fconventions\u002Fbrain-first.md` for the lookup chain — resolve every name\n> against the brain BEFORE reaching for external lookups.\n\n## Contract\n\nThis skill guarantees:\n- Works with ANY meeting recorder — AI notetaker export, webhook payload, share\n  link, raw audio transcription, or manual paste. The normalized transcript\n  record (below) is the contract; per-source fetch\u002Fparse is the host agent's job\n- One brain page per REAL meeting — multi-meeting recordings are split first\n- Meeting page created with attendees, summary, key decisions, action items,\n  notable quotes\n- Speakers resolved by evidence, never by guess\n- Recorder auto-summaries treated as CLAIMS, not facts — every surprising claim\n  passes the consistency check before it touches an entity page\n- EVERY attendee gets a people page (created or updated)\n- EVERY company discussed gets entity propagation\n- Timeline entries on ALL mentioned entities (timeline merge)\n- Back-links created bidirectionally\n- Meeting is NOT fully ingested until enrich runs for every entity\n- The meeting is never REPORTED as ingested until the verification checklist\n  (below) passes — every quote grounded, every slug backed by a page and a\n  timeline backlink, every speaker resolved or flagged\n- The narrated SEQUENCE is verified independently of substance: a sequence\n  contradiction BLOCKS ingestion until fixed or explicitly waived by the user\n\nEvery attendee and company mentioned MUST get a back-link from their page to\nthe meeting page. An unlinked mention is a broken brain.\n\n## Any recorder, one pipeline\n\nMeeting content arrives from many sources: an AI notetaker (Granola and\nCircleback are common examples), a phone voice memo, a video-call transcript\nexport, or a transcript the user pastes directly. Do NOT build per-vendor\npipelines or paraphrase this skill in ad-hoc instructions — normalize whatever\nthe source provides into the transcript record below, then run the shared\nphases. Source-specific logic ends at normalization.\n\n## The normalized transcript record\n\nBefore running the pipeline, reduce the input to this shape (mentally or as a\nscratch file — it does not get written to the brain as-is):\n\n```yaml\nsource: \"\u003Crecorder name, or 'manual'>\"\nsource_id: \"\u003Cunique recording id from the source, if any>\"\ntitle: \"Meeting Title\"\ndate: YYYY-MM-DD\ntime: \"HH:MM TZ\"               # null if unknown\nduration: \"45m\"                # null if unknown\nattendees:                     # the SOURCE'S notion of who was there —\n  - name: \"...\"                # may need correction during speaker resolution\n    email: \"...\"               # only if the source provides it\n    role: \"...\"                # only if known\ntranscript_segments:           # structured form when the source diarizes\n  - speaker: \"...\"             # resolved name OR \"UNKNOWN_N\" if unresolved\n    speaker_raw: \"...\"         # the source's raw speaker label, for traceability\n    text: \"...\"\nraw_transcript_text: \"...\"     # the complete transcript. NEVER truncate.\nsource_summary: \"...\"          # the recorder's AI summary if present — a CLAIM, not a FACT\nsource_url: \"...\"              # link back to the source platform, if any\n```\n\n**Invariants:**\n- `raw_transcript_text` is complete and untruncated. Always.\n- `attendees` is a claim by the source. People invited ≠ people present.\n- `source_summary` is TWO lossy layers deep (speech-to-text, then AI\n  summarization). Both layers confabulate. Verify before writing anything\n  from it into the brain.\n\nRetain the raw transcript when the source provides one: file it as a sidecar\npage with `type: source` at `sources\u002Fmeetings\u002FYYYY-MM-DD-{slug}-transcript`\n(the default pack files raw evidence as `source` under `sources\u002F`; never\ninvent `meeting-transcript`, and don't write the `transcript` alias) or keep\nthe source file reachable, and link it from the meeting page. The transcript is the canonical\nevidence for every quote and claim check downstream. Put\n`facts_backstop: false` in the sidecar's frontmatter: automatic fact\nextraction would otherwise mine the raw transcript, garbles and banter\nincluded, beside the verified meeting page (Phase 5).\n\n**Redact before you retain.** A raw transcript routinely captures pasted\nsecrets and PII (a read-aloud API key, a screen-shared token, a private phone\nnumber). Before writing the sidecar, scan for secret-shaped strings (`sk-…`,\n`ghp_…`, `AKIA…`, bearer tokens, long hex\u002Fbase64 blobs) and PII, and redact\nmatches to labeled placeholders — same deterministic deny-list \u002F\n`runPrivacyLint` model as `conversation-archive`. \"Untruncated\" means the\ntranscript's substance, never a live credential.\n\n## Phases\n\n### Phase 1: Normalize the input\n\nBuild the transcript record from whatever arrived. If the input is malformed\n(empty transcript, summary-only payload with no transcript, in-progress\nrecording), STOP — do not create a meeting page from a summary alone. Surface\nthe problem to the user.\n\nFor raw transcript files with no structure at all, `gbrain capture` is the\npreferred entry (it handles dedup and frontmatter routing); this pipeline is\nfor building structured meeting pages.\n\n### Phase 2: Split detection — one recording is not always one meeting\n\nA single recording is often several distinct meetings stitched together (a\nrecorder left running across back-to-back sessions). Detect this BEFORE page\ncreation, so each real meeting becomes its own page and dedupes\u002Fenriches\ncorrectly.\n\n**Split signals** (one is enough to investigate; two or more = split almost\ncertainly):\n- **Roster shift** — a new person arrives mid-transcript (a greeting deep into\n  the file), or the speaker set in the back half differs from the front\n- **Topic hard-cut** with no continuity between the halves\n- **Context reset** — \"ok, next one\", a fresh intro round, a restart phrase\n- **The source's own title\u002Fagenda names multiple sessions**\n\n**When a split is detected:**\n1. Find the boundary segments — the exact points where roster\u002Ftopic flips.\n2. Partition the segments into N contiguous chunks, one per real meeting.\n   Never drop or duplicate a segment; the union must equal the original, in order.\n3. Run the remaining phases once per chunk → N separate meeting pages, each\n   with its own corrected attendees, title, and time.\n4. Cross-link the sibling pages (\"same recording, session k of N\") and note\n   the shared `source_id` so dedup never re-merges them.\n\n**Borderline judgment:** same people + one flowing conversation that wanders\ntopics = ONE meeting; don't over-split. The test is roster + hard context\nbreak, not \"the topic changed.\" If you genuinely cannot tell, surface the\nboundary to the user rather than guessing.\n\n### Phase 3: Dedup across recorders\n\nUsers increasingly run two recorders at once as a backup. Before creating a\npage, check whether the same meeting already exists:\n`gbrain search \"{title or attendee names}\"`, then match by date ± 1 day +\nattendee overlap ≥ 50% + similar title.\n\n- If a page exists, MERGE into it instead of creating a duplicate: build from\n  the RICHER transcript, record both source ids in frontmatter.\n- Source *priority* (which diarization to trust) is not source *completeness*\n  (which transcript is fuller). One recorder may capture 20% of a session the\n  other captured fully. Compare lengths; keep the fuller transcript as the\n  grounding evidence.\n\n### Phase 4: Speaker resolution — before writing the page\n\nRecorders ship anonymous labels (`UNKNOWN_N`, `Participant 2`, `microphone`)\nand sometimes confidently WRONG names. Resolve by evidence:\n\n1. Start from the source's attendee list, corrected by any roster evidence the\n   user can provide (an invite list, an event page, \"it was just me and\n   charlie-example\").\n2. **Never guess.** When uncertain, write `[Room]` or `UNKNOWN` and flag it.\n   A wrong attribution is worse than no attribution.\n3. Cross-reference the brain: `gbrain search \"{name}\"` for each candidate;\n   read their page before accepting an identification.\n4. Assign each named speaker a confidence: `high` (roster-confirmed), `medium`\n   (named unambiguously in the transcript), `low` (inferred from content —\n   flag explicitly).\n5. **Content-identity check:** for every named speaker who claims a\n   role\u002Fcompany\u002Fproduct in their own words (\"I founded X\", \"at my company Y\n   we…\"), verify the claim against that person's brain page. If the named\n   person's established identity CONTRADICTS what the speaker says about\n   themselves, the recorder substituted the wrong person — resolve by\n   identity, not by name, and reattribute the whole track.\n6. **Phantom-speaker check:** if the recorder reports MORE distinct speakers\n   than the known attendee count, suspect over-splitting — one real voice\n   diarized into two labels. Tells: two labels never address each other, or\n   hand off mid-thought. Collapse phantoms into the real attendee.\n7. **User ground truth overrides everything.** If the user states who was in\n   the room, that beats the recorder's diarization AND the roster. Reattribute,\n   fix the page, and never re-litigate a room the user has confirmed.\n8. Speech-to-text garbles names constantly. Before creating a NEW person page\n   from a transcript-only name, search the brain for plausible spelling\n   variants of the surname; default assumption is that a near-miss IS the\n   existing person with a mangled name. Update the existing page and record\n   the variant as an alias rather than creating a duplicate.\n\n### Phase 5: Create meeting page\n\n```markdown\n---\ntype: meeting\nattendees: [{comma-separated slugs of the same people, e.g. people\u002Falice-example}]\nfacts_backstop: false\n---\n\n# {Meeting Title} — {Date}\n\nAttendees: {comma-separated links to the people pages of everyone in the room}\n**Date:** {YYYY-MM-DD}\n**Duration:** {if available}\n\n## Summary\n{3-5 bullet key outcomes}\n\n## Key Decisions\n{Decisions with context. If none: _No decisions — discussion only._}\n\n## Action Items\n{Tasks with owners and deadlines. If none: _None — exploratory conversation._}\n\n## Notable Quotes\n{Verbatim from the transcript, attributed, `>` blockquotes.\nIf none: _No notable quotes — operational\u002Flogistics meeting._}\n\n## Discussion Notes\n{Structured notes by topic}\n```\n\nThe `Attendees:` line is the page's attendance record, and the link extractor\nreads it literally. Write it as one line that starts with `Attendees:` (no\nbold markup) and holds only links to people pages, one per person who was in\nthe room, separated by commas (no \"and\"). Link a person only when the\nidentification is high or medium confidence (Phase 4). Put a company, role,\nspeaker confidence, a low-confidence guess, or an unresolved speaker such as\n`UNKNOWN_2` in Discussion Notes instead: any extra text on the line stops the\nextractor from reading it as the attendance record, and a line wrapped onto a\nsecond line loses everyone after the break. Leave people who were only\ninvited or mentioned off the line. The `attendees:` frontmatter lists\nexactly the same people by slug; extraction reads it as a second attendance\nrecord, so the two must agree.\n\nThe four required sections are Summary, Key Decisions, Action Items, and\nNotable Quotes — additional sections (Discussion Notes, a link to the\ntranscript sidecar) are additive, never replacements. An empty section always\ncarries an explicit reason; a bare `- None.` is a dodge, not an answer.\n\nQuotes are VERBATIM. Write what was said the way it was said — a paraphrase in\na blockquote is a fabricated quote.\n\nTimeline events (`life\u002Fevents\u002F`) are extracted from the saved meeting page in\nthe background (Life Chronicle, on by default; one paid chat call per page).\nCheck the write receipt: `chronicle_backstop.pending: \"next_cycle\"` means the\nnext cycle extracts it (`gbrain dream --phase chronicle` runs it now, paid), and\n`chronicle_backstop.skipped` names the reason and its fix. Never hand-write\n`life\u002Fevents\u002F` pages; edit the meeting page and extraction updates its events.\nSee `docs\u002Fguides\u002Flife-chronicle.md`.\n\nFacts (what `recall` returns) are also extracted from saved pages in the\nbackground, on by default, one paid chat call each time a page's body changes.\nExtraction files each fact on the entity page it names, so it reaches people\nand company pages without passing Phase 6, and it never takes back facts it\nfiled from an earlier version of the page. That is why the template drafts\nthe meeting page with `facts_backstop: false` (the receipt reads\n`facts_backstop: { skipped: \"opted_out\" }`): nothing is extracted while the\npage is still being corrected. When the verification checklist passes, save\nthe page once more without that line; the receipt then reads\n`facts_backstop: { queued: true }`. Everything on the verified page is\nextraction input, so an uncertain note kept under the downgrade protocol\nbecomes a lower-confidence fact. The transcript sidecar keeps its\n`facts_backstop: false`. Don't call `extract_facts` for the meeting or its\ntranscript.\n\n### Phase 6: Claim verification + consistency check (gate for every entity write)\n\nRecorder summaries inject false facts: speech-to-text garbles proper nouns,\nand AI summaries turn banter into commitments. Before writing ANY of the\nfollowing claim types to a person\u002Fcompany page (compiled truth, frontmatter,\nor timeline), verify:\n\n| Claim type | Verification bar |\n|---|---|\n| Relationship\u002Frole 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. |\n| Ownership\u002Fattribution (\"her project\", \"his company\") | A speaker saying a word ≠ owning the thing. Require explicit ownership language or brain corroboration. |\n| New proper nouns (project\u002Fcompany\u002Fproduct 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. |\n| Major life\u002Fdeal events (raised, acquired, hired, shut down) | Verbatim transcript support required. These propagate the furthest and are the most expensive to be wrong about. |\n\n**Consistency check — transcript support alone is NOT sufficient.** A claim\ncan be faithfully transcribed and still wrong. Every claim that passes the\ntranscript bar ALSO gets:\n\n1. **Brain contradiction check.** `gbrain query \"{entity}\"` and read the\n   relevant pages. Does the new claim CONTRADICT established brain truth?\n   When it does, the ESTABLISHED truth wins by default — flag the conflict to\n   the user, don't silently overwrite. New claims override old truth only with\n   explicit, verbatim, unambiguous transcript support, and even then the\n   change is flagged in the ingest report.\n2. **Logic\u002Fplausibility check.** Is the claim POSSIBLE given what else is\n   known? Two people can't both independently \"start\" the same project; a\n   company founded last year can't have been acquired five years ago. A\n   logical impossibility means a probable garble — investigate before writing.\n3. **Surprise = signal.** If a claim would make the user say \"wait, what?\",\n   that surprise is exactly when these checks are mandatory. Boring claims\n   (\"discussed metrics\", \"attended\") pass on transcript verification alone;\n   surprising claims need every layer. Don't rationalize surprise into a story\n   that fits — surface it as a question.\n\n**Downgrade protocol:** if the transcript supports only an inference, record\nit as an explicitly-uncertain note on the meeting page — never in an entity\npage's compiled truth or frontmatter.\n\n**Propagation rule:** a claim that fails verification must not fan out. Do not\ncopy it to other entity pages or timeline entries, and keep it off the\nverified meeting page too: fact extraction carries that page's claims to entity\npages (Phase 5). A false claim written to\nfive pages costs five corrections.\n\n### Phase 7: Attendee enrichment (MANDATORY)\n\nFor EACH attendee:\n1. Does a people page exist? Only an exact match counts: one match from\n   `gbrain whois \u003Cemail> --json`, else from `gbrain whois \"\u003Cname>\" --json`\n   ([identity rule](..\u002Fconventions\u002Fbrain-first.md#identity-exact-match)).\n   A search's top hit is a candidate to check, never the attendee.\n2. If NO → create via the enrich skill (`skills\u002Fenrich\u002FSKILL.md`). Every\n   person who was actually IN the meeting gets a page, even a thin one. Skip\n   only ephemeral third-party mentions (a name invoked about someone not\n   present, with no standalone context) and non-participants (a server taking\n   orders).\n3. If YES → update compiled truth with meeting context (subject to Phase 6).\n4. Add a timeline entry on the person's page:\n   `gbrain timeline-add {person-slug} {date} \"Attended {meeting-title}\"`\n\nBack-link known people who are MENTIONED or SPEAK in the transcript too, not\njust attendees — but high-confidence identifications only. Never backlink a\ngarbled name or a low-confidence guess; a wrong backlink pollutes the graph\nworse than a missing one.\n\n**Note:** Once the meeting page is written via `gbrain put`, the auto-link\npost-hook reads attendance from the page. Where the active schema pack does\nnot override attendance (gbrain-base-v2, which `gbrain init` sets), each\nperson on the `Attendees:` line and in `attendees:` frontmatter (Phase 5) gets a\n`person --attended--> meeting` edge, and people linked anywhere else on the\npage are not recorded as attendance; a pack that overrides attendance, such as\nthe older `gbrain-base`, sets its own rule and direction. Leave attendance to\nauto-link rather than `gbrain link` or `add_link`: a hand-written `attended`\nedge can point the wrong way. Over MCP, `put_page` does not auto-link inline\n(the receipt says `auto_links.skipped: remote`). It queues plain `mentions`\nedges to pages that already exist (`auto_links.mention_links: queued`), but\nnever the typed `attended` edges. Those come from a maintenance pass: a stdio\n`gbrain serve` runs it on its startup and idle sweeps; behind\n`gbrain serve --http` the host runs `gbrain sweep --once` or\n`gbrain extract links --source db`. No MCP tool runs that pass for you, so\nover HTTP ask the host operator, and don't hand-write `attended` with\n`add_link`.\n\nA missing `attended` edge has one of two causes. Either the attendee record\nbreaks a Phase 5 rule, or it names a person whose page did not exist when the\nmeeting page was written; auto-link then reports an error and writes none of\nthe page's links. This skill creates new people pages in Phase 7, after the\nmeeting page, so once Phase 7 is done run `gbrain extract --stale` (over MCP,\nthe sweep above) to link the page. You DO still need `gbrain timeline-add` for\ndated events (auto-link only handles links, not timeline entries).\n\n### Phase 8: Entity propagation + timeline merge (MANDATORY)\n\nFor each company, project, or concept discussed:\n1. Check the brain for an existing page (`gbrain search`, then `gbrain get`).\n2. Create\u002Fupdate as needed (claims subject to Phase 6).\n3. Add a timeline entry referencing the meeting.\n4. Back-link from entity page to meeting page.\n\n**Timeline merge:** the same event appears on ALL mentioned entities'\ntimelines. If alice-example met charlie-example at acme-example, the event\ngoes on alice-example's page, charlie-example's page, AND acme-example's page.\nFor a multi-company session (e.g. group office hours), disaggregate the\nfeedback per company — each company's timeline entry carries its own content,\nnot a blob about the whole session.\n\nIf the meeting contains original thinking worth extracting beyond the page\nitself, chain into `skills\u002Fsignal-detector\u002FSKILL.md` after ingestion.\n\n### Phase 9: Sync\n\nThe pages are already in the brain; this step catches the index up to the\nbrain repo checkout. Use the form that matches the brain:\n\n- **Managed brain** (managed persistence on): `gbrain sync --source \u003Cid> --no-pull`.\n  A managed checkout moves only through `gbrain sources refresh \u003Cid>`, never\n  through sync, so a bare `gbrain sync` there would pull the checkout behind\n  the coordinator's back.\n- **Unmanaged brain:** `gbrain sync`.\n\nIf you can't tell which, use `gbrain sync --source \u003Cid> --no-pull`: it is\ncorrect on both and never moves the checkout.\n\n## Verify before declaring done (HARD GATE)\n\nThe write phases do the work; this phase verifies the work was actually done.\nRun the checklist on the finished page — every item, every meeting, including\n\"quick\" logistics meetings. **Never report a meeting as ingested until every\nitem passes.** Saying \"ingested\" first and fixing later is a contract\nviolation; a false completion report is worse than an honest partial one.\n\n**V1 — Required sections have substance.**\n- `## Summary` carries real outcomes (2+ bullets or a few substantive\n  sentences), not one vague line.\n- `## Key Decisions`, `## Action Items`, and `## Notable Quotes` each have\n  real content OR an explicit reason (`_None — exploratory conversation._`).\n- A bare `- None.` or `_n\u002Fa_` written to silence the checklist is a violation.\n  Before writing \"none\", confirm against the transcript that there truly were\n  no decisions\u002Fcommitments\u002Fquotes worth keeping.\n\n**V2 — Every people\u002Fcompanies slug has a page AND a timeline backlink.**\nFor each person\u002Fcompany slug referenced by the meeting page:\n```bash\ngbrain get people\u002F{slug}          # page exists?\ngbrain timeline people\u002F{slug}     # has an entry pointing back at this meeting?\n```\nA slug with no page means Phase 7\u002F8 was skipped — go do it. A page with no\ntimeline entry for this meeting means the merge was incomplete — add it.\n\n**V3 — Speaker map resolved.**\nNo `Participant N` \u002F `UNKNOWN_N` \u002F raw recorder labels remain in the page\nwithout either a resolution or an explicit uncertainty flag (`[Room]`,\n`⚠️ attribution uncertain`). Every named speaker carries a confidence from\nPhase 4. An unflagged anonymous label means speaker resolution was skipped.\n\n**V4 — Every quote grounded VERBATIM in the transcript.**\n- **Deterministic check (transcript retained):** for each `>` blockquote,\n  verify its contiguous span appears in the transcript sidecar. Filler words\n  (`like`, `you know`, `I mean`) may be stripped from both sides; a genuine\n  quote still shares a long contiguous run of content words, a fabricated one\n  does not.\n  ```bash\n  gbrain get sources\u002Fmeetings\u002F{date}-{slug}-transcript   # then locate each quote span\n  ```\n- **Prompt checklist (no transcript retained):** re-read the source notes and\n  attest that each quote traces to them word-for-word.\n- When a quote fails grounding, the fix is almost always to restore the spoken\n  phrasing (or pick a cleaner contiguous span) — NOT to delete the quote, and\n  never to keep the paraphrase inside the blockquote.\n\n**V5 — Fabricated-attendee sanity checks.**\nRecorders confidently invent names and emails for unlabeled speakers.\n- An attendee name that appears NOWHERE in the transcript or roster evidence\n  did not survive the evidence — treat it as a guess. Identify the real person\n  from in-call tells (companies, shared history) or remove the name.\n- An attendee email whose domain doesn't match the person's claimed org is a\n  fabrication suspect (recorders commonly grab the host's domain). Verify the\n  real address or clear the field.\n- When either fires: do NOT auto-rename or auto-fill. Read the transcript,\n  resolve by evidence, correct the page + frontmatter + backlinks, then re-run\n  this checklist.\n\n**V6 — Sequence verify (order, not substance).**\nA meeting page can be right on depth and wrong on order — they are independent\nfailure axes, and V1–V5 never look at order. Verify the narrated sequence:\n\n1. **Extract event atoms.** Walk the page body in document order and list each\n   narrated sub-event as `{phase, place, people}` — where `phase` is its\n   position relative to the meeting's central event (before \u002F during \u002F after)\n   as the PROSE claims it.\n2. **Deterministic checks** (agent-executed, mechanical — no judgment needed):\n   - **PHASE_INVERSION (hard):** an atom narrated as \"before\" appears after\n     the central event in the document's sequence (or vice versa). Example: the\n     page narrates the debrief of alice-example's pitch, then narrates the\n     pitch itself as if still upcoming.\n   - **TELEPORT (hard):** the same person is placed in two non-adjacent\n     locations with no transit or movement narrated between them. Example:\n     alice-example is in the car en route in one paragraph and already inside\n     the acme-example office in the next, with nothing connecting the two.\n3. **Day-timeline corroboration (soft):**\n   ```bash\n   gbrain day {date}\n   ```\n   assembles that day's events and timeline entries across the whole brain.\n   - **DAY_TIMELINE_GAP:** a narrated participant who never appears in the\n     day's assembled timeline is a SIGNAL, not a hard fail — most often it\n     means their timeline entry was never written (go fix Phase 7\u002F8), and\n     occasionally it means the narration names someone who wasn't there.\n     Investigate; don't auto-block.\n4. **Verdict — PASS or BLOCK.**\n   - No hard contradiction → **PASS**. Proceed to report.\n   - Any PHASE_INVERSION or TELEPORT → **BLOCK**. The meeting is NOT ingested.\n     Fix the narration (re-read the transcript for the true order) and re-run\n     V6 — or, if the user explicitly says the order is fine as written, record\n     a **waive**. A waive is logged in the report as acknowledged, NOT\n     resolved: `sequence: WAIVED by user — {contradiction} stands`.\n   - Genuinely ambiguous order (flashbacks, prose that implies but doesn't\n     state a sequence) is a judgment call, not a deterministic class — flag it\n     in the report, don't block on it.\n\n**The loop:** fix → re-check → fix, until every item passes (or V6 is\nexplicitly waived). Then save the meeting page without the draft\n`facts_backstop: false` line (Phase 5), and report.\n\n## Sensitive meetings\n\nIf the title or transcript signals legal or deeply personal content\n(deposition, attorney, counsel, privileged, health): keep the page minimal and\nfactual, keep its `facts_backstop: false` unless the user agrees to fact\nextraction, do not extract biographical color into other pages, and prefer\nrestraint on back-links. When in doubt about whether content should propagate,\nask the user.\n\n## Output Format\n\nMeeting page created AND the verification checklist passed. Report: \"Meeting\ningested: {N} attendees enriched, {N} entities updated, {N} action items\ncaptured. Verification: passed. Sequence: PASS.\" If the sequence check was\nwaived, say so explicitly: \"Sequence: WAIVED by user — {contradiction} stands\n(acknowledged, not resolved).\" If the recording was split, report one line per\nresulting meeting page. Name the final meeting-page save's `facts_backstop`\nreceipt: `queued`, or `opted_out` with the reason extraction stays off (a\nsensitive meeting the user did not clear). If a claim was withheld or a contradiction flagged by\nPhase 6, list each flag — the user resolves them, not silence. If any\nchecklist item cannot be made to pass, report the meeting as NOT ingested and\nname the failing item.\n\n## When it fails\n\nFollow the [agent operator protocol](..\u002Fconventions\u002Fagent-operator-protocol.md) for any gbrain error `code`, exit code, `[AGENT]` block or notice block. Specific to this skill:\n\n- A contradiction with an existing page blocks ingestion until the user fixes or waives it: show both sources and wait.\n- `add_link` \u002F auto-link reports an error after the meeting page was written: the page is saved but the links are not; list the failed links and add them after fixing slugs.\n- `put_page` returns `revision_conflict` on an attendee page: re-read and merge; never overwrite a person page from an old read.\n\n## Anti-Patterns\n\n- Creating the meeting page without enriching attendees\n- Skipping entity propagation (\"I'll do that later\")\n- Not merging timelines across all mentioned entities\n- Creating attendee stubs without meaningful content\n- Filing meeting pages without cross-linking to all participants\n- Building a per-vendor pipeline or paraphrasing this skill in ad-hoc\n  instructions instead of normalizing to the transcript record\n- Treating a recorder auto-summary name or claim as fact without transcript\n  verification\n- Writing a summary's relationship\u002Frole\u002Fownership claim into an entity page\n  without finding the verbatim transcript line\n- Writing a garbled proper noun bare instead of resolving canonical spelling\n- Fanning an unverified claim out to multiple entity pages\n- Silently overwriting established brain truth with a new meeting claim\n  instead of flagging the contradiction\n- Guessing a speaker identity instead of writing `[Room]`\u002F`UNKNOWN` and flagging\n- Truncating a transcript, or paraphrasing inside a quote blockquote\n- Ingesting one page for a recording that contains two meetings\n- Reporting \"ingested\" before the verification checklist passes\n- Writing `- None.` under a required section to silence the checklist without\n  confirming against the transcript\n- Skipping the checklist because \"it's just a quick logistics meeting\"\n- Declaring a page ingested while a sequence contradiction stands unresolved\n  and unwaived\n- Treating a user waive as a resolution — a waive is an acknowledgment; the\n  contradiction is still in the page\n- Passing a page that puts a person in two non-adjacent places with no\n  transit between them\n- Re-checking substance in the sequence pass (or order in V1–V5) — the axes\n  are orthogonal by design\n",{"name":5,"tools":51,"version":58,"mutating":59,"triggers":60,"upstream":71,"writes_to":75,"description":79,"when_to_use":80,"writes_pages":59},[40,37,52,53,54,55,56,57],"get_page","put_page","add_link","add_timeline_entry","get_timeline","chronicle_day","2.2.0",true,[61,62,63,64,65,66,67,68,69,70],"meeting transcript","process this meeting","meeting notes","meeting recorder","ingest this recording","capture meetings","audit this meeting","check the sequence","did I get the order right","meeting transcript received",[72,73,74],"meeting-ingestion@fc834ee","meeting-gold-standard@fc834ee","chronology-guard@fc834ee",[76,77,78],"meetings\u002F","people\u002F","companies\u002F","Ingest meeting transcripts from ANY meeting recorder into brain pages with\nattendee enrichment, entity propagation, and timeline merge. One unified\npipeline: normalize the source into a standard transcript record, split\nmulti-meeting recordings, resolve speakers by evidence, create the page,\npass every surprising claim through the consistency check (transcript +\nbrain + plausibility), enrich every entity, then run the verification\nchecklist — substance AND sequence. A meeting is NOT fully ingested until\nthe enrich skill has processed every entity AND the verification checklist\npasses, including the sequence verify (PASS or explicit user waive).\n","Use when the user asks: \"meeting transcript\", \"process this meeting\", \"meeting notes\", \"meeting recorder\", \"ingest this recording\".","master","074ec301549ded4930f04d2c3b22d709a6c095cc","plugin\u002Fskills\u002Fmeeting-ingestion",4871,"https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain","https:\u002F\u002Fgithub.com\u002Fgarrytan","https:\u002F\u002Fblog.garrytan.com","Writes software, dreams. ","2026-11-12 04:41:06",{"source":8,"skillName":5,"commands":91,"prompt":101,"zipAvailable":59},[92,95,98],{"tool":93,"command":94},"npx","npx skills add garrytan\u002Fgbrain --skill meeting-ingestion",{"tool":96,"command":97},"bunx","bunx skills add garrytan\u002Fgbrain --skill meeting-ingestion",{"tool":99,"command":100},"pnpm","pnpm dlx skills add garrytan\u002Fgbrain --skill meeting-ingestion","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Fplugin\u002Fskills\u002Fmeeting-ingestion\u002FSKILL.md",[103],{"path":104,"resourceType":105,"resourceTypeDesc":106,"sizeBytes":107,"sourceHtmlUrl":108},"routing-eval.jsonl","OTHER","其他",1019,"https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Fplugin\u002Fskills\u002Fmeeting-ingestion\u002Frouting-eval.jsonl",[110,121,127,147,169,183],{"skillNo":111,"name":112,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":113,"categories":114,"keywords":115,"publishedAt":46},"01M6N109FNGH1PK49T734PZXGB","query-helper","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog\u002Fskills\u002Fquery-helper\u002FSKILL.md",[],[116,117,37,40,118,119,120],"query helper","helper","page","find","false",{"skillNo":122,"name":123,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":124,"categories":125,"keywords":126,"publishedAt":46},"01M6N108A23GW6NGQEQ0E7GV0A","broken","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog\u002Fskills\u002Fbroken\u002FSKILL.md",[],[123],{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":133,"publishedAt":46},"01M6N105E88C5K2H03H0CC79G1","brain-ops","Read, enrich, and write brain pages with source attribution.","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog\u002Fskills\u002Fbrain-ops\u002FSKILL.md",[],[134,34,135,136,137,118,138,139,140,141,37,142,33,143,32,144,145,146,40],"brain ops","ops","true","attribution","read","write","up","look","pages","with","web","path","source",{"skillNo":148,"name":149,"nameZh":6,"description":150,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":151,"categories":152,"keywords":154,"publishedAt":46},"01M6N102GRMF22TVCNT6RK5MRD","meeting-prep","Build a briefing before you meet someone, from their recent threads, open loops and what they care about.","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog-query\u002Fskills\u002Fmeeting-prep\u002FSKILL.md",[153],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[25,155,156,157,158,40,159,160,161,37,120,162,163,164,165,166,167,168],"prep","meeting prep","me","prepare","brief","open","build","loops","about","you","briefing","threads","for","Meet",{"skillNo":170,"name":171,"nameZh":6,"description":172,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":173,"categories":174,"keywords":175,"publishedAt":46},"01M6N1005QK7REHX5KGMNR5PF0","inbox-zero","Triage the inbox into replies, delegations and archives.","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog-query\u002Fskills\u002Finbox-zero\u002FSKILL.md",[],[176,177,178,40,179,120,180,181,182],"inbox zero","ZeRO","inbox","clear","replies","my","triage",{"skillNo":184,"name":185,"nameZh":6,"description":186,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":187,"categories":188,"keywords":189,"publishedAt":46},"01M6N0ZXQ8RRRW71PNV61T57GP","deal-memo","Draft an investment memo from the deal pages and the founder history already in the brain.","https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgbrain\u002Fblob\u002Fmaster\u002Ftest\u002Ffixtures\u002Fskill-catalog-query\u002Fskills\u002Fdeal-memo\u002FSKILL.md",[],[190,191,192,142,34,193,194,139,195,136,40,196],"deal memo","deal","memo","draft","investment","history","founder"]