feat/openmerch-sdk · last
update 2026-07-06, all three surfaces run on the SDK
@openmerch/*), building
on the existing types package. Tilpas stays the consumer brand.
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.
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.
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.
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.
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.
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.
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.
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/.
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.
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.