l-takazudo-product-on-sale
Transition a Takazudo Modular product from `mercariStatus: 'incoming'` to live on-sale state. Creates the Mercari listing draft, prompts for the new mercariProductId, removes the `mercariStatus: 'inco...
Takazudo Product On Sale (Stage 2: incoming → on-sale)
Flip a Takazudo Modular product from Coming-Soon (mercariStatus: 'incoming') to live on-sale state
once the Mercari Shop listing is ready. This skill is the Stage 2 partner of
/l-add-takazudo-product (Stage 1). Together they implement the incoming → on-sale two-stage
release pattern that lets the product page go live before the Mercari listing exists.
This skill orchestrates existing skills (/l-add-mercari-item, optionally
/l-sync-mercari-ids). It does not duplicate their content — read the linked skills when you reach
each step.
When to use / when not to use
- Use when an existing Takazudo Modular product is currently
mercariStatus: 'incoming'and the Mercari Shop listing data (description, photos, price) is ready to be drafted and posted. - Do not use for products that are not in
incomingstate — this is a state machine, not a generic editor. See the pre-flight checks below. - Do not use for non-Takazudo brands (e.g., ADDAC, OXI Instruments, Make Noise). Mercari listings for those brands follow a different workflow handled outside this skill.
- Do not use for adding a brand-new product. Use
/l-add-takazudo-product(Stage 1) for that.
Pre-flight checks
Before doing anything, verify all of the following. If any check fails, refuse to proceed and print a clear message explaining which check failed and what the user should do instead.
-
Slug provided — the product slug must be passed as the argument (e.g.,
/l-takazudo-product-on-sale zudo-3u-to-1u). If missing, ask the user once for the slug. -
Master-data entry exists —
src/data/product-master-data.mjscontains an entry whoseslugmatches. If not, refuse with: “No product with slug<slug>found in master data. Use/l-add-takazudo-productfirst to create it.” -
Status is
'incoming'— that entry hasmercariStatus: 'incoming'. If absent or any other value, refuse with: “Product<slug>is not inincomingstate — this skill only flipsincoming → on-sale. Wrong workflow.” -
Detail MDX exists —
src/mdx/products/<slug>-intro.mdxexists. If missing, refuse with: “Product detail MDXsrc/mdx/products/<slug>-intro.mdxis missing — Stage 1 was incomplete. Run/l-add-takazudo-productto finish it before listing on Mercari.” -
mercari.pngexists —static/images/p/<slug>/mercari.pngexists locally. If missing, refuse with: “mercari.pngfor<slug>is missing. Runpnpm r2:download(to fetch from R2) orpnpm convimgs:upload(to regenerate from/imgs/) before listing.” -
Every referenced image slug is registered in
metadata-db.json— gather every image slug the product references and confirm each one exists as a key inmetadata-db.json. The regression this catches: convimgs ran, the WebP/blurhash/mercari.pngfiles were uploaded to R2, butmetadata-db.jsonwas never regenerated/committed. Local files look fine, R2 looks fine, but the deployed site renders a black-square blurhash placeholder on cards and an empty<ImgsGrid>on the detail page because the build-time DB lookup returns nothing. Pre-flight check 5 alone does not catch this — it only inspectsmercari.png.How to gather slugs:
- From the product detail MDX (
src/mdx/products/<slug>-intro.mdx):imgThumb,heroImgUrl,imgOgpfrom frontmatter (any that are present)- Every entry inside every
<ImgsGrid srcs={[...]}>block
- From master data (
src/data/product-master-data.mjs):- The
imgSrcof the matching entry, with the leading/images/p/prefix stripped
- The
- For multi-variant products, repeat the master-data step for every variant entry that points to this detail page
How to verify:
node -e ' const fs = require("fs"); const db = JSON.parse(fs.readFileSync("metadata-db.json", "utf8")); const slugs = ["zudo-3u-to-1u-view1__og", "zudo-3u-to-1u-view2" /* ... */]; const missing = slugs.filter((s) => !(s in db)); if (missing.length) { console.error("Missing from metadata-db.json:", missing); process.exit(1); } console.log("OK — all", slugs.length, "slugs registered"); 'If any slug is missing, refuse with: “Image slug(s)
<list>referenced insrc/mdx/products/<slug>-intro.mdx(or master dataimgSrc) are not registered inmetadata-db.json. The convimgs/upload pipeline likely ran, butpnpm build:metadatawas skipped or the regeneratedmetadata-db.jsonwas not committed. Run/l-metadata-updateto regenerate and ship it via the skip-CI metadata branch, then retry. Do not bypass this check — shipping the on-sale flip without it produces a live page with a black-square blurhash placeholder and an empty<ImgsGrid>.” - From the product detail MDX (
Step-by-step procedure
Step 1 — Generate the Mercari draft
Invoke /l-add-mercari-item and pass the slug. That skill reads the product detail MDX, transforms
the content into Mercari’s format, verifies images, and appends a draft entry to
sub-packages/mercari-viewer/draft.json.
Do not duplicate that skill’s content here — read its SKILL.md when you reach this step.
Step 2 — Open the Mercari viewer
Start the viewer so the user can review and finalize the draft, then post it on the Mercari Shop website/app:
pnpm mercari:dev
Then visit http://zmercari.localhost:23234 and navigate to the Drafts page. As an alternative,
the user can launch the desktop wrapper tauri-mercari-viewer (also serves on the same port).
Step 3 — Wait for user confirmation
The Mercari listing must be created manually by the user on Mercari Shop’s site/app — this skill does not (and cannot) automate Mercari posting. Pause and wait for explicit confirmation that the listing is live before proceeding to Step 4. Do not assume; ask.
Step 4 — Prompt for mercariProductId
Once the user confirms the Mercari listing is posted, ask for the new product ID. Validate the input:
- Must be a non-empty string
- Must look like a Mercari product ID — alphanumeric, roughly 22 characters
If the input fails validation, ask again rather than guessing.
Step 5 — Edit master data
Open src/data/product-master-data.mjs, locate the entry for the slug, and apply two edits using
the Edit tool (do not rewrite the file):
- Set
mercariProductId: '<the-id>'— replace the emptymercariProductId: ''from Stage 1 with the new ID. - Delete the
mercariStatus: 'incoming'line entirely.
⚠️ DELETE THE LINE — DO NOT SET
nullOR'available'. ⚠️The master-data convention is clean removal: when a product is live on-sale, the
mercariStatusfield is absent, notnull, not'available', not any other sentinel. This keeps git diffs minimal and matches every other on-sale product in the file. Settingnullor any string value is wrong for this transition — remove the whole line.
Right vs. wrong — concrete examples
✅ Right — line removed entirely; only mercariProductId is set:
slug: 'zudo-3u-to-1u',
...
- mercariProductId: '',
- mercariStatus: 'incoming',
+ mercariProductId: 'm12345abcdef67890ghijk',
spec: { width: '26HP' },
❌ Wrong — leaves mercariStatus behind in any form:
- mercariStatus: 'incoming',
+ mercariStatus: null, // WRONG — set to null
+ mercariStatus: 'available', // WRONG — invented value (not in MercariStatus type)
+ mercariStatus: '', // WRONG — empty string
Only the ✅ pattern is correct. Anything else fails the contract.
After editing, sanity-check that the file is still valid JavaScript:
node --input-type=module -e "import('./src/data/product-master-data.mjs').then(() => console.log('OK'))"
Step 6 — Optional: sync Mercari IDs from CSV
If the user just downloaded a fresh Mercari Shops CSV, also run /l-sync-mercari-ids to backfill
any other products whose mercariProductId is still empty. This is optional and only worth doing
when the CSV is fresh; skip if the user has nothing to sync.
Verification
Before reporting Stage 2 complete:
pnpm check # typecheck + lint + format + MDX validation
pnpm dev # smoke test (zmod.localhost:34434)
Manually confirm in the dev server:
/products/<slug>-intro/now renders<AvailableItem>(Buy on Mercari button) — not<IncomingItem>(Coming-Soon badge + notify-me dialog).- View-source shows JSON-LD with
"availability": "https://schema.org/InStock"(notPreOrder). /brands/takazudo/no longer shows theincomingbadge on the product card.
Commit
Recommended commit message (matches the repo’s [data] scope convention):
[data] <slug>: incoming → on sale
The commit should contain only the master-data edit (and, if Step 6 ran, the additional mercariProductId backfills).
Notes
- Removing the line, not setting
null, keeps git diffs clean and matches the convention used by every other on-sale Takazudo product inproduct-master-data.mjs. Re-stating because this is the most common mistake on this skill. - Refuse on non-incoming products — pre-flight check 3 is mandatory. This skill is a state
machine:
incoming → on-sale, nothing else. Never silently re-write a product that is already on-sale, sold, or discontinued. - For non-Takazudo brands, this skill does not apply. Mercari listings for ADDAC, OXI, Make Noise, etc. follow a different (non-two-stage) workflow.
- Manual Mercari posting — this skill stops at draft generation and waits for the user. It does not call Mercari APIs.
Related skills
/l-add-takazudo-product— Stage 1 partner skill. Creates the product withmercariStatus: 'incoming'. This Stage 2 skill closes the loop./l-add-mercari-item— invoked in Step 1 to generate the draft entry insub-packages/mercari-viewer/draft.json./l-sync-mercari-ids— optional Step 6 helper for backfilling IDs from a freshly downloaded Mercari Shops CSV.