shop-build-template
Build a website that will be PUBLISHED TO THE TEMPLATE GALLERY — a finished site other merchants clone as the starting point for their own. Use when working in the templates organisation, when the user says "dựng website mẫu", "build a template", "add one to the gallery", or when a store is about to be promoted with promoteStoreToTemplate. A template is built like any other site (use shop-builder / shop-content-site for the actual building) and then held to ONE EXTRA STANDARD: every visible word must be reachable by an automated copy pass, and its own business identity must appear nowhere a copy pass cannot reach. This skill is that standard, plus the machine check that decides it. Do NOT use for an ordinary merchant store — nothing here applies to a site nobody will clone.
A template is not a special kind of file. It is an ordinary store, built by the normal pipeline, that has been published to a gallery other merchants pick from. Cloning it copies the theme, the pages, the content types, the records and the media, then two cheap model turns re-dress it: one renames the brand, the catalogues and the routes; one rewrites every string.
Build the site itself with
shop-builder(full site) orshop-content-site(non-ecommerce), and readshop-core-rulesfirst. This skill adds only what is different about a site that will be copied by strangers.
Why the standard is stricter than for a normal store
A fault in a merchant's own site is one bad site. A fault in a template is inherited
by every clone made from it, and the clone's gates cannot repair what they cannot
reach. If your business name is baked into an aria-label, every merchant who picks
your template ships a page whose logo announces your shop to a screen reader.
So the bar is: every word a visitor can see must be reachable by an automated rewrite, and nothing about your invented business may sit outside that reach.
The check that decides it — run this, don't guess
graphql_query: query { siteTemplateReadiness(store_id: "<id>") {
ready errors warnings stats } }
It is not a checklist someone ticks. It dry-runs the clone's own residue gate: every copy slot on the site is answered with a sentinel, the answers are applied through the same code path a real clone uses, and the result is searched for your store's own name and slug. Anything still standing is prose no answer can reach.
ready: false lists exactly what is wrong and why it matters. Fix, re-run. Do not
reach for force until you have read every error and decided you accept it — force
publishes a fault into everyone's clone.
stats is worth reading even when ready:
| field | what to look for |
|---|---|
text_asks | the number of distinct strings a clone rewrites. A real site sits in the hundreds; under 20 is refused. |
image_asks | pictures the stock-photo step can swap. |
sections_without_slots | decorative bands are fine; a large share means much of the page is frozen text. |
unreachable_words | must be empty. Each entry names the string and the first file it survived in. |
records | per catalogue, below 3 is refused. |
The seven rules that come out of that check
1. Never write your business name into markup
The build lifts plain-text leaves into {{ section.settings.text_N | default: "…" }}
automatically, and those ARE reachable. What is not reachable:
- text inside an element that also has child markup (an accent
<span>inside the<h1>) — it is left verbatim and becomes frozen; - anything in an attribute other than
alt,title,placeholder,aria-label; - text you hand-wrote into a section with
upsert_theme_fileinstead of letting the draw lift it.
If the readiness check names an unreachable word, move it into a setting
({{ section.settings.x | default: "…" }}) or delete it. Do not solve it by
renaming your invented business to something bland — the next template will hit the
same wall, and a template with a nameless brand teaches the copy turn nothing.
2. Give the invented business a DISTINCTIVE name
Counter-intuitive, but it follows from the check. The residue gate searches for the template's identity strings; a store called "Shop" or "Demo" matches half the page and the gate becomes useless (strings under 4 characters are dropped entirely). Invent a name a real business would have — "Tay Người Việt", "Haven Estates" — and the gate can actually prove your template is clean.
3. The SHAPE you build is the shape every clone gets
A clone renames freely and restructures never. There is no step that adds a catalogue, adds a column, or changes a column's type. So:
- Catalogue count is a product decision. Two catalogues suit a business with products and services. One suits a magazine. Build the shape a whole segment of merchants shares, not the shape one imaginary shop needs.
- Columns must generalise. A furniture merchant cloning a hotel template inherits
the hotel's columns with new labels.
mo_ta(description),gia(price),anh(image) travel anywhere;so_giuong(bed count) does not. - Closed lists are LENGTH-FROZEN. A dropdown drawn with 3 options renders 3 filter chips, and a clone may swap the values but never the count. Pick 3–5 — enough to look real, few enough that any business can fill them.
4. Fill every catalogue with at least 3 records — 6 is better
A band drawn as a six-card grid showing one card reads as broken, and a clone cannot invent records the template never had. Under 3 the check refuses outright.
Write the records as a real business would: no lorem, no "Sản phẩm 1 / Sản phẩm 2". The copy turn is shown the original string and asked for a replacement of roughly the same length — so placeholder text of the wrong shape teaches it the wrong shape.
5. Write copy at the length the layout wants
The clone's copy gate refuses a replacement that grows past ~160% or shrinks below ~35% of the original. That is measured against your string. A heading you wrote as one word forces every clone into one word; a 400-character paragraph where the design wanted 80 will wrap in every clone. Write each string at the length the design was drawn for, and the constraint works for the merchant instead of against them.
6. Every catalogue needs both of its templates
The storefront resolves a content type's pages through templates/<meta.template>.json
and templates/<meta.template>-list.json. A clone renames no theme files, so a
handle with a missing file 404s the detail route on the template and on every clone.
scaffold_content_routes creates both; the readiness check verifies both.
Note that meta.template is a separate field from meta.code. The clone renames the
code and leaves the handle alone — that is deliberate, and it is why nothing breaks.
7. Set the locale, and keep images neutral
localedecides which language the copy turn writes in. Unset, it guesses.- Prefer stock photography that reads as its category rather than as one specific business — a workshop bench, not a signed storefront. A clone can swap images, but a photo with the template's own signage in it survives every swap.
- Use
search_stock_images/upload_imageso images end up on the platform; the clone copies/storage/bytes into the new store, and an external hotlink is left pointing at somebody else's host.
Workflow
- Work inside the templates organisation. Every store there is a candidate;
publishing is still an explicit act, so a half-finished build never reaches the
gallery. Create the store with the normal
createStore, in that org. - Build the site normally —
shop-builderorshop-content-site. Nothing about the building is special. Aim for a site you would be happy to hand a merchant as-is. - Fill the catalogues with real-looking records (rule 4).
- Run the check.
siteTemplateReadiness. Fix every error. Re-run. - Publish.
graphql_mutation: mutation {
promoteStoreToTemplate(
store_id: "<id>",
slug: "khach-san-ven-bien",
display_name: "Khách sạn ven biển",
description: "Trang khách sạn nhỏ: phòng, tiện nghi, đặt phòng.",
industry: "hospitality",
status: "published"
) { id slug status source_rev shape }
}
promoteStoreToTemplate runs the readiness check itself and refuses on the same
errors. It derives the manifest — you never write one — and re-running it
re-derives in place rather than creating a second card. Publish with
status: "draft" to register a template without offering it yet.
- Verify by cloning it once. The only complete proof. Clone into a throwaway store and read the result:
graphql_mutation: mutation {
cloneSiteFromTemplate(template_slug: "<slug>", target_store_id: "<throwaway>",
plan: { replace: true, brand: "Thử Nghiệm", catalogues: [...] })
{ copy_applied routes subst warnings }
}
A clone that returns without error has already passed the residue gate. Open the result and read it as a merchant would.
What the gallery card shows, and why
The card carries the shape — how many catalogues, pages and sections — because
that is what tells a merchant whether the template can serve them at all. Give the
template a display_name and description that say what kind of business it is for,
not what it looks like: "Xưởng nội thất: sản phẩm + dịch vụ đóng theo yêu cầu" helps
someone choose; "Modern minimal layout" does not.
Checklist (each line is a machine error you can pre-empt)
- Store is in the templates organisation.
- Business name is distinctive and ≥4 characters.
-
localeis set. - Theme has its own sections (not inheriting the base).
-
text_asksin the hundreds. -
unreachable_wordsempty. - Every catalogue: a
url_pattern, at least one column, ≥3 records. - Every catalogue:
templates/<handle>.jsonandtemplates/<handle>-list.json. - Closed lists have 3–5 options.
- Images are on-platform and not business-specific.
- Cloned once into a throwaway store and read the result.
