Fix print CSS bug and fold in real-world findings from the first live invocation

First test run (Oshkosh GO Transit internal kickoff, via the actual
installed skill, not Claude manually walking the SKILL.md) surfaced real,
fixable gaps. All fixed here:

1. Print CSS bug (root cause, found by Bryan): section{break-inside:
   avoid-page} plus whole-table break-inside:avoid caused a 14-page,
   mostly-blank PDF. Fixed: sections can split across pages now; only
   cards/table-rows avoid splitting, with repeating table headers.

2. SO/PO lookup was wrongly scoped out of this brief type by default.
   Wrong -- an internal kickoff about a real sale needs the SO/PO as much
   as any external brief would. Scope is now by topic nature (commercial
   vs. purely internal), not by brief-type name.

3. Contact-info lookup (not just name resolution) is now an explicit
   standing step in attendee capture, starting from a linked SO/PO's own
   Odoo contacts when one exists.

4. Added the MPM Technical Reference System (a 131-file Drive registry:
   brand, platform, display tech, products, engineering, concepts, proof)
   as a standing reference for part-number/product verification --
   previously unknown to this skill.

5. Pulled the real run's improved _topic-state.md schema back into the
   spec: confidence tags (verified/stated/open), a folder-layout header,
   numbered-vs-proposed open items, a Findings/flags section. These
   emerged from real use and are now documented, not one-off.

6. Meeting transcript archival (a Meeting Transcripts/ subfolder) is now
   a standing step, not something that has to be asked for.

7. section-structure.md: flow and specbar are now marked validated (real
   examples: Oshkosh deployment sequence, billing milestones). Added the
   meeting-vs-proposed open-items badge pattern and a standard
   confidentiality footer note for this brief type.

None of this was a failure of judgment in the moment -- these are gaps
between what the file said and what real use needed, now closed.
This commit is contained in:
Bryan Gilliom (via Claude)
2026-09-22 21:34:20 +00:00
parent 2c6fa0da03
commit 2209a19eb7
3 changed files with 168 additions and 35 deletions
+150 -28
View File
@@ -49,9 +49,16 @@ result from one source is not evidence the meeting has no record at all.
Other tools this skill uses: Other tools this skill uses:
- **Odoo** (MCP) — linked Project/tasks, Sales/Purchase Orders, Knowledge Base, - **Odoo** (MCP) — linked Project/tasks, Sales/Purchase Orders, Knowledge Base,
Helpdesk tickets Helpdesk tickets, and contacts (customer/partner/sales-rep records tied to an SO/PO)
- **Google Workspace** (MCP) — Gmail, Google Chat, Drive, Sheets - **Google Workspace** (MCP) — Gmail, Google Chat, Drive, Sheets
- **Google Drive** — the topic's continuity folder (`_topic-state.md` + brief history) - **Google Drive** — the topic's continuity folder (`_topic-state.md` + brief history
+ `Meeting Briefs/` + `Meeting Transcripts/`)
- **MPM Technical Reference System** — a Drive-based product/technical knowledge
registry (131 entity files as of 2026-09-22: brand voice, platform/software,
display technologies, products, engineering, concepts, proof/evidence). Read
`_INDEX.md` first — it's the routing table — then only the specific files a claim
actually needs. This is where part numbers, model codes, and product/technical
claims get verified; it is not this skill's content to maintain, only to reference.
If any of these fail outright (not just "returns nothing," but errors or times out), 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 say so plainly in the generated brief's Notes & Flags section rather than silently
@@ -66,18 +73,49 @@ Drive for an existing topic folder (naming pattern: `<Topic Name> — Meeting Br
before creating a new one. If an Odoo Project already exists for this topic and has an 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. attached Drive folder, use that folder instead of creating a parallel one.
### 2. Read (or create) the continuity manifest ### 2. Read (or create) the continuity manifest — ALWAYS, before anything else
Open `_topic-state.md` in the topic folder. If it doesn't exist, this is the topic's Open `_topic-state.md` in the topic folder. **This is not optional and does not need
first run — create it with the schema below, and expect a fuller interview in the to be asked about — do it automatically, every run, as the first real action.** If it
following steps since there's no history to draw on yet. doesn't exist, this is the topic's first run: create it immediately with the schema
below, then 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, **Schema — refined 2026-09-22 against a real production run** (Oshkosh GO Transit
Plaud recording ID/pattern, last-meeting date, running open-items/decisions ledger, kickoff), which improved on the original design in ways worth keeping permanently:
attendee list, learned spreadsheet task-registry column mapping (if this topic uses one - **Confidence tags** on every factual claim: `verified` (checked against a source
— see step 5), brief-history log (date/type/mode/link), and a "Related documents" document — an SO, a PO, an Odoo record), `stated` (said by someone in the meeting or
table for cross-referencing other MPM document types about the same real-world topic by the person running the brief, not independently confirmed), `open` (unresolved —
(pre-mortems, BAI build docs, etc. — see CW-027 Concept.md's rewrap-vs-harvest including outright conflicts between two sources, e.g. an address that differs
principle; don't merge their content into this ledger, just reference them). between a PO and its matching SO). Do not present a `stated` fact with the same
confidence as a `verified` one.
- **Folder layout** section at the top of the manifest, documenting what's actually in
the topic folder (including subfolders Brief didn't create, like a pre-existing
`Financial/` folder) — self-documenting, so opening the manifest alone orients
someone to the whole folder.
- **Links** table: linked Odoo Project ID, SO, PO, Chat Space, Plaud recording
pattern, relevant Gmail thread(s), and — critically — the actual **people**
resolved from those records (customer contact, reseller/partner contact, MPM sales
rep), each with role, email, phone, and its confidence tag. See step 4.
- **Decisions ledger** and **Open items ledger** as two separate tables. Open items
get sequential numbers (1, 2, 3...) for things actually assigned/committed in the
meeting, and a separate `P`-prefixed series (P1, P2...) for things *suggested*
during the meeting but not yet formally assigned — don't conflate a live suggestion
with a committed action item.
- **Findings / flags** section for meta-observations worth carrying forward that
don't fit the ledgers — e.g. "this topic's Plaud AI summary is unreliable, build
from the raw transcript" or a specific diarization error spotted at a timestamp.
This is where operational lessons about the data sources themselves get recorded,
not just facts about the topic.
- **Related documents** table for cross-referencing other MPM document types about
the same real-world topic (pre-mortems, BAI build docs, a technical reference
registry, etc. — see CW-027 Concept.md's rewrap-vs-harvest principle; don't merge
their content into this ledger, just reference them).
- **Brief history** log (date/type/mode/file link(s) — both HTML and PDF if a PDF
exists).
Append to this file; don't rewrite history. On every subsequent run, read the whole
thing before doing anything else — it's the difference between starting fresh and
starting from zero.
### 3. Determine mode: Agenda or Recap ### 3. Determine mode: Agenda or Recap
Agenda mode (pre-meeting prep) pulls the *previous* meeting's record and any updates Agenda mode (pre-meeting prep) pulls the *previous* meeting's record and any updates
@@ -85,7 +123,7 @@ since. Recap mode (post-meeting) pulls the *current* meeting's record. Ask if no
specified by the request. "Record" — see Prerequisites above — is whatever source specified by the request. "Record" — see Prerequisites above — is whatever source
actually applies for that meeting, not necessarily Plaud. actually applies for that meeting, not necessarily Plaud.
### 4. Capture and normalize attendees ### 4. Capture and normalize attendees — and resolve real contact info, always
- **First run for this topic:** ask who's attending / who attended. - **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 - **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. last time, or any changes?" — not a blank-slate interview.
@@ -95,16 +133,22 @@ actually applies for that meeting, not necessarily Plaud.
- **Normalize into two groups:** - **Normalize into two groups:**
- Internal MPM attendees — record by name for the brief; no further resolution - Internal MPM attendees — record by name for the brief; no further resolution
needed (no external allowlist implications for this brief type). needed (no external allowlist implications for this brief type).
- External attendees (rare for this brief type, but possible) — cross-reference - External attendees — cross-reference against Odoo to resolve real contact info
against Odoo contacts to resolve/confirm email addresses rather than trusting raw (email, phone, title, company), not just a confirmed spelling of a name. **This is
input. a standing step, not something to be asked to do:** if the topic has a linked SO
- **If Plaud's diarization didn't resolve a speaker to a name:** render the literal or PO, pull the Odoo contacts tied to those records first (customer contact,
reseller/partner PM, sales rep) — this is usually faster and more accurate than a
generic contact search, and surfaces exactly who the real counterparts are even
if they weren't in the meeting. If someone's name as heard doesn't match Odoo
cleanly (a misheard surname, a title mismatch), flag it in Findings/flags as
`open` rather than silently picking one.
- **If a speaker's diarization didn't resolve to a name:** render the literal
transcript label ("Speaker 1," "Speaker 3"). Do not guess a name from context, even 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. when a guess feels obvious. Flag unresolved speakers in the Notes & Flags section.
### 5. Pull data, scoped to what this brief type actually needs ### 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 Not a monolithic "pull everything," but scope by what the *topic* actually is, not a
first run, are: fixed list per brief type:
- The meeting record (per mode, above) — the primary source, whichever tool it - The meeting record (per mode, above) — the primary source, whichever tool it
actually lives in actually lives in
- Gmail / Google Chat: updates since the manifest's last-meeting date, scoped by - Gmail / Google Chat: updates since the manifest's last-meeting date, scoped by
@@ -113,10 +157,26 @@ first run, are:
PM-tool adoption is inconsistent; fall back to a spreadsheet task registry if the 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 topic uses one, learned via a one-time interview on first use and stored in the
manifest, never assumed to follow a universal schema) manifest, never assumed to follow a universal schema)
- **Sales Order / Purchase Order — pull whenever the topic is a real customer or
reseller engagement, not just for vendor-partner-brief/status-update-brief.**
Correction from initial design: an *internal* kickoff meeting about a *real sale*
(an internal-meeting-brief, by audience) still needs the SO/PO — that's where the
scope, the customer, the reseller/partner, and the real contacts actually live. The
dividing line is the topic's nature (commercial engagement vs. purely internal
process work like a booth build), not the brief type's name. Search Odoo by
customer/topic name if not already linked in the manifest.
- **Part numbers / product/technical claims — check them against the MPM Technical
Reference System** (a Drive-based registry; read `_INDEX.md` first, then
`_RULES.md` for the confidence-tag contract, then only the specific entity files
the topic needs — e.g. `30-products/model-code-grammar.md` for verifying a model
number's structure, or the relevant `30-products/*` file for a named display
family). Don't guess at whether a model number or part number is right — verify it
here, or flag it `open` if the registry doesn't resolve it (e.g. a variant with no
assigned code yet). This registry is a genuine dependency, not this skill's own
content to maintain — see Dependencies.
Sales/Purchase Orders and Helpdesk ticket review are not typically relevant to this Helpdesk ticket review is still a deliberate, manual exception (see below) — not
brief type (they matter more to vendor-partner-brief and status-update-brief) — don't pulled automatically regardless of topic type.
pull them by default.
### 6. Synthesize into the section structure ### 6. Synthesize into the section structure
Follow `references/section-structure.md` exactly: six fixed sections in order, dynamic Follow `references/section-structure.md` exactly: six fixed sections in order, dynamic
@@ -129,12 +189,33 @@ Single self-contained HTML file. Include `assets/brief-components.css` inline (d
link out to it — the brief must survive as a standalone file). Include: sticky jump-nav, 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 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 `@media print` rules from the shared CSS (forces light, hides nav/toggle, avoids
awkward page breaks). awkward page breaks). Small brief-specific CSS additions beyond the shared stylesheet
(e.g. a status-source badge, a role subtitle under an attendee's name) are fine —
append them, don't fork the canonical rules to get the same effect.
### 8. Archive **Footer confidentiality note — standard for this brief type.** internal-meeting-brief
Save the generated HTML into the topic's Drive folder. Update `_topic-state.md`: append is, by definition, internal content. End the footer with an explicit statement of that
to the brief-history log, update the running open-items/decisions ledger (carry forward (e.g. "Internal. Do not forward to [named external party]." when the topic has one) —
anything still open from this run), update the attendee list, update last-meeting date. validated in the Oshkosh brief, where this genuinely matters: the brief discusses a
reseller/customer deal in more candid terms than anyone would want forwarded to that
customer.
### 8. Archive — the brief, the transcript, and the manifest update
- Save the generated HTML into the topic folder's `Meeting Briefs/` subfolder
(`YYYY-MM-DD <Meeting> - <Mode> Brief.html`). If a PDF was also generated, save it
alongside with the matching name.
- **Save a plain-text copy of the meeting record into a `Meeting Transcripts/`
subfolder** (`YYYY-MM-DD <source name> - Transcript.txt`) — a standing step, not
something to be asked for. This matters beyond convenience: it makes the topic
resilient to the source connector being unavailable later (Plaud specifically has
had real, live connector problems this project — see Resolved limitation below), and
it's the raw material a future brief needs if it has to re-derive something the
original brief didn't capture.
- Update `_topic-state.md`: append to the brief-history log (both file links), update
the decisions and open-items ledgers (carry forward anything still open, close
anything resolved, add new items with the correct sequential/P-prefixed numbering —
see step 2), update the attendee list and any newly-resolved contact info, update
Findings/flags with anything learned this run, update last-meeting date.
### 9. Present ### 9. Present
Hand the file to the person. Don't silently skip presenting it even if archived — the Hand the file to the person. Don't silently skip presenting it even if archived — the
@@ -152,6 +233,47 @@ person needs to actually see and review it, not just trust it happened.
separate, deliberately not built yet. Don't attempt to stand up delivery separate, deliberately not built yet. Don't attempt to stand up delivery
infrastructure as part of running this skill. infrastructure as part of running this skill.
## First real invocation — findings (2026-09-22)
The first time this skill ran from an actual installed invocation (not Claude manually
walking through this file) — a recap brief for an Oshkosh GO Transit internal kickoff.
Real, valuable findings, each fixed in this revision:
1. **Print CSS bug — fixed.** `section{break-inside:avoid-page;}` and whole-table
`break-inside:avoid` in the shared stylesheet's `@media print` block caused a
14-page, mostly-blank PDF: any section taller than the remaining space on a page
got pushed whole to the next page, leaving the gap behind blank. Fixed in
`assets/brief-components.css`: sections can split across pages now; only the small
atomic elements (cards, individual table rows) avoid splitting, with table headers
repeating on each new page. A correctly-generated PDF from the same content came out
6 clean pages.
2. **SO/PO lookup was wrongly scoped out by default — fixed.** Original design said
Sales/Purchase Orders "aren't typically relevant" to this brief type. Wrong: an
internal kickoff about a real sale needs them just as much as an external brief
would. Corrected in step 5 — the deciding factor is whether the topic is a
commercial engagement, not which brief type is generating the document.
3. **Contact-info lookup had to be manually requested — now a standing step.**
Resolving attendee names wasn't enough; real contact info (email, phone, title) for
external counterparts needed to be pulled from Odoo, and doing that well meant
starting from the linked SO/PO's own contacts rather than a generic search. Now
explicit in step 4, not something that needs to be asked for.
4. **Part number / product verification wasn't referenced at all — now is.** The
actual run needed to check a model variant and part number against MPM's product
catalog. The right source turned out to be the MPM Technical Reference System (see
Prerequisites) — added as a standing reference, not previously known to this skill.
5. **The manifest schema got meaningfully better in real use — pulled back into the
spec.** Confidence tags (verified/stated/open), a folder-layout header, split
numbered-vs-proposed open items, and a Findings/flags section for operational
observations (e.g. "this topic's Plaud AI summary is unreliable, build from the raw
transcript") all emerged from the real run and are now part of the documented
schema in step 2, not a one-off improvisation.
6. **Meeting transcript wasn't being archived — now a standing step.** The raw
transcript needed to be saved alongside the brief so future runs don't depend on
the source connector still being reachable. Now explicit in step 8.
None of these were failures of judgment in the moment — they're gaps between what
this file said and what turned out to actually be needed, now closed.
## Resolved limitation (was open 2026-09-21, fixed 2026-09-22) ## Resolved limitation (was open 2026-09-21, fixed 2026-09-22)
Earlier attempts to pull this skill's first real test recording failed for two Earlier attempts to pull this skill's first real test recording failed for two
@@ -228,9 +228,10 @@ footer{padding:22px 6vw; color:var(--muted); font-size:11.5px; text-align:center
.theme-toggle, nav.toc{display:none !important;} .theme-toggle, nav.toc{display:none !important;}
body{background:#FAF8F4 !important; color:#272326 !important;} body{background:#FAF8F4 !important; color:#272326 !important;}
header{background:#79640E !important; -webkit-print-color-adjust:exact; print-color-adjust:exact;} header{background:#79640E !important; -webkit-print-color-adjust:exact; print-color-adjust:exact;}
.card, .quadcard, .layerbox, .filepill, table.unitgrid, table.actions, .note, .colcard, .spec{ .card, .quadcard, .layerbox, .filepill, .note, .colcard, .spec{
break-inside:avoid; break-inside:avoid;
} }
section{break-inside:avoid-page;} table.unitgrid tr, table.actions tr{break-inside:avoid;}
table.unitgrid thead, table.actions thead{display:table-header-group;}
a{color:inherit; text-decoration:none;} a{color:inherit; text-decoration:none;}
} }
@@ -8,10 +8,16 @@ structure per meeting. This is the contract — read it before generating a brie
1. **Header** — title (meeting subject, not the raw Plaud filename), meeting date + 1. **Header** — title (meeting subject, not the raw Plaud filename), meeting date +
duration, prepared date, source (transcript / manual notes), mode (Agenda/Recap). duration, prepared date, source (transcript / manual notes), mode (Agenda/Recap).
Also attach relevant deal-identifying context in the header/subhead when the topic
is a commercial engagement — e.g. the SO/PO numbers and total, as validated in the
Oshkosh brief — so the document is self-identifying without opening the manifest.
2. **Attendees** — one card per attendee. Internal MPM people resolved by name where 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 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 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. `note.info` flagging any unresolved speakers explicitly, so it's visible, not buried.
When external counterparts are named in discussion but weren't present, give them
their own card group ("Named in discussion, not present") with resolved contact
info (see SKILL.md step 4) rather than omitting them or burying them in prose.
3. **Topics Covered** — a `cards` grid, one card per distinct subject actually 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 — 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 don't pad to hit a round number, don't force unrelated topics into one card to save
@@ -23,7 +29,13 @@ structure per meeting. This is the contract — read it before generating a brie
parallelism. parallelism.
5. **Open Items** — an `actions` table with status pills. Every action item needs an 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, 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." since a blank reads as "forgotten to track" rather than "no owner yet." **When the
brief includes action items Claude is suggesting rather than items the meeting
itself raised** (validated pattern from the Oshkosh brief), split them into two
visually distinct tables with a labeled subhead and a badge (e.g. `MEETING` vs.
`PROPOSED`), and say explicitly that proposed items are suggestions needing an
owner to accept them — never blend a suggestion into the same table as a committed
action item where they'd read as equally real.
6. **Notes & Flags** — one `note` per genuinely separate concern: data-source 6. **Notes & Flags** — one `note` per genuinely separate concern: data-source
limitations encountered (a failed pull, a missing transcript), naming/identity limitations encountered (a failed pull, a missing transcript), naming/identity
uncertainty, anything that needed a judgment call worth surfacing rather than uncertainty, anything that needed a judgment call worth surfacing rather than
@@ -37,13 +49,11 @@ sections above would flatten into an unreadable wall of text or an over-long tab
Add one only when the meeting's actual content has this shape — never as decoration, Add one only when the meeting's actual content has this shape — never as decoration,
never because a template "should" have variety. never because a template "should" have variety.
| If the content is... | Use... | Real example | | If sequential, ordered process (3+ steps, each depending on the last) | `flow` | Anti-vandalism 3-tier escalation (09-16 meeting); Oshkosh deployment sequence, client kickoff → survey → mount → install → go-live → acceptance (09-22 meeting) — **validated in real production use** |
|---|---|---|
| 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 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 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 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) | | A handful of headline numbers worth calling out at a glance | `specbar` | Oshkosh billing milestones — three estimate amounts + total, each with its own trigger condition — **validated in real production use** |
**Rule of thumb:** if you're reaching for a dynamic section and can't point to the **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 specific part of the meeting that has this shape, don't add it. The fixed six sections