OpenMerch SDK

Editor refactor program · branch feat/openmerch-sdk · last update 2026-07-14, Gate-2 truth verdicts are in — sweatshirt passes on placement truth (authored twin now governs the sold route), hoodie fails honestly (the IR bridge cannot represent its construction yet)

Phases

0 · Git hygiene + branch outdone
1 · Baseline visual QA + feature matrix (3 surfaces)done
2 · SDK architecture spec + adversarial reviewdone
3 · Scaffold @openmerch/* + headless coredone
4 · Port studio surfacedone
5 · Port /design + delete Yjs/collab stackdone
6 · Port /customize buyer surfacedone
7 · Legacy sweep of next-appdone
8 · Final QA: golden E2E + visual diff + docsdone
9 · Extract @openmerch/garment-3d (WebGPU twin)done
10 · Direct-on-garment mm editingdone
11 · Print-process realismdone
12 · Hoodie + sweatshirt truth lanes → Gate-2 verdictdone

Decisions

Updates

2026-07-14 · Gate-2 verdicts: sweatshirt PASS on placement truth, hoodie FAIL with measured evidence — both beads closed

The sweatshirt lane closed as a pass. The compiled twin (regraded pattern, seam deltas at 0.0 mm, six decoration zones, distortion under the cap) was promoted to an authored-pattern canonical donor, and the family's placement truth now points at it on all 54 routes — including the sold blueprint 95 / provider 30 route. Print rects are derived from the compiled panel map by a new deterministic exporter, so front, back and both sleeves carry production-calibrated UV rects instead of display-shell guesses. Provider-expanded neck areas fail closed with explicit blockers, and nothing in checkout or fabrication consumes the flip. Display is untouched: the founder's bar ("the fixed shell must not look worse than the original") holds, the twin's drape is still draft (detached neckband ring, recorded in its realism gate), so every surface keeps the b49 shell — zero model URLs changed. Both adversarial reviews passed after the derived JSONs were regenerated in a clean worktree so no parallel drift rode along.

The hoodie lane closed as an honest fail. The IR bridge resolves seam sides by edge id alone, so the pocket's partial-edge attachment collapses into a full hem seam; the fixture's cuff and waistband joins are tube self-closures the bridge also cannot express. That is a cannot-represent verdict, measured and independently verified — not a quality judgment call. Per the freeze rule the hoodie ships the existing UV-transfer preview lane and placeholder card, and partial-edge seam support in the bridge is the named reopening criterion. Donor gating was hardened so no tee donor can silently stand in for a hoodie again.

2026-07-11 · Print realism ships — nine technique/colorway cells inspected shot-by-shot, preview and commit agree to the pixel

The print-process realism phase is done. The closing QA pass rendered the same artwork through every technique the editor offers — DTG, screen print, DTF transfer, embroidery on both ecru and jet black, plus sublimation — and every frame was inspected by eye against the failure list that matters in production: prints that float off the cloth like stickers, plastisol that lets the weave show through, white ink missing under a print on black, embroidery without the directional sheen real thread has, dye that changes the fabric's surface. None of it happened. DTG on black shows its white underbase; the screen layer hides the weave exactly as an opaque film should; DTF reads glossier and sharper than DTG with its carrier film visible; embroidery keeps its thread-direction highlight even rotated 30°; sublimation dyes the cloth without touching its relief. A second probe held a live drag mid-gesture and compared it against the committed result: the only pixels that changed were the measurement HUD and the selection guides — the garment itself is identical, so what you see while dragging is exactly what you get. Interaction stays fast: 95th-percentile frame cost during a drag is 1.1 ms against the 4 ms budget. Full gate suite green: golden E2E 5/5, 155 material tests, all typechecks and boundaries clean.

2026-07-11 · Fabric materials tell the truth now — measured sidecar v2, renders pixel-verified unchanged

Step 5 of print realism is in. Every fabric the renderer ships (hero tee, mineral wash, open-end jersey, supplier white) now carries an honest machine-readable description of itself: where its capture came from, what conventions its maps use, the physical size of one pattern repeat, how its surface must be mapped, and whether the display uses the captured maps or the renderer's procedural cloth. All the old guesswork died — the unit-guessing tiling heuristic, the "if the name contains patina" sniff, and two hardcoded material-id escape hatches. The review round caught something subtle: the spec's single "surface strategy" field couldn't describe reality, because texture choice and surface mapping turned out to be independent — all four combinations exist in shipped fabrics. Collapsing them would have silently swapped one fabric's captured textures for procedural ones, so the schema got two honest fields instead, and the tech-pack sheet that production partners see now labels a tile size "(nominal)" until a real measured swatch backs it. The whole point was changing nothing visually: the two fabrics that carried the riskiest rewiring came back bit-identical in before/after captures, the other two differ only at invisible micro-grain level, and the full gate suite (155 material tests, golden E2E 5/5) stayed green.

2026-07-11 · Print technique is now a saved design choice — picker in the dock, persisted through undo, reload and export

Step 4 of print realism is live. The technique a design is meant for (DTG, DTF, embroidery) is no longer just a render setting — it lives in the design document itself. The placement dock grew a technique picker that only offers what the garment zone and colorway physically allow; picking embroidery re-renders the print as stitches on the spot, marks the document dirty, saves, and survives reload, undo and redo like any other placement edit. If a saved choice later becomes unavailable (say the colorway flips dark and excludes sublimation) the document keeps the user's intent untouched and the UI shows the resolved fallback with a note — nothing is silently rewritten. Export now downloads a versioned .composition.json alongside the PNG carrying the mm placement frame, technique and print-rect — the manufacturing contract, no renderer internals. The document format bumped to v3 with a hard rule for the future: a document written by a newer app version shows "needs a newer app version" and disables saving everywhere (including the proof route), and the save path is now a single locked transaction so a stale open tab can never overwrite newer work. Two adversarial reviews drove eight fixes before commit — among them that save race, a proof loader that quietly degraded instead of failing, and a render-path crash for documents carrying a technique the zone never declared. Gates: 513 core tests, full golden E2E 5/5 (the placement gate now also proves picker persistence and both export downloads), and live visual QA on the twin.

2026-07-11 · Physically honest ink rendering shipped — every technique looks like its process

Step 3 of print realism is live. Committed prints now render through a real ink model instead of a flat decal: linear-light compositing with a white underbase on dark garments (visible as a physically plausible halo on jet black), per-technique film coverage and roughness, embroidery as analytic 0.4mm stitch rows whose thread direction and specular follow the artwork rotation, and weave that continues under thin ink instead of stopping at the print edge. A 36-shot evidence matrix (5 techniques × light/dark × 4 camera angles) was captured through the production preview page and inspected shot by shot — zero blocking defects; measured pixels match the spec equations by hand. The heavier material compile exposed a hidden timing assumption in the placement E2E gate (it probed before the twin was drag-ready and the camera orbited away) — the gate now waits on the viewer's real ready signal, and the full golden suite is 5/5 again. The tank's receiver-overlay art path was restored and smoke-verified live.

2026-07-11 · Print realism under way — spec binding, color + technique contract landed

The print-process realism spec survived two adversarial reviews plus an independent verification pass and is now binding (docs/specs/openmerch-print-process-realism.md). Steps 1–2 of six are shipped. Color foundation: OKLCH utilities with branded encoded/linear types, the faithful Khronos PBR Neutral curve with an analytic inverse, and one shared lightness source — the renderer, colorways, and stage selection all classify dark/light garments through the same OKLCH-L thresholds now (the old loader disagreed with the app about forest and wash-slate). A live emissive color-checker probe renders 16 swatches through the real WebGPU pipeline and round-trips within deltaE 0.0014 (gate: 0.02). Technique contract v2: every print technique (DTG, screen/plastisol, DTF, embroidery, sublimation) is a measured, provenance-commented variant — cured film height in mm, weave show-through, ink roughness, edge profile, underbase behavior — and a shared resolver picks legal techniques per zone with real garment-darkness context (sublimation is excluded on darks). A characterization test pins the render output byte-identical until the step-3 ink core swaps in; a Proxy-based consumption test proves no contract field is decorative. Step 3 (the physically honest ink render path: linear underbase compositing, physical stitch pitch, embroidery anisotropy with a real thread-direction field, weave continuity across the print edge) is implementing now.

2026-07-11 · Direct-on-garment mm editing shipped — placement gate green

All seven steps landed. Users drag, resize, and rotate the committed print directly on the 3D garment with a live mm HUD; the HUD, the WebGPU decal, and the manufacturing export all derive from one panel-space mm frame (derivePanelSpaceExportFrame + compileAreaPlacement), so what you place is what gets printed — parity is asserted by unit fixtures at 0°/30°/90° and non-uniform scales, and out-of-raster placements are refused before Canvas can silently crop. Committed decals now project with true rotation (the old axis-aligned bounds projection stretched rotated art). QA evidence: a new golden gate (openmerch-placement-parity.test.ts) drags on the twin, reads the dock mm, saves, reloads, and requires exact restore; the placement-preview benchmark holds p95 0.5ms / max 0.6ms across 120 frames (4ms budget). The pass also root-caused the flaky Gate 1: the save indicator's exit animation kept the stale "Saved" label visible for ~200ms after an edit, so the E2E's text poll reloaded before the debounced autosave fired and the back-area edit was lost. Save gating now asserts the raw machine via data-save-status — Gate 1 went 5/5 after three consecutive suite failures before the fix.

2026-07-10 · @openmerch/garment-3d extracted — all surfaces QA green

The ~18k-LOC WebGPU garment twin now lives in @openmerch/garment-3d: viewer, loader, surface picker (panel-space mm), print-surface contracts, technique appearance, and the product-neutral color pipeline. The licensing boundary holds — Printify-derived data (asset URLs, receiver-surface registry, print areas, fabric textures) stays in next-app and is injected through four required loader dependencies on the viewer; a boundary scan in the package typecheck keeps it that way. packages/next-app/src/components/3d/webgpu is deleted and all consumers (/customize, /garment-preview, /design-studio, studio mockup scenes) import the package. Verified: package tests 52/52, scoped app suites 36/36, typecheck + boundary scans clean, golden gates 4/4, and hands-on browser QA of all four surfaces with zero page errors — including the authed studio in both 3D modes ("On the item" and the "In use" live scene). Along the way the pre-push preflight got a real fix: a lint chunk containing only biome-ignored markdown files no longer aborts the push. Next: direct-on-garment mm editing on top of the extracted surface picker.

2026-07-07 · Final QA done — golden gates green, program complete

Phase 8 shipped in five commits. 9e2baba368 finishes the interrupted ColorField repair (hex input keeps a focused draft; 3-digit shorthand expands only on blur/Enter — the studio baseline defect — with 7 regression tests). 742137992 lands the golden E2E merge gates (save→reload parity across both print areas, publish gating, tee + cap add-to-cart with metadata) and deletes the five Yjs-era Playwright suites; 4/4 green on three consecutive runs. 4ea5f9f214 fixes three Major /customize baseline defects (hardcoded “tee” CTA, order-summary sliver, artwork/currency honesty on the cart line). 7aaef82af2 fixes a live studio regression the QA pass caught: fill edits updated the panel but never the canvas, because the resolver didn't know EditorProvider-mounted engines — @openmerch/react now exports useOptionalEngine and the resolver checks it. Final-QA evidence (30 shots + defect-disposition matrix vs the 2026-07-05 baseline) lives in harness/evidence/openmerch-final-qa/REPORT.md; SDK docs landed as README.md in @openmerch/core and @openmerch/react. Every Major+ baseline defect in scope is fixed or architecturally eliminated; open remainder is cosmetic polish + the env-lane studio org-drift.

2026-07-06 · Legacy sweep done — 30k lines of editor-era code deleted

Three commits: 28c4ecb892 rewires the bare useCanvas hook through the canvas-service registry (it read a provider no production surface mounts, so shadow/matting/style controls silently no-oped — shadow toggle now verified live on /design); d526f3c291 drops the dead segmentation hook and the CanvasDO-era JWT draft scope; 74ada30b38 deletes 147 orphaned files (29.3k lines): old agent chat panels, fabric-canvas services, mobile tree, template gallery, layers panels, 46 components/editor orphans, 27 ai-elements files, dead canvas hooks and design config. The set was cross-verified by an independent import-graph audit and a clean-config knip scan, and A/B-tested: the full vitest suite is bit-identical with and without the deleted files (56 pre-existing environment failures, logged for final QA). All three surfaces smoke clean. Deferred: ~160 unused files outside the editor scope (landing pages, explore, ecom, social, dev tools) — dead but not editor legacy; separate cleanup decision.

2026-07-06 · /customize runs on the SDK — all three surfaces ported

Commit 94a12d6f62. The buyer studio's workplane dropped its bespoke snapshot-array undo/redo and now mounts a per-zone CanvasService: object add/delete/duplicate, z-reorder, clear-all, and history all go through the engine managers, with history:update driving both the button state and the debounced commit back to the garment (that last part covers undo-to-empty, where no object events fire). The port surfaced two engine-level findings. One in the app: fabric v6 IText measures ~2x too wide before it's attached to a canvas, so computing a centered position up front mis-placed text — placement now adds first, then centers. One real bug in core: history restore passed a completion callback as loadFromJSON's second argument, which fabric v6 treats as a per-object reviver — the final render never ran, leaving the visible canvas stale after every undo/redo (and permanently blank on undo-to-empty). Completion is the returned promise; fixed at the source with tests moved to the promise contract. Live QA on :3005, zero page errors throughout: add text/shape, ink recolor, font swap, duplicate, reorder, delete, clear-all, undo/redo round-trips (both workplane and garment texture), Front↔Back zone switching with design restore, and the Done handoff — the Art panel picks up the studio artwork with placement controls live.

2026-07-06 · the Yjs/collab stack is gone

Commit cb30fe6eb2 deletes multiplayer end to end. next-app lost src/sync (orchestrator, yjs adapter, awareness, predictive sync, telemetry), the collaboration components/hooks, the proof-snapshot JWT plumbing, the /api/canvasdo routes, and the whole orchestrator/awareness/collaborator slice of the editor store — undo/redo and the history timeline now run purely on the SDK CanvasService registry. shared dropped its yjs mapping modules. In worker-editor the 1,800-line CanvasDOYjs durable object is deleted; the MCP session/actions API it secretly hosted was re-homed verbatim into a dedicated McpSessionStore DO (wrangler migration v8-mcp-session-store), and jwt-auth lost its proof-scope and DO-name path sniffing. yjs, y-protocols, y-websocket, and lib0 are out of every package.json. Verified live: worker boots, MCP session create→action→readback round-trips on :5173, /design renders and edits with zero console errors on :3005. next-app + shared + worker typechecks clean; 155/155 worker tests and 25/25 touched next-app tests green, including new McpSessionStore coverage.

2026-07-06 · studio runs on the SDK, live-verified

The studio surface is the first consumer of the full SDK stack: @openmerch/react is real (EditorProvider + hooks, its own boundary rules and tests), and studio's save path now flows through the DocumentController — DesignDocument in/out, a rolling design_versions row per draft, thumbnails to Cloudflare Images, old payload shapes folded forward on read with their writer deleted. Live parity gate caught two real engine bugs before sign-off: the mount baseline marked every freshly opened document dirty (phantom autosave on open), and undo was off-by-one — the first click restored the state you were already in. Both fixed at the source: history is now an explicit checkpoint model where mount re-baselines and a lone baseline is not undoable. Verified in the browser: open→idle, edit→Unsaved changes→Saving…→Saved, reload persists the design with undo/redo correctly disabled, single-click undo/redo round-trips. 497 core + 3 react tests green.

2026-07-06 · the engine lives in the SDK now

The whole canvas engine — Fabric object model, CanvasService with all its managers, event bridge, snapshot/export, guides — moved out of the app into @openmerch/core. ~40k lines, originals deleted, 168 app files rewritten to import the SDK, Tilpas-branded engine names neutralized. The engine's last app couplings (zustand stores, the dead multiplayer seam, a server action) were severed first in a separate surgical pass. Verified end to end: core's boundary check + 483 unit tests green, app typecheck green, and a live browser run — editor boots, draft creates, a rectangle lands on the canvas and in the layer list with zero page errors (screenshot below). Test debt went down, not up: 26 previously-failing fabric tests now pass under core's test setup.

2026-07-06 · @openmerch/core is real

First SDK code landed. @openmerch/core now holds the versioned document schema (one document owning N print-area subtrees), the save-state machine where "Saved" can only ever mean an acknowledged write (unit-tested), typed redacted error envelopes, and all five provider seams: persistence, asset storage, AI image ops, fonts, POD. Import boundaries are enforced in CI from the first commit — core physically cannot import React, Next, or app code. garment-ir is renamed to @openmerch/garment-ir. Now extracting the ~40k-line canvas engine out of the app: a surgical pass is severing its store/sync couplings in place, then the wholesale move into core.

2026-07-06 · spec reviewed and hardened, scaffolding starts

Adversarial review done: an independent gpt-5.5 pass produced 20 evidence-backed findings; all folded into the spec. Biggest upgrades: a save-state machine where "Saved" only ever means an acknowledged write, versioned document schema with migrations, full undo/history semantics (batching, branch-aware, selection excluded), typed + redacted error envelopes on every provider seam, and a hard ordering rule — the SDK save path must prove save→reload parity before Yjs gets deleted. Now scaffolding @openmerch/* and the headless core.

2026-07-06 · baseline QA complete, big catch on /design

All three surfaces baselined with reports + ~70 screenshots (committed + pushed). Headline finds: /design autosave is silently broken — saves ride the Yjs websocket, the client falls back to an anonymous token before the user session hydrates, and the worker rejects every connection; the UI shows "Saving…" forever and a reload loses your edits. Strongest possible validation of the "delete Yjs, save through an explicit persistence adapter" decision. Also root-caused the editor boot hang: worker-editor was missing its Printify API key (env drift — fixed, boot passes now). /customize has a squeezed order-summary line + hardcoded "tee" CTA; studio's hex input mangles 3-digit colors; /design's crop inputs render garbled digits. Full defect tables live in harness/evidence/openmerch-baseline/.

2026-07-05 · baseline QA running

Found your pnpm dev next-app had died (port 3000 is your MedicalTextbook dev server); tilpas now runs on :3005 against the hosted dev Supabase. Provisioned QA fixture user qa-openmerch@tilpas.test via admin API. Three parallel QA agents are now exercising /customize, /tilpas/studio, and /design end to end with screenshot evidence, while a fourth maps all editor code for the SDK spec.

2026-07-05 · kickoff

Repo cleaned: recovered the uncommitted buyer-pack task, deploy hygiene, and docs into three grouped commits on codex/sota-garment-twin-plan, then branched feat/openmerch-sdk. Code-map agent is sweeping all editor code; baseline QA of the three surfaces starts now.

Screenshots

/customize on the SDK: studio→Art panel handoff
studio on the SDK: Saved after reload parity
/design running on @openmerch/core
/design editor (baseline)
/design image AI tools
/design branch-aware history
studio 3D on-the-item
/customize buyer surface
defect: boot gate dead-end (root-caused)