Add internal-meeting-brief — first real skill in the Brief family
Real-tested 2026-09-21 against the APTA 2026 booth 09-16 planning meeting (recap mode). Includes: - skills/internal-meeting-brief/SKILL.md — full workflow: topic resolution, continuity manifest, agenda/recap branching, attendee capture/normalization, scoped data pulls, synthesis, render, archive. - assets/brief-components.css — canonical shared component library (cards, flow, layerbox/filepill, unitgrid, quad, compare, specbar, actions+status pills, note callouts x4 tones, colvis/ring), MPM tokens from CW-028, required light/dark toggle, print rules. - references/section-structure.md — fixed-vs-dynamic section contract, explicitly marked unproven beyond one real run. Known limitation: Plaud MCP connector is currently non-functional (local server via Claude Desktop, times out). Brief was built from a manually uploaded transcript, not a live pull. Five more skills planned, not started: vendor-partner-brief, status-update-brief, five-whys-brief, kickoff-brief, development-brief. Full design history in CW-027's Concept.md (Drive).
This commit is contained in:
1 parent
1fd2f91762
commit
c98daed984
5 files changed
+488
-2
No files matched your search
@@ -0,0 +1,142 @@
|
||||
---
|
||||
name: internal-meeting-brief
|
||||
description: Generate an MPM-branded internal meeting briefing document as a single-file HTML artifact — covers internal planning sessions, executive leadership meetings, and project team meetings (e.g. APTA planning, status reviews, internal design discussions). Produces agenda-mode (pre-meeting prep, pulling the previous meeting's transcript) or recap-mode (post-meeting summary, pulling the current meeting's transcript). Always use this skill when the user asks to "brief this meeting," "recap the meeting," "prep me for [meeting]," "write up the [meeting name] meeting," or names an internal MPM meeting/topic and wants it documented or summarized — even if they don't say "internal-meeting-brief" by name or don't mention CoWork/Brief explicitly. Part of the CW-027 Brief plugin family; see git.mpm.to/mpm/brief.
|
||||
---
|
||||
|
||||
# internal-meeting-brief
|
||||
|
||||
Generates a single self-contained HTML briefing document for an internal MPM meeting,
|
||||
built on Brief's shared MPM-branded component library. Does the work of assembling the
|
||||
brief itself — pulling from connected tools — rather than requiring the person to
|
||||
hand-supply meeting notes.
|
||||
|
||||
Read `assets/brief-components.css` and `references/section-structure.md` before
|
||||
generating any brief. The CSS is the canonical, only source of Brief's visual
|
||||
language — never re-derive it inline. The section-structure doc is the fixed-vs-dynamic
|
||||
contract for what this skill's output actually contains.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This skill uses, but does not own, the following connected tools — confirm they're
|
||||
reachable before starting, and degrade gracefully (see Hard Rules) if one isn't:
|
||||
- **Plaud** — meeting transcripts (recap: current meeting; agenda: previous meeting)
|
||||
- **Odoo** (MCP) — linked Project/tasks, Sales/Purchase Orders, Knowledge Base,
|
||||
Helpdesk tickets
|
||||
- **Google Workspace** (MCP) — Gmail, Google Chat, Drive, Sheets
|
||||
- **Google Drive** — the topic's continuity folder (`_topic-state.md` + brief history)
|
||||
|
||||
If any of these fail outright (not just "returns nothing," but errors or times out),
|
||||
say so plainly in the generated brief's Notes & Flags section rather than silently
|
||||
omitting that source. See "Known limitation" below for a real, currently-open example.
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Resolve the topic
|
||||
Every brief belongs to a project/topic, not just a meeting instance. Ask which
|
||||
topic/project this meeting is about if it isn't obvious from context. Search Google
|
||||
Drive for an existing topic folder (naming pattern: `<Topic Name> — Meeting Briefs`)
|
||||
before creating a new one. If an Odoo Project already exists for this topic and has an
|
||||
attached Drive folder, use that folder instead of creating a parallel one.
|
||||
|
||||
### 2. Read (or create) the continuity manifest
|
||||
Open `_topic-state.md` in the topic folder. If it doesn't exist, this is the topic's
|
||||
first run — create it with the schema below, and expect a fuller interview in the
|
||||
following steps since there's no history to draw on yet.
|
||||
|
||||
`_topic-state.md` schema: linked Odoo Project ID / SO / PO, linked Google Chat Space,
|
||||
Plaud recording ID/pattern, last-meeting date, running open-items/decisions ledger,
|
||||
attendee list, learned spreadsheet task-registry column mapping (if this topic uses one
|
||||
— see step 5), brief-history log (date/type/mode/link), and a "Related documents"
|
||||
table for cross-referencing other MPM document types about the same real-world topic
|
||||
(pre-mortems, BAI build docs, etc. — see CW-027 Concept.md's rewrap-vs-harvest
|
||||
principle; don't merge their content into this ledger, just reference them).
|
||||
|
||||
### 3. Determine mode: Agenda or Recap
|
||||
Agenda mode (pre-meeting prep) pulls the *previous* meeting's transcript and any
|
||||
updates since. Recap mode (post-meeting) pulls the *current* meeting's transcript. Ask
|
||||
if not specified by the request.
|
||||
|
||||
### 4. Capture and normalize attendees
|
||||
- **First run for this topic:** ask who's attending / who attended.
|
||||
- **Recurring topic:** pre-fill from the manifest's last attendee list and ask "same as
|
||||
last time, or any changes?" — not a blank-slate interview.
|
||||
- **Recap mode with a Plaud transcript available:** prefer *actual* attendance from the
|
||||
transcript's speaker attribution over the pre-meeting expected list — someone who
|
||||
showed up unexpectedly and spoke shouldn't be treated as absent.
|
||||
- **Normalize into two groups:**
|
||||
- Internal MPM attendees — record by name for the brief; no further resolution
|
||||
needed (no external allowlist implications for this brief type).
|
||||
- External attendees (rare for this brief type, but possible) — cross-reference
|
||||
against Odoo contacts to resolve/confirm email addresses rather than trusting raw
|
||||
input.
|
||||
- **If Plaud's diarization didn't resolve a speaker to a name:** render the literal
|
||||
transcript label ("Speaker 1," "Speaker 3"). Do not guess a name from context, even
|
||||
when a guess feels obvious. Flag unresolved speakers in the Notes & Flags section.
|
||||
|
||||
### 5. Pull data, scoped to what this brief type actually needs
|
||||
Not a monolithic "pull everything" — internal-meeting-brief's real needs, based on the
|
||||
first run, are:
|
||||
- Plaud transcript (per mode, above) — the primary source
|
||||
- Gmail / Google Chat: updates since the manifest's last-meeting date, scoped by
|
||||
project/topic keywords, never a blanket search
|
||||
- Odoo Project tasks, if a linked project exists (expect this to be sparse or absent —
|
||||
PM-tool adoption is inconsistent; fall back to a spreadsheet task registry if the
|
||||
topic uses one, learned via a one-time interview on first use and stored in the
|
||||
manifest, never assumed to follow a universal schema)
|
||||
|
||||
Sales/Purchase Orders and Helpdesk ticket review are not typically relevant to this
|
||||
brief type (they matter more to vendor-partner-brief and status-update-brief) — don't
|
||||
pull them by default.
|
||||
|
||||
### 6. Synthesize into the section structure
|
||||
Follow `references/section-structure.md` exactly: six fixed sections in order, dynamic
|
||||
sections only where the meeting's actual content has a structure that calls for one.
|
||||
Real, verified content over generic summary — carry forward specific facts (names,
|
||||
dates, exact technical decisions), not vague paraphrase.
|
||||
|
||||
### 7. Render
|
||||
Single self-contained HTML file. Include `assets/brief-components.css` inline (don't
|
||||
link out to it — the brief must survive as a standalone file). Include: sticky jump-nav,
|
||||
a working light/dark toggle (`data-theme` attribute, default light), and the
|
||||
`@media print` rules from the shared CSS (forces light, hides nav/toggle, avoids
|
||||
awkward page breaks).
|
||||
|
||||
### 8. Archive
|
||||
Save the generated HTML into the topic's Drive folder. Update `_topic-state.md`: append
|
||||
to the brief-history log, update the running open-items/decisions ledger (carry forward
|
||||
anything still open from this run), update the attendee list, update last-meeting date.
|
||||
|
||||
### 9. Present
|
||||
Hand the file to the person. Don't silently skip presenting it even if archived — the
|
||||
person needs to actually see and review it, not just trust it happened.
|
||||
|
||||
## Hard rules
|
||||
|
||||
- **Never fabricate.** No matching Plaud recording, no linked Odoo data, an empty
|
||||
Gmail/Chat scan — say so plainly in Notes & Flags. A brief with visible gaps is
|
||||
correct; a brief with invented content to fill them is not.
|
||||
- **Bounded queries only.** Gmail/Chat scans are scoped by project keyword + date
|
||||
window from the manifest. Never an open-ended search.
|
||||
- **No live publishing yet.** This skill produces a local file only. Hosting/publishing
|
||||
(a shareable link, external access control) is CW-029 (Brief Server) — deliberately
|
||||
separate, deliberately not built yet. Don't attempt to stand up delivery
|
||||
infrastructure as part of running this skill.
|
||||
|
||||
## Known limitation (open as of 2026-09-21)
|
||||
|
||||
The Plaud MCP connector has failed on every attempt so far — first appeared to be an
|
||||
account/workspace mismatch, but a later attempt timed out with a message indicating it
|
||||
runs as a **local server via the Claude Desktop app**, not a cloud connector. Confirmed
|
||||
still broken after Bryan verified the connector's own settings looked correct. Treat
|
||||
Plaud as unreliable until this is resolved — the one real brief produced so far
|
||||
(2026-09-16 APTA meeting) used a manually uploaded transcript as a workaround, not a
|
||||
live pull. Don't assume this is fixed without testing it again.
|
||||
|
||||
## Provenance
|
||||
|
||||
Component library, tokens, and this workflow were derived from two real test runs
|
||||
(2026-09-21): a recap brief for a real APTA 2026 booth planning meeting, and a
|
||||
rewrap of an unrelated pre-mortem document to validate the component system against a
|
||||
different content shape. See CW-027's Concept.md (Drive) for full design history and
|
||||
the five other planned skills in this family (vendor-partner-brief, status-update-brief,
|
||||
five-whys-brief, kickoff-brief, development-brief) — none built yet.
|
||||
@@ -0,0 +1,236 @@
|
||||
/* ==========================================================================
|
||||
Brief — shared component library (CW-027)
|
||||
Canonical CSS for all Brief skills. Do not fork per-brief — edit here.
|
||||
|
||||
Design tokens sourced from CW-028 (@jason/ui-kit, src/lib/tokens.json).
|
||||
Light theme is default; dark theme via [data-theme="dark"]; print always
|
||||
forces light regardless of the on-screen toggle state.
|
||||
|
||||
Re-pull tokens from git.alwisp.com/jason/ui-kit at each Brief release in
|
||||
case Jason has updated them — see CW-027 Concept.md, "Design system
|
||||
source of truth."
|
||||
========================================================================== */
|
||||
|
||||
:root{
|
||||
--bg:#FAF8F4; --surface:#FFFFFF; --surface-2:#F4F0EA; --border:#DED8D1;
|
||||
--text:#272326; --muted:#665F62;
|
||||
--brand:#79640E; --brand-bright:#665308; --brand-dark:#5C4B07; --brand-fg:#FFFFFF;
|
||||
--success:#217A4A; --success-fg:#FFFFFF;
|
||||
--warning:#7A5B00; --warning-fg:#FFFFFF;
|
||||
--danger:#A42D20; --danger-fg:#FFFFFF;
|
||||
--info:#366B99; --info-fg:#FFFFFF;
|
||||
|
||||
--radius-sm:6px; --radius-md:10px; --radius-lg:14px; --radius-xl:20px;
|
||||
--shadow-sm:0 1px 2px 0 rgb(20 16 18 / .10);
|
||||
--shadow-md:0 4px 12px -2px rgb(20 16 18 / .14), 0 2px 4px -2px rgb(20 16 18 / .10);
|
||||
--shadow-lg:0 12px 32px -8px rgb(20 16 18 / .20), 0 4px 8px -4px rgb(20 16 18 / .12);
|
||||
--shadow-glow:0 0 0 1px rgb(121 100 14 / .18), 0 8px 28px -6px rgb(121 100 14 / .20);
|
||||
--ease-out:cubic-bezier(0.22,1,0.36,1);
|
||||
--dur-fast:120ms; --dur-base:180ms; --dur-slow:320ms;
|
||||
}
|
||||
[data-theme="dark"]{
|
||||
--bg:#232022; --surface:#2B282A; --surface-2:#333031; --border:#3A3638;
|
||||
--text:#F5F1EC; --muted:#A99F97;
|
||||
--brand:#DCBB4F; --brand-bright:#F5CD15; --brand-dark:#998643; --brand-fg:#232022;
|
||||
--success:#62C890; --success-fg:#232022;
|
||||
--warning:#E4C85B; --warning-fg:#232022;
|
||||
--danger:#D76F4A; --danger-fg:#232022;
|
||||
--info:#76A9D6; --info-fg:#232022;
|
||||
--shadow-glow:0 0 0 1px rgb(220 187 79 / .20), 0 8px 28px -6px rgb(220 187 79 / .28);
|
||||
}
|
||||
|
||||
/* Fonts: production should self-host from CW-001's assets/fonts/*.ttf.
|
||||
This @import is a portability fallback for previewing outside that
|
||||
pipeline — swap it for local <link>/@font-face at publish time. */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@600;700&family=Open+Sans:wght@400;600&family=JetBrains+Mono:wght@500&display=swap');
|
||||
|
||||
*{box-sizing:border-box;}
|
||||
body{
|
||||
margin:0; background:var(--bg); color:var(--text);
|
||||
font-family:"Open Sans",system-ui,sans-serif; line-height:1.55;
|
||||
transition:background var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out);
|
||||
}
|
||||
h1,h2,h3,h4{font-family:"Montserrat",system-ui,sans-serif;}
|
||||
code,.mono{font-family:"JetBrains Mono",ui-monospace,monospace;}
|
||||
|
||||
/* ---- Header ---- */
|
||||
header{
|
||||
background:linear-gradient(135deg, var(--brand-dark), var(--brand));
|
||||
color:#fff; padding:32px 6vw 26px; position:relative;
|
||||
}
|
||||
[data-theme="dark"] header{background:linear-gradient(135deg,#3a2f08,var(--brand-dark));}
|
||||
header .wordmark{
|
||||
display:inline-flex; align-items:center; gap:8px; font-family:"Montserrat";
|
||||
font-weight:700; font-size:15px; letter-spacing:1px; opacity:.9; margin-bottom:10px;
|
||||
}
|
||||
header .wordmark .dot{width:8px; height:8px; border-radius:50%; background:#fff;}
|
||||
header .eyebrow{font-size:12.5px; letter-spacing:2px; text-transform:uppercase; opacity:.8;}
|
||||
header h1{margin:8px 0 4px; font-size:26px; font-weight:700; max-width:860px;}
|
||||
header .meta{margin-top:12px; font-size:13px; opacity:.9; display:flex; flex-wrap:wrap; gap:18px;}
|
||||
header .meta b{font-weight:600;}
|
||||
.theme-toggle{
|
||||
position:absolute; top:28px; right:6vw; background:rgba(255,255,255,.16);
|
||||
border:1px solid rgba(255,255,255,.35); color:#fff; border-radius:999px;
|
||||
padding:7px 14px; font-size:12.5px; font-weight:600; cursor:pointer;
|
||||
transition:background var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.theme-toggle:hover{background:rgba(255,255,255,.28);}
|
||||
|
||||
/* ---- Sticky jump-nav ---- */
|
||||
nav.toc{
|
||||
background:var(--surface); border-bottom:1px solid var(--border);
|
||||
padding:10px 6vw; display:flex; flex-wrap:wrap; gap:6px 16px;
|
||||
position:sticky; top:0; z-index:50; font-size:12.5px;
|
||||
}
|
||||
nav.toc a{color:var(--brand); text-decoration:none; white-space:nowrap; font-weight:600;}
|
||||
nav.toc a:hover{text-decoration:underline;}
|
||||
|
||||
/* ---- Layout & section rhythm ---- */
|
||||
main{padding:26px 6vw 60px; max-width:1180px; margin:0 auto;}
|
||||
section{margin-bottom:42px;}
|
||||
section > .sechead{
|
||||
display:flex; align-items:baseline; gap:10px; margin-bottom:14px;
|
||||
border-bottom:3px solid var(--brand); padding-bottom:8px;
|
||||
}
|
||||
.secnum{
|
||||
background:var(--brand); color:var(--brand-fg); font-weight:700; font-size:14px;
|
||||
width:28px; height:28px; border-radius:var(--radius-sm); display:flex;
|
||||
align-items:center; justify-content:center; flex-shrink:0;
|
||||
}
|
||||
.sechead h2{margin:0; font-size:19px; color:var(--text);}
|
||||
.sechead .sub{font-size:12px; font-weight:400; color:var(--muted); margin-left:4px;}
|
||||
.sec-note{font-size:13px; color:var(--muted); margin:8px 0 14px; max-width:75ch;}
|
||||
|
||||
/* ---- Cards (c2/c3/c4/c5 densities) ---- */
|
||||
.cards{display:grid; gap:14px;}
|
||||
.cards.c2{grid-template-columns:repeat(2,1fr);}
|
||||
.cards.c3{grid-template-columns:repeat(3,1fr);}
|
||||
.cards.c4{grid-template-columns:repeat(4,1fr);}
|
||||
.cards.c5{grid-template-columns:repeat(5,1fr);}
|
||||
@media(max-width:900px){.cards.c3,.cards.c4,.cards.c5{grid-template-columns:1fr 1fr;}}
|
||||
@media(max-width:600px){.cards.c2,.cards.c3,.cards.c4,.cards.c5{grid-template-columns:1fr;}}
|
||||
.card{
|
||||
background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md);
|
||||
padding:16px; box-shadow:var(--shadow-sm);
|
||||
transition:box-shadow var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
|
||||
}
|
||||
.card:hover{box-shadow:var(--shadow-md); transform:translateY(-1px);}
|
||||
.card h3{margin:0 0 6px; font-size:14.5px; color:var(--text);}
|
||||
.card p{margin:6px 0; font-size:13px; color:var(--muted);}
|
||||
.icon{
|
||||
width:36px; height:36px; border-radius:var(--radius-sm); display:flex;
|
||||
align-items:center; justify-content:center; color:#fff; font-size:17px;
|
||||
font-weight:700; margin-bottom:8px;
|
||||
}
|
||||
.sensorgrid .card{text-align:center;}
|
||||
.sensorgrid .icon{margin:0 auto 8px;}
|
||||
|
||||
/* ---- Flow (sequential process) ---- */
|
||||
.flow{display:flex; align-items:stretch; gap:10px; flex-wrap:wrap;}
|
||||
.flow .step{flex:1; min-width:160px;}
|
||||
.flow .arrow{align-self:center; font-size:20px; color:var(--muted); padding:0 2px;}
|
||||
|
||||
/* ---- Layerbox + filepill (tiered component grouping) ---- */
|
||||
.layerbox{border-radius:var(--radius-lg); padding:16px 18px; margin-bottom:12px; border:2px solid var(--border);}
|
||||
.layer-base{background:color-mix(in srgb, var(--info) 8%, var(--surface)); border-color:var(--info);}
|
||||
.layer-ext{background:color-mix(in srgb, var(--warning) 10%, var(--surface)); border-style:dashed; border-color:var(--warning);}
|
||||
.layerbox .tag{
|
||||
display:inline-block; font-size:10.5px; font-weight:700; letter-spacing:.5px;
|
||||
padding:2px 9px; border-radius:999px; color:#fff; margin-bottom:8px;
|
||||
}
|
||||
.tag-base{background:var(--info);} .tag-ext{background:var(--warning); color:var(--text);}
|
||||
.filepill{
|
||||
display:inline-flex; flex-direction:column; background:var(--surface); border:1px solid var(--border);
|
||||
border-radius:var(--radius-sm); padding:8px 12px; margin:4px 8px 4px 0; font-size:12.5px; min-width:160px;
|
||||
}
|
||||
.filepill b{font-size:13px; color:var(--text);}
|
||||
.filepill span{color:var(--muted);}
|
||||
|
||||
/* ---- Unitgrid (spec/comparison matrix) ---- */
|
||||
table.unitgrid{border-collapse:collapse; width:100%; margin-top:10px; font-size:12.5px;}
|
||||
table.unitgrid th, table.unitgrid td{border:1px solid var(--border); padding:8px 10px; text-align:center;}
|
||||
table.unitgrid th{background:var(--text); color:var(--bg); font-weight:600;}
|
||||
table.unitgrid td.yes{color:var(--success); font-weight:700;}
|
||||
table.unitgrid td.no{color:var(--muted);}
|
||||
table.unitgrid td:first-child{text-align:left; font-weight:600;}
|
||||
|
||||
/* ---- Quad (2x2 flagship callouts) ---- */
|
||||
.quad{display:grid; grid-template-columns:1fr 1fr; gap:14px;}
|
||||
@media(max-width:700px){.quad{grid-template-columns:1fr;}}
|
||||
.quadcard{
|
||||
border-radius:var(--radius-md); padding:14px 16px; background:var(--surface);
|
||||
border:1px solid var(--border); border-left-width:5px; border-left-style:solid;
|
||||
}
|
||||
.q-1{border-left-color:var(--info);}
|
||||
.q-2{border-left-color:var(--warning);}
|
||||
.q-3{border-left-color:var(--danger);}
|
||||
.q-4{border-left-color:var(--success);}
|
||||
.quadcard h4{margin:0 0 6px; font-size:13.5px; color:var(--text);}
|
||||
.quadcard p{font-size:12.5px; margin:4px 0; color:var(--muted);}
|
||||
|
||||
/* ---- Compare (two-column contrast) ---- */
|
||||
.compare{display:grid; grid-template-columns:1fr 1fr; gap:16px;}
|
||||
@media(max-width:700px){.compare{grid-template-columns:1fr;}}
|
||||
.compare .col{border-radius:var(--radius-md); padding:14px 16px; border:1px solid var(--border);}
|
||||
.col-a{background:color-mix(in srgb, var(--info) 8%, var(--surface));}
|
||||
.col-b{background:color-mix(in srgb, var(--brand) 8%, var(--surface));}
|
||||
.compare h3{margin-top:0; font-size:14px;}
|
||||
.compare ul{margin:8px 0 0; padding-left:18px; font-size:12.5px; color:var(--muted);}
|
||||
.compare li{margin-bottom:8px;}
|
||||
|
||||
/* ---- Specbar (stat cards) ---- */
|
||||
.specbar{display:flex; flex-wrap:wrap; gap:14px;}
|
||||
.spec{flex:1; min-width:160px; background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); padding:14px;}
|
||||
.spec .val{font-size:22px; font-weight:700; color:var(--brand); font-family:"Montserrat";}
|
||||
.spec .lbl{font-size:11.5px; color:var(--muted); margin-top:2px;}
|
||||
|
||||
/* ---- Actions table + status pills ---- */
|
||||
table.actions{border-collapse:collapse; width:100%; font-size:12.5px;}
|
||||
table.actions th{background:var(--text); color:var(--bg); padding:8px 10px; text-align:left; font-weight:600;}
|
||||
table.actions td{padding:9px 10px; border-bottom:1px solid var(--border); vertical-align:top; color:var(--text);}
|
||||
table.actions tr:nth-child(even) td{background:var(--surface-2);}
|
||||
.status{display:inline-block; font-size:10.5px; font-weight:700; padding:2px 10px; border-radius:999px; white-space:nowrap;}
|
||||
.status.info{background:var(--info); color:var(--info-fg);}
|
||||
.status.warn{background:var(--warning); color:var(--warning-fg);}
|
||||
.status.danger{background:var(--danger); color:var(--danger-fg);}
|
||||
.status.success{background:var(--success); color:var(--success-fg);}
|
||||
.status.muted{background:var(--muted); color:var(--surface);}
|
||||
|
||||
/* ---- Note callouts (4 semantic tones) ---- */
|
||||
.note{border-radius:var(--radius-sm); padding:12px 16px; font-size:13px; border-left:4px solid; margin-bottom:10px;}
|
||||
.note.info{background:color-mix(in srgb, var(--info) 10%, var(--surface)); border-color:var(--info);}
|
||||
.note.warn{background:color-mix(in srgb, var(--warning) 12%, var(--surface)); border-color:var(--warning);}
|
||||
.note.danger{background:color-mix(in srgb, var(--danger) 10%, var(--surface)); border-color:var(--danger);}
|
||||
.note.success{background:color-mix(in srgb, var(--success) 10%, var(--surface)); border-color:var(--success);}
|
||||
.note b{color:var(--text);}
|
||||
|
||||
/* ---- Colvis / conic-gradient ring (space/percentage allocation) ---- */
|
||||
.colvis{display:flex; gap:16px; flex-wrap:wrap; align-items:stretch;}
|
||||
.colvis .colcard{flex:1; min-width:230px; background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); padding:16px; text-align:center;}
|
||||
.colvis .ring{
|
||||
width:110px; height:110px; border-radius:50%; margin:0 auto 10px; position:relative;
|
||||
display:flex; align-items:center; justify-content:center; font-weight:700; color:var(--text); font-size:12.5px;
|
||||
}
|
||||
.ring-main{background:conic-gradient(var(--brand) 0 35%, var(--info) 35% 65%, var(--warning) 65% 100%);}
|
||||
.ring-main::after{content:""; position:absolute; width:68px; height:68px; background:var(--surface); border-radius:50%;}
|
||||
.colvis .ring span{position:relative; z-index:2;}
|
||||
.legend{display:flex; flex-wrap:wrap; gap:8px; justify-content:center; font-size:11px; margin-top:6px; color:var(--muted);}
|
||||
.legend .dot{display:inline-block; width:9px; height:9px; border-radius:50%; margin-right:4px;}
|
||||
|
||||
/* ---- Footer ---- */
|
||||
footer{padding:22px 6vw; color:var(--muted); font-size:11.5px; text-align:center; border-top:1px solid var(--border); margin-top:20px;}
|
||||
|
||||
/* ==========================================================================
|
||||
PRINT — always light, no chrome, avoid awkward breaks.
|
||||
PDF generation at publish time should render this media state.
|
||||
========================================================================== */
|
||||
@media print{
|
||||
.theme-toggle, nav.toc{display:none !important;}
|
||||
body{background:#FAF8F4 !important; color:#272326 !important;}
|
||||
header{background:#79640E !important; -webkit-print-color-adjust:exact; print-color-adjust:exact;}
|
||||
.card, .quadcard, .layerbox, .filepill, table.unitgrid, table.actions, .note, .colcard, .spec{
|
||||
break-inside:avoid;
|
||||
}
|
||||
section{break-inside:avoid-page;}
|
||||
a{color:inherit; text-decoration:none;}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
# internal-meeting-brief — section structure
|
||||
|
||||
Derived from the first real run (2026-09-21, APTA 2026 Booth 09-16 recap) and made
|
||||
explicit here so every future brief has the same skeleton instead of reinventing its
|
||||
structure per meeting. This is the contract — read it before generating a brief.
|
||||
|
||||
## Always present (fixed, in this order)
|
||||
|
||||
1. **Header** — title (meeting subject, not the raw Plaud filename), meeting date +
|
||||
duration, prepared date, source (transcript / manual notes), mode (Agenda/Recap).
|
||||
2. **Attendees** — one card per attendee. Internal MPM people resolved by name where
|
||||
possible. If Plaud's speaker diarization didn't resolve a name, render the literal
|
||||
transcript label ("Speaker 1," "Speaker 3") — never guess a name. Follow with a
|
||||
`note.info` flagging any unresolved speakers explicitly, so it's visible, not buried.
|
||||
3. **Topics Covered** — a `cards` grid, one card per distinct subject actually
|
||||
discussed. Card count and grid density (c2/c3/c4) follow the number of real topics —
|
||||
don't pad to hit a round number, don't force unrelated topics into one card to save
|
||||
space.
|
||||
4. **Key Decisions** (recap) or **Discussion Plan** (agenda) — decisions actually made
|
||||
(recap) or points to be raised (agenda). Use `quad` when there are 3-5 distinct,
|
||||
roughly parallel decisions; use plain prose or a `note` when there's only one or two,
|
||||
since forcing a single decision into a quad grid wastes space and implies false
|
||||
parallelism.
|
||||
5. **Open Items** — an `actions` table with status pills. Every action item needs an
|
||||
owner field even if the honest value is "Unassigned" — never leave the column blank,
|
||||
since a blank reads as "forgotten to track" rather than "no owner yet."
|
||||
6. **Notes & Flags** — one `note` per genuinely separate concern: data-source
|
||||
limitations encountered (a failed pull, a missing transcript), naming/identity
|
||||
uncertainty, anything that needed a judgment call worth surfacing rather than
|
||||
silently resolving. This section exists specifically so the never-fabricate rule has
|
||||
somewhere to put its flags, visibly, not as a footnote.
|
||||
|
||||
## Present only when content genuinely calls for it (dynamic)
|
||||
|
||||
A dynamic section is content that has a **specific internal structure** the fixed
|
||||
sections above would flatten into an unreadable wall of text or an over-long table row.
|
||||
Add one only when the meeting's actual content has this shape — never as decoration,
|
||||
never because a template "should" have variety.
|
||||
|
||||
| If the content is... | Use... | Real example |
|
||||
|---|---|---|
|
||||
| A sequential, ordered process (3+ steps, each depending on the last) | `flow` | Anti-vandalism 3-tier escalation (09-16 meeting) |
|
||||
| A named component/system with distinct sub-parts worth breaking out | `layerbox` + `filepill` | (not yet exercised on a real internal-meeting-brief; seen in development-brief-flavored source material) |
|
||||
| A genuine two-sided contrast (us/them, before/after, keep/avoid) | `compare` | (not yet exercised on internal-meeting-brief; used in vendor-partner-style source material) |
|
||||
| A spec/BOM-style matrix with yes/no or multi-column comparison | `unitgrid` | (not yet exercised) |
|
||||
| A handful of headline numbers worth calling out at a glance | `specbar` | (not yet exercised on internal-meeting-brief) |
|
||||
|
||||
**Rule of thumb:** if you're reaching for a dynamic section and can't point to the
|
||||
specific part of the meeting that has this shape, don't add it. The fixed six sections
|
||||
above should carry ordinary meeting content on their own. Dynamic sections are earned
|
||||
by unusual content structure, not added for visual variety.
|
||||
|
||||
## What's still unproven
|
||||
|
||||
This contract is based on exactly one real run. It hasn't been tested against:
|
||||
- Agenda mode (only recap mode has run for real)
|
||||
- A meeting with genuinely no clear "topics" (a single-subject deep-dive)
|
||||
- A meeting needing more than one dynamic section
|
||||
- A recurring topic's second brief (pre-fill/delta-confirm behavior for attendees,
|
||||
per CW-027 Concept.md, hasn't been exercised — the 09-16 brief was a topic's first
|
||||
run, full interview by necessity)
|
||||
|
||||
Revise this file after the second real run rather than treating it as settled.
|
||||
Reference in new issue
Block a user