Building a deck on a corporate template (a Google Slides export whose design lives in full-bleed background pictures) produced a badly broken file. Investigating it turned up six separate defects, five of which fail silently — the caller gets a success response and no warning.
Reproduced against both the customer template and the two templates this repo ships.
1. two_column addresses placeholders by idx, and loses content when the numbering differs
_build_two_column_slide assumes PowerPoint's built-in numbering — 1/2 for Two Content, 1–4 for Comparison. That is a convention a template need not follow. The template in question numbered its two cards 4 and 2, left and right in that order, and its comparison layout 4, 13, 14, 15.
Result: the right column was written into the left card, the left column and both headings were dropped, three placeholders were left showing "Click to add text" — and nothing was reported. Both spellings were affected; the no-headings path lost a column too.
2. No warning on any of it
AGENTS.md is explicit: "A branch that skips, substitutes or degrades something the caller asked for reports it… never the log alone." This path dropped two headings and a whole column and returned zero warnings.
3. Autofit is measured against a hardcoded 18pt
_fit_text always passed DEFAULT_BODY_FONT_SIZE to estimate_text_fill. Both shipped templates set 28pt in the master's <p:bodyStyle>, so every estimate was low by the square of the ratio (~2.4×). Text needing 1.9× its placeholder measured as 0.86×, no fontScale was written, and PowerPoint renders a bare <a:normAutofit/> at full size until someone clicks into the box — so the text ran off the bottom of the slide. The overflow warning is derived from the same number, so it never fired either.
This affects every deck built on the shipped templates, not just the customer one.
4. Four content placeholders is not enough to mean "Comparison"
classify_layout() claimed ROLE_COMPARISON for any layout with four content placeholders. The customer template spends four on a layout that is three cards side by side plus a caption bar, so it took the comparison role and every two-column slide in the deck landed on three cards.
Related: when a role is unprovided the fallback is the positional index, which is a guess about a template that has already proved unusual. Position 4 there is Title Only — no body placeholder at all — so the slide lost both columns rather than merely looking wrong.
5. Unfilled placeholders are left behind
add_slide() copies every placeholder the layout defines. A layout offering more than the slide filled leaves empty "Click to add text" boxes — the third card, an unused heading strip, a Section Header's body (a section slide carries only a title), a title slide's absent subtitle. Three on one slide here. They do not print, but they are the first thing anyone opening the file to edit sees.
6. Drawn text and table fills ignore the template
Anything the builder draws rather than places in a placeholder is a plain text box, so it inherits <p:defaultTextStyle> — tx1, black — not the body style. On a dark template the KPI figures and timeline detail lines were black on near-black. Chart text is worse: it lives in its own part and inherits nothing, so scatter axis labels were black whatever the deck looked like.
Table colours were pinned to literals (TABLE_HEADER_FILL = 4172C4, Office's old default blue), so a table came out that blue on every template — including one whose own accent is a brand teal. _set_cell_fill() already wrote a theme name as schemeClr; only the default was not one.
Also, separately
closing slides were hardcoded to ROLE_TITLE, so they always used the cover layout and there was no way to point them at a template's own contact layout short of naming it on every slide.
Fixes on claude/friendly-ramanujan-vxejrj.
Building a deck on a corporate template (a Google Slides export whose design lives in full-bleed background pictures) produced a badly broken file. Investigating it turned up six separate defects, five of which fail silently — the caller gets a success response and no warning.
Reproduced against both the customer template and the two templates this repo ships.
1.
two_columnaddresses placeholders byidx, and loses content when the numbering differs_build_two_column_slideassumes PowerPoint's built-in numbering — 1/2 for Two Content, 1–4 for Comparison. That is a convention a template need not follow. The template in question numbered its two cards 4 and 2, left and right in that order, and its comparison layout 4, 13, 14, 15.Result: the right column was written into the left card, the left column and both headings were dropped, three placeholders were left showing "Click to add text" — and nothing was reported. Both spellings were affected; the no-headings path lost a column too.
2. No warning on any of it
AGENTS.md is explicit: "A branch that skips, substitutes or degrades something the caller asked for reports it… never the log alone." This path dropped two headings and a whole column and returned zero warnings.
3. Autofit is measured against a hardcoded 18pt
_fit_textalways passedDEFAULT_BODY_FONT_SIZEtoestimate_text_fill. Both shipped templates set 28pt in the master's<p:bodyStyle>, so every estimate was low by the square of the ratio (~2.4×). Text needing 1.9× its placeholder measured as 0.86×, nofontScalewas written, and PowerPoint renders a bare<a:normAutofit/>at full size until someone clicks into the box — so the text ran off the bottom of the slide. The overflow warning is derived from the same number, so it never fired either.This affects every deck built on the shipped templates, not just the customer one.
4. Four content placeholders is not enough to mean "Comparison"
classify_layout()claimedROLE_COMPARISONfor any layout with four content placeholders. The customer template spends four on a layout that is three cards side by side plus a caption bar, so it took the comparison role and every two-column slide in the deck landed on three cards.Related: when a role is unprovided the fallback is the positional index, which is a guess about a template that has already proved unusual. Position 4 there is Title Only — no body placeholder at all — so the slide lost both columns rather than merely looking wrong.
5. Unfilled placeholders are left behind
add_slide()copies every placeholder the layout defines. A layout offering more than the slide filled leaves empty "Click to add text" boxes — the third card, an unused heading strip, a Section Header's body (asectionslide carries only a title), a title slide's absent subtitle. Three on one slide here. They do not print, but they are the first thing anyone opening the file to edit sees.6. Drawn text and table fills ignore the template
Anything the builder draws rather than places in a placeholder is a plain text box, so it inherits
<p:defaultTextStyle>—tx1, black — not the body style. On a dark template the KPI figures and timeline detail lines were black on near-black. Chart text is worse: it lives in its own part and inherits nothing, so scatter axis labels were black whatever the deck looked like.Table colours were pinned to literals (
TABLE_HEADER_FILL = 4172C4, Office's old default blue), so a table came out that blue on every template — including one whose own accent is a brand teal._set_cell_fill()already wrote a theme name asschemeClr; only the default was not one.Also, separately
closingslides were hardcoded toROLE_TITLE, so they always used the cover layout and there was no way to point them at a template's own contact layout short of naming it on every slide.Fixes on
claude/friendly-ramanujan-vxejrj.