Where this came from
On 2026-08-15 we ran the same attempt-then-catalog exercise that produced Phases 27 and 28, but on a screen type deliberately unlike a dashboard: a commerce checkout. The screen was built end to end with the v2.0.0 toolkit, with entirely original content, and taken all the way through the normal loop — evaluate, resolve, re-evaluate, stress.
It finished at 97/100 with a clean stress run. But it only got there because I declared genre: "dashboard" on a checkout page, which is not what the screen is. Without that declaration the score was 77, and the entire 20-point difference came from one cliché tell firing on the page's own prices. That is the finding this phase exists to fix, along with the other problems the build surfaced.
Fourteen gaps were hit live. This phase takes the four that form a coherent story plus the small bugs found on the way. The rest are named as deliberate deferrals at the end.
Slice A — teach the evaluator what a commerce screen is
Fourteen of the fifteen cliché flags on the finished checkout were honest-content complaining that the subtotal, the line prices, the store-credit balance and the discount percentage looked like fabricated data. On a checkout those figures are the design: they drive column widths, decimal alignment, and whether the total reads as the most important number on the screen. This is the same argument that earned dashboard its relaxation in Phase 12, applied to a different screen type.
Add a commerce genre that relaxes the honest-content tell, following the existing RELAXED_BY_GENRE mechanism exactly as dashboard and material already work. Scope it in the documentation the same way dashboard is scoped: it is for transactional screens — cart, checkout, order confirmation, billing history — and it is explicitly not for a pricing marketing page, where invented customer counts and fake savings claims are exactly the slop the tell exists to catch.
The same slice fixes a false positive found on the same screen. The phrase "30-day plant guarantee" was flagged as stock AI copy on the grounds that it reads as a section-number eyebrow, because it begins with a number followed by a hyphen. Ordinary retail copy is full of this shape — 30-day returns, 2-year warranty, 24/7 support — and the tell should not fire on it. Every fix in this slice ships with a negative fixture proving the legitimate phrasing passes and the actual slop still fails.
Because commerce is a new name-like concept, it has to land on the agent-visible surfaces in the same change: the genre gotcha in index.ts, the canvas_evaluate and canvas_set_genre docstrings, docs/GUIDELINES.md, and the lists inside test-discoverability.ts.
Slice B — stop the generator from being overruled by inherited tokens
generate_design_system was called with a seed and the soft personality, and it wrote a complete design language to the canvas. It then reported, under preservedFromDesignSystem, that it had kept several tokens inherited from the workspace layer instead of the ones it had just generated. Two of those preservations were actively wrong.
The first was a colour. The workspace's border token is #2a241a, a dark brown that belongs to framesmith's own interface. Preserving it onto a freshly generated light-green system meant every hairline on the new screen would have been a hard brown line until I overrode it by hand.
The second was worse because it was silent. The generator wanted body to be 16px Inter and label to be 13px Inter with a specific weight and letter-spacing. It preserved the workspace's body and label tokens instead, which carry a size and nothing else. The result was that the $body role still resolved — so nothing errored — but it had quietly lost the font family the personality had just chosen. A design that references type roles everywhere, exactly as the guidelines instruct, would have rendered in the fallback stack.
Fix this by merging preserved typography tokens field by field, so a preserved size never discards the generated family, weight, line height or tracking. For colours, either stop preserving an inherited token when it fails contrast against the surfaces the generator just wrote, or report that case loudly enough that an agent will act on it rather than skim past it. Add an explicit way to opt out of preservation entirely, since a caller who has just asked for a whole new design language usually means it.
Slice C — put the component scaffolds on the system they are stamped into
Two stock scaffolds hardcode font sizes that are not on the scale the generator produces. empty-state uses a 14px call-to-action label and initials-avatar uses a 12px monogram, while the generated scale runs 11, 13, 16, 20, 25, 31, 39. The consequence is immediate and self-inflicted: stamping a scaffold puts an off-scale size into the design, which then trips the type-scale ratio check and the "too many font sizes" advisory, and the agent spends a round fixing framesmith's own output.
The same two scaffolds are also fragile under hostile content. Under the long-text perturbation the initials-avatar monogram grew to 36px inside a 32px box and its text escaped the parent entirely, and the empty-state title overflowed its container. Phase 27 hardened the v2 page shells against exactly this; the Phase 28 micro-patterns never got the same pass.
Change every component scaffold to reference the typography roles rather than literal pixel sizes, and give each one the minimum-width floors, wrapping and designed truncation it needs to survive long text and the i18n perturbation. Add a regression test that stamps every scaffold in the library onto a generated design system and asserts two things: no node carries a font size that is absent from the generated scale, and canvas_stress comes back clean.
Slice D — make the advisories honest
Three separate things in this family cost real time during the build and none of them pointed at a genuine defect.
The sibling-padding consistency check cannot be satisfied. Fixing it at one level of the tree simply moves the complaint to another level: it went from the document root to the left column, then to the summary rail, then to the basket card, then to the order-options card, in a loop, as each fix changed which siblings differed. The premise is wrong in two ways. A page's structural bands legitimately differ — a thin utility strip, a taller header and a padded main region are supposed to have different padding — and a card's internal padding is a per-component decision, not something that should match its neighbours. Either scope this check to cases where mismatched padding is actually a defect, or remove it.
The spacing-variety advisory is unreachable on a dense screen. It asks for four to six unique spacing values and still reported nine after I had collapsed my own authored values down to the set 0, 4, 8, 16, 24 and 32. The remaining values live inside the stock scaffolds, which Slice C will partly address, but the threshold itself is too tight for a screen with this much anatomy and it costs fifteen points of the spacing category.
Most importantly, the gate can be pinned by items that are advisory by definition. At one point the evaluation reported zero issues to resolve and fifty-two optional refinements, and the directive still said NOT READY at 93 out of 100. There was no honest move available: every blocking problem was fixed, and the only way to raise the number further was to make the design worse. When nothing blocking remains, the directive should say the design is ready, or advisory-only findings should stop moving the score.
Slice E — the bugs found along the way
Three defects turned up during the build that are unrelated to each other but all cheap to fix and all reproducible.
Setting responsive: "fixed" on the top utility bar caused the renderer to paint a second copy of the page header at the bottom of the artboard whenever the document was taller than its content. Changing that one property to "wrap" made the duplicate disappear.
read_nodes ignores both its nodeId and its fields arguments. Two calls that named a specific subtree and a specific field list each returned the entire document from the root, which makes inspecting a stamped scaffold impossible and forces a workaround.
screenshot with fullPage: true returned a clipped image rather than the whole page, and the only way to see the full design was to raise the artboard height by hand. This one is lower confidence than the other two and may be an interaction with the fixed artboard height rather than a bug in the flag, so it needs to be reproduced before it is fixed.
Proof
The phase is done when the checkout attempt can be rebuilt from the same inputs with no dodges: the correct commerce genre rather than a borrowed dashboard one, no hand-repair of the generated design system, no rewriting of scaffold font sizes after stamping, and no rewording of legitimate retail copy to get past a tell. That rebuild should reach at least 95 in both the light and dark themes with a clean stress run, and the evaluation should carry no advisory that cannot be acted on.
Deliberately not in this phase
A checkout page archetype is the obvious follow-on and is not here. The core loop tells agents never to start from a blank canvas, and for this screen there was nothing to start from — the nearest structure was catalogue. Adding one is craft work rather than code work and deserves its own slice once the evaluator and the scaffolds underneath it are correct.
A product-image placeholder primitive is not here either. A commerce screen is mostly product photography and there is no primitive for it, so the attempt used tinted icon tiles. This belongs with the archetype work.
Reshaping the state-coverage vocabulary is not here. The coverage check identified the basket line list as a data table and demanded the generic empty, loading and error trio. Empty and error are right for a checkout and loading is marginal, but the deeper point is that a checkout's real states are an empty basket, a declined payment, an out-of-stock line and an invalid address. Making coverage flow-aware rather than table-aware is a larger design question than this phase should absorb.
Evidence
The attempt canvases are in the "Checkout attempt" project. All content is original — the reference screen was apparel, this is an invented plant shop with different products, currency and copy, following the rule established at the v2.0.0 release that a reference's own figures and names never get mirrored into our artifacts.
Where this came from
On 2026-08-15 we ran the same attempt-then-catalog exercise that produced Phases 27 and 28, but on a screen type deliberately unlike a dashboard: a commerce checkout. The screen was built end to end with the v2.0.0 toolkit, with entirely original content, and taken all the way through the normal loop — evaluate, resolve, re-evaluate, stress.
It finished at 97/100 with a clean stress run. But it only got there because I declared
genre: "dashboard"on a checkout page, which is not what the screen is. Without that declaration the score was 77, and the entire 20-point difference came from one cliché tell firing on the page's own prices. That is the finding this phase exists to fix, along with the other problems the build surfaced.Fourteen gaps were hit live. This phase takes the four that form a coherent story plus the small bugs found on the way. The rest are named as deliberate deferrals at the end.
Slice A — teach the evaluator what a commerce screen is
Fourteen of the fifteen cliché flags on the finished checkout were
honest-contentcomplaining that the subtotal, the line prices, the store-credit balance and the discount percentage looked like fabricated data. On a checkout those figures are the design: they drive column widths, decimal alignment, and whether the total reads as the most important number on the screen. This is the same argument that earneddashboardits relaxation in Phase 12, applied to a different screen type.Add a
commercegenre that relaxes thehonest-contenttell, following the existingRELAXED_BY_GENREmechanism exactly asdashboardandmaterialalready work. Scope it in the documentation the same waydashboardis scoped: it is for transactional screens — cart, checkout, order confirmation, billing history — and it is explicitly not for a pricing marketing page, where invented customer counts and fake savings claims are exactly the slop the tell exists to catch.The same slice fixes a false positive found on the same screen. The phrase "30-day plant guarantee" was flagged as stock AI copy on the grounds that it reads as a section-number eyebrow, because it begins with a number followed by a hyphen. Ordinary retail copy is full of this shape — 30-day returns, 2-year warranty, 24/7 support — and the tell should not fire on it. Every fix in this slice ships with a negative fixture proving the legitimate phrasing passes and the actual slop still fails.
Because
commerceis a new name-like concept, it has to land on the agent-visible surfaces in the same change: the genre gotcha inindex.ts, thecanvas_evaluateandcanvas_set_genredocstrings,docs/GUIDELINES.md, and the lists insidetest-discoverability.ts.Slice B — stop the generator from being overruled by inherited tokens
generate_design_systemwas called with a seed and thesoftpersonality, and it wrote a complete design language to the canvas. It then reported, underpreservedFromDesignSystem, that it had kept several tokens inherited from the workspace layer instead of the ones it had just generated. Two of those preservations were actively wrong.The first was a colour. The workspace's
bordertoken is#2a241a, a dark brown that belongs to framesmith's own interface. Preserving it onto a freshly generated light-green system meant every hairline on the new screen would have been a hard brown line until I overrode it by hand.The second was worse because it was silent. The generator wanted
bodyto be 16px Inter andlabelto be 13px Inter with a specific weight and letter-spacing. It preserved the workspace'sbodyandlabeltokens instead, which carry a size and nothing else. The result was that the$bodyrole still resolved — so nothing errored — but it had quietly lost the font family the personality had just chosen. A design that references type roles everywhere, exactly as the guidelines instruct, would have rendered in the fallback stack.Fix this by merging preserved typography tokens field by field, so a preserved size never discards the generated family, weight, line height or tracking. For colours, either stop preserving an inherited token when it fails contrast against the surfaces the generator just wrote, or report that case loudly enough that an agent will act on it rather than skim past it. Add an explicit way to opt out of preservation entirely, since a caller who has just asked for a whole new design language usually means it.
Slice C — put the component scaffolds on the system they are stamped into
Two stock scaffolds hardcode font sizes that are not on the scale the generator produces.
empty-stateuses a 14px call-to-action label andinitials-avataruses a 12px monogram, while the generated scale runs 11, 13, 16, 20, 25, 31, 39. The consequence is immediate and self-inflicted: stamping a scaffold puts an off-scale size into the design, which then trips the type-scale ratio check and the "too many font sizes" advisory, and the agent spends a round fixing framesmith's own output.The same two scaffolds are also fragile under hostile content. Under the long-text perturbation the
initials-avatarmonogram grew to 36px inside a 32px box and its text escaped the parent entirely, and theempty-statetitle overflowed its container. Phase 27 hardened the v2 page shells against exactly this; the Phase 28 micro-patterns never got the same pass.Change every component scaffold to reference the typography roles rather than literal pixel sizes, and give each one the minimum-width floors, wrapping and designed truncation it needs to survive long text and the i18n perturbation. Add a regression test that stamps every scaffold in the library onto a generated design system and asserts two things: no node carries a font size that is absent from the generated scale, and
canvas_stresscomes back clean.Slice D — make the advisories honest
Three separate things in this family cost real time during the build and none of them pointed at a genuine defect.
The sibling-padding consistency check cannot be satisfied. Fixing it at one level of the tree simply moves the complaint to another level: it went from the document root to the left column, then to the summary rail, then to the basket card, then to the order-options card, in a loop, as each fix changed which siblings differed. The premise is wrong in two ways. A page's structural bands legitimately differ — a thin utility strip, a taller header and a padded main region are supposed to have different padding — and a card's internal padding is a per-component decision, not something that should match its neighbours. Either scope this check to cases where mismatched padding is actually a defect, or remove it.
The spacing-variety advisory is unreachable on a dense screen. It asks for four to six unique spacing values and still reported nine after I had collapsed my own authored values down to the set 0, 4, 8, 16, 24 and 32. The remaining values live inside the stock scaffolds, which Slice C will partly address, but the threshold itself is too tight for a screen with this much anatomy and it costs fifteen points of the spacing category.
Most importantly, the gate can be pinned by items that are advisory by definition. At one point the evaluation reported zero issues to resolve and fifty-two optional refinements, and the directive still said NOT READY at 93 out of 100. There was no honest move available: every blocking problem was fixed, and the only way to raise the number further was to make the design worse. When nothing blocking remains, the directive should say the design is ready, or advisory-only findings should stop moving the score.
Slice E — the bugs found along the way
Three defects turned up during the build that are unrelated to each other but all cheap to fix and all reproducible.
Setting
responsive: "fixed"on the top utility bar caused the renderer to paint a second copy of the page header at the bottom of the artboard whenever the document was taller than its content. Changing that one property to"wrap"made the duplicate disappear.read_nodesignores both itsnodeIdand itsfieldsarguments. Two calls that named a specific subtree and a specific field list each returned the entire document from the root, which makes inspecting a stamped scaffold impossible and forces a workaround.screenshotwithfullPage: truereturned a clipped image rather than the whole page, and the only way to see the full design was to raise the artboard height by hand. This one is lower confidence than the other two and may be an interaction with the fixed artboard height rather than a bug in the flag, so it needs to be reproduced before it is fixed.Proof
The phase is done when the checkout attempt can be rebuilt from the same inputs with no dodges: the correct
commercegenre rather than a borroweddashboardone, no hand-repair of the generated design system, no rewriting of scaffold font sizes after stamping, and no rewording of legitimate retail copy to get past a tell. That rebuild should reach at least 95 in both the light and dark themes with a clean stress run, and the evaluation should carry no advisory that cannot be acted on.Deliberately not in this phase
A checkout page archetype is the obvious follow-on and is not here. The core loop tells agents never to start from a blank canvas, and for this screen there was nothing to start from — the nearest structure was
catalogue. Adding one is craft work rather than code work and deserves its own slice once the evaluator and the scaffolds underneath it are correct.A product-image placeholder primitive is not here either. A commerce screen is mostly product photography and there is no primitive for it, so the attempt used tinted icon tiles. This belongs with the archetype work.
Reshaping the state-coverage vocabulary is not here. The coverage check identified the basket line list as a data table and demanded the generic empty, loading and error trio. Empty and error are right for a checkout and loading is marginal, but the deeper point is that a checkout's real states are an empty basket, a declined payment, an out-of-stock line and an invalid address. Making coverage flow-aware rather than table-aware is a larger design question than this phase should absorb.
Evidence
The attempt canvases are in the "Checkout attempt" project. All content is original — the reference screen was apparel, this is an invented plant shop with different products, currency and copy, following the rule established at the v2.0.0 release that a reference's own figures and names never get mirrored into our artifacts.