fix(converter): see grids, forms and link labels - 100% design copy captured #110
No reviewers
Labels
No labels
priority
P1
priority
P2
priority
P3
priority
P4
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Toilville/toilville-design!110
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/toilville-source-regressions"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
First step toward 1:1 on #102. Design copy reaching the ForgeBlock tree goes
from 97/100/98/92/80% per page to 100% on all five.
Four converter fixes — three are the same underlying bug
A container's own text was dropped whenever the walker only recursed into child
elements.
1.
_grid_columns()only matchedrepeat(<digit>. Every grid in the designis
repeat(auto-fit, minmax(Npx, 1fr))or an explicit track list(
150px 1fr auto,208px 1fr,1.35fr 1fr) — so none were detected andevery grid flattened. Now resolves
auto-fit/auto-fillfrom the child countand counts explicit tracks paren-aware, so
minmax()'s comma doesn't split.2. A column that IS its own text vanished. Table cells and teaser fields are
<span>2026-07-14</span>with no child elements, so_convert_childrenproduced nothing and the column was skipped. That is how the news titles and the
console's intent/handler/p95 values disappeared the moment grids started being
detected.
3. A text-only
<a>emitted nothing — losing every plain link label ("Allnews →", "Enter the portal →", "Browse RFCs →", "Read the post →"). Now emits a
one-button
actionsrun, which is what it is.4.
inputcould not be produced at all — no heuristic, nodata-blockcase. Adds both, plus bare
<button>. The design's controls carry noname/id/value, so theaction_idis synthesised from the label text andselect options are read from
<option>text.The Contact form now yields 5
inputblocks (2 text, email, a 4-choiceselect, textarea) where it previously yielded nothing.
Two source regressions fixed — both mine
in #101 classified the six full-bleed card links as buttons (
padding+backgroundmatchedis_button()) and the line-based wrapper assumedsingle-line anchors, so
</div>closed inside the still-open<a>,orphaning every card's content. All five sources now parse with zero tag
mismatches (checked with
html.parser).background-color: #ccccccleft on the three Home section headers — the greybars visible in the review renders.
And a silent test hole
test_forgeblock_grammarlocated the sibling Rituals checkout as../Toilville_Ritualsrelative to the source tree. That's wrong inside a gitworktree, so 18 grammar tests silently skipped instead of running. Now asks
git for the real repo root — 26 pass in both the main checkout and a worktree.
Verification
just checkgreen;pytest generator/tests/ tools/wp_theme_ingestor/test_*.py— 267 passedfux-capacity,pardnerandtctdeach gain acolumn_set;peterswimmandtctdgain anactionsrun;fux-capacitygains 5 words it was dropping.What this does not do
Structure is now captured; it is not yet rendered faithfully.
column_setrenders as generic
wp:columnsandinputrenders as plain paragraphs, so thepatterns are still not 1:1. The block work (generalized
card-grid, theterminal, console, ritual demo, and
wish-formon Contact) follows intoilville_rituals, with adapters here after.
https://claude.ai/code/session_01Kbzfscp4w5enUrZxMnDt78
Design copy reaching the ForgeBlock tree goes from 97/100/98/92/80% per page to 100% on all five. Four fixes, three of which are the same underlying bug: a container's own text was dropped whenever the walker only recursed into child ELEMENTS. 1. _grid_columns() only matched `repeat(<digit>`. Every grid in the toilville design is `repeat(auto-fit, minmax(Npx, 1fr))` or an explicit track list (`150px 1fr auto`, `208px 1fr`, `1.35fr 1fr`), so NONE were detected and every grid flattened. Now resolves auto-fit/auto-fill from the child count and counts explicit tracks (paren-aware, so minmax() commas don't split). 2. A column that IS its own text vanished. Table cells and teaser fields are `<span>2026-07-14</span>` with no child elements, so _convert_children produced nothing and the column was skipped — which is how the news titles and the console's intent/handler/p95 values disappeared once grids started being detected at all. 3. A text-only <a> emitted nothing, losing every plain link label ("All news", "Enter the portal", "Browse RFCs", "Read the post"). Now emits a one-button actions run, which is what it is. 4. `input` could not be produced at all — no heuristic, no data-block case. Adds both, plus <button>. The design's controls carry no name/id/value, so the action_id is synthesised from the label text and select options are read from the <option> text. The Contact form now yields 5 input blocks (2 text, email, a 4-choice select, textarea) instead of nothing. Two source regressions fixed, both mine: - The Spelwork Documentation grid was BROKEN markup. My CTA-annotation pass in #101 classified the six full-bleed card links as buttons (padding + background matched) and the line-based wrapper assumed single-line anchors, so the closing div landed inside the still-open anchor, orphaning every card's content. All five sources now parse with zero tag mismatches. - background-color #cccccc left on the three Home section headers — visible as grey bars in the review renders. Also: test_forgeblock_grammar located the sibling Rituals checkout relative to the source tree, which is wrong inside a git worktree — 18 grammar tests silently SKIPPED instead of running. Now asks git for the real repo root. Cross-brand: zero copy lost on any of the five other brands; fux-capacity, pardner and tctd each gain a column_set, peterswimm and tctd gain an actions run. Claude-Session: https://claude.ai/code/session_01Kbzfscp4w5enUrZxMnDt78Reworked: the vocabulary decides, the client renders
Peter's steer — ritual blocks are universal, clients like Gutenberg decide how
to render them — meant the adapter approach in this branch was backwards. It
pattern-matched runs of ForgeBlocks and bypassed the vocabulary entirely.
Rewritten.
Vocabulary (
udg/config/forgeblock.yml)code— a monospaced literal.of: [text],enums: kind: [terminal, source].There was no way to say "terminal transcript";
diagramrenders towp:codebut means Mermaid.
column_set—variant: [plain, cards, table, list]declared, and flippedto
used: true. It was the most important composite in the design whilemarked unused, and a client had no way to ask what kind of grid it had.
Adding one block here is deliberately not a one-line change. The repo demanded,
and this supplies: a renderer in the WP pattern, email and ASCII
clients; text · BBS · SMS · RCS · Apple Business Chat reductions; a mock in
the Ritual Blocks catalog; and the count tripwire bumped 32 → 33.
wcag.py reportagain shows all 33 blocks reducing to every channel.The client boundary is now real
forgeblock_patterntakes aclientand no longer imports one — brand-agnosticin fact, not just intent.
build_surfaceslooks it up per brand, so a brandwithout one gets the universal rendering. Verified: no toilville block leaks
into the other five brands.
render_sequencegives the client first refusal at every depth. A choicethat only held at the top level meant the Contact form — which lives inside a
column — silently reverted to the universal rendering.
forgeblock_toilvilleis now two levels:Converter
<article>joins the container tags — the design puts the grid on thearticle for news rows, so those never registered as a
column_setat all.column_setnow records its variant and its chrome, so the client doesn'tguess. Card fields are read in order (a short run before the heading is the
mono slot; prose after it is the body) — reading purely by type put the tag in
the body and left the real body on the floor.
Guards worth keeping
The card renderer refuses a column carrying inputs, a terminal, a nested grid,
or a long repeated run. Without them the Contact form rendered as a card, and
the ritual demo's step list and signed history were dropped past the first
paragraph. Both were caught by measuring rendered output, not by reading code.
Result
98% of design copy survives to rendered HTML — landing and rituals 100%,
spelwork 98%, news 94%, contact 92% (the remainder is a handful of
contractions). Every page emits toilville blocks. Contact renders the real
wish-formwith Name, Email, Organization, Interested in, and the custommessage label.
just checkgreen, 270 tests pass.Still not wired
toilville/ritual-demoandtoilville/intent-consoleexist (toilville_rituals#368)but nothing emits them. Doing that properly means annotating the design source
so the demo's steps become
statusblocks and its history becomeslineage_entry— the converter can't producelineage_entryat all today. Thatis the vocabulary-first path and it is the next piece, rather than another
shape-matcher.