# Task Plan: Server-Backed Brand Kit Canvas Context ## Goal Persist Brand Kits by authenticated user, bind one optional Brand Kit to each project/canvas, expose a canvas-header selector, and make every Agent Chat turn and newly created thread use the selected kit as authoritative creative context. ## Current Phase Phase 32 ## Phases ### Phase 1: Architecture Discovery - [x] Map Brand Kit frontend state, project persistence, chat/thread creation, and generation context flow - [x] Confirm API generation, storage-driver, schema, and test patterns - **Status:** complete ### Phase 2: API and Domain Design - [x] Update the `.api` contract before generated code - [x] Define user-owned Brand Kit storage and project binding invariants - [x] Define server-side prompt injection behavior for all project chat sessions - **Status:** complete ### Phase 3: Backend Implementation - [x] Generate handlers/types from the validated API specification - [x] Implement memory and PostgreSQL Brand Kit persistence keyed by user ID - [x] Persist and expose project `brandKitId` - [x] Inject selected Brand Kit into Agent Chat and new-thread context - [x] Add backend tests and API documentation - **Status:** complete ### Phase 4: Frontend Integration - [x] Replace local-only Brand Kit persistence with authenticated API persistence - [x] Add canvas-title Brand Kit selector with None and user-owned kits - [x] Add a Brand Kit selector to the Home prompt composer, including an empty create action - [x] Keep Brand Kit editor, defaults, and project binding synchronized - **Status:** complete ### Phase 5: Verification - [x] Run API validation, generation checks, Go tests/build, frontend typecheck/build - [ ] Verify CRUD, user isolation, project binding, chat context, and selector behavior - [ ] Inspect desktop and mobile browser layouts - **Status:** in_progress ### Phase 6: Delivery - [ ] Review scope, generated files, and prohibited product references - [ ] Provide preview URL and concise implementation notes - **Status:** pending ### Phase 7: Device Management Discovery and Design - [x] Trace the existing account dialog, authentication gateway, and device API behavior - [x] Define production-grade loading, empty, error, current-device, and destructive-action states - **Status:** complete ### Phase 8: Device Management Implementation - [ ] Implement the device management view and responsive styling in the existing account surface - [ ] Wire list/remove-all behavior to the authenticated device API without changing unrelated account flows - [ ] Make desktop/mobile session limits configuration-driven and return the effective policy from the device API - [ ] Enforce each configured device-type limit atomically and render last-active timestamps in the browser timezone - [ ] Add focused backend or frontend coverage where the repository supports it - **Status:** in_progress ### Phase 9: Device Management Verification - [ ] Run focused tests, frontend typecheck/build, and Go verification if backend code changes - [ ] Inspect the authenticated desktop and mobile UI in a real browser - **Status:** pending ### Phase 10: Device Management Delivery - [ ] Review scope and final diff - [ ] Provide the working preview URL and concise implementation notes - **Status:** pending ### Phase 11: Visual Annotation Discovery and Design - [x] Map image toolbar/action generation, canvas transforms, and reusable panel conventions - [x] Translate the supplied annotation reference into the repository's interaction and visual language - **Status:** complete ### Phase 12: Visual Annotation Implementation - [ ] Add an image-toolbar entry and visual annotation editing mode - [ ] Support brush, circle, rectangle, and cross marks on the selected image - [ ] Make each mark's AI edit instruction directly editable and removable - [ ] Submit the annotated source through the existing image action generation flow - [ ] Preserve the source node and write generation state/results only to a sibling copy - **Status:** in_progress ### Phase 13: Visual Annotation Verification - [ ] Add focused interaction/serialization coverage where supported - [ ] Run frontend typecheck/build and inspect desktop/mobile behavior in a real browser - **Status:** pending ### Phase 14: Visual Annotation Delivery - [ ] Review scope and final diff without altering unrelated user changes - [ ] Provide the working preview URL and concise implementation notes - **Status:** pending ### Phase 15: CAD Import Discovery and Design - [x] Trace the canvas asset import, upload, persistence, rendering, and export paths - [x] Define the supported DWG/DXF conversion boundary and failure states - **Status:** complete ### Phase 16: CAD Import Implementation - [x] Confirm no backend API change is required for browser-side conversion - [x] Add DWG/DXF selection, conversion, SVG/canvas rendering, and user feedback - [x] Add focused tests and documentation - **Status:** complete ### Phase 17: CAD Import Verification and Delivery - [x] Run relevant frontend tests/builds - [x] Inspect CAD import and canvas behavior in a browser with the exact R12 file - [x] Review scope and deliver concise support/limitation notes - **Status:** complete ### Phase 18: General CAD Compatibility Follow-up - [x] Reproduce the block-heavy DWG timeout and isolate its dominant output path - [x] Replace recursive INSERT/DIMENSION expansion with reusable SVG block definitions and full insert transforms - [x] Cover nested blocks, repeated inserts, MINSERT arrays, malformed references, and recursion cycles - **Status:** complete ### Phase 19: Vector Image Toolbar Follow-up - [x] Recognize uploaded SVG and CAD-derived SVG nodes as vector images - [x] Hide the raster image toolbar and show the compact vector/download toolbar for vector nodes - [x] Preserve original SVG download behavior - **Status:** complete ### Phase 20: Follow-up Verification and Delivery - [x] Verify both user-provided CAD files plus representative DWG/DXF regression fixtures - [x] Run focused tests, typecheck, and production builds - [x] Verify vector toolbar, rendering, download, and persistence in a real browser - **Status:** complete ### Phase 21: Vector Reference Message Rendering - [x] Reproduce CAD-derived SVG references falling back to raw prompt directives - [x] Parse SVG/CAD reference names with nested parentheses into the existing message capsule - [x] Verify SVG directives enter the existing thumbnail/hover/copy component path; run tests and production build - **Status:** complete ### Phase 22: Model-Compatible SVG References - [x] Identify every canvas frontend submission path that can pass an SVG image reference to image generation - [x] Rasterize SVG references to transparent WebP only at the model submission boundary - [x] Cache and replace submitted reference URLs without mutating SVG canvas nodes or display names - [x] Add focused regression coverage and verify prepared model content receives a WebP asset URL - **Status:** complete ### Phase 23: CAD Semantics and Crop Discovery - [x] Map CAD entity/layer/block/text data through conversion, canvas persistence, references, and Agent Chat generation - [x] Map the existing crop overlay, sibling-node placement, vector toolbar, and upload/save paths - [x] Define bounded semantic metadata and crop-output contracts for large drawings - **Status:** complete ### Phase 24: CAD Semantics and Crop Implementation - [x] Preserve bounded CAD semantics during DWG/DXF conversion and canvas persistence - [x] Attach CAD semantic context to model-bound references without exposing it in user-visible chat text - [x] Add vector crop action that preserves the source and creates a cropped sibling asset - [x] Make cropped CAD regions attachable and reusable in Agent Chat - **Status:** complete ### Phase 25: CAD Semantics and Crop Verification - [x] Add conversion, persistence, semantic-payload, crop, and source-preservation regression coverage - [x] Verify both supplied CAD files, focused Go/frontend tests, typecheck, and production builds - [x] Attempt real-browser crop/reference inspection and record the unavailable browser backend - **Status:** complete ### Phase 26: CAD Semantics and Crop Delivery - [x] Review scope and generated files without altering unrelated changes - [x] Document the semantic contract, crop behavior, and preview URL - **Status:** complete ### Phase 27: Non-Passive Canvas Wheel Regression - [x] Add a deterministic regression test for non-passive element-level wheel listener registration and cleanup - [x] Replace the React synthetic canvas wheel handler with a native `{ passive: false }` listener - [x] Verify zoom behavior, DWG upload flow, focused/full tests, and production build - **Status:** complete ### Phase 28: Workspace Toolbar Crop Action - [x] Add a persistent Crop icon immediately after the Text tool - [x] Enable it only for a selected, ready image/vector node and reuse the existing crop overlay - [x] Verify icon order, disabled/active states, crop entry, tests, and production build - **Status:** complete ### Phase 29: Enlarged Single-Image Crop - [x] Add regression coverage for translating the visible viewport intersection into normalized crop coordinates - [x] Let workspace Crop automatically target the only visible image when none is selected - [x] Anchor the crop selection and controls inside the visible viewport for oversized images - [x] Verify enlarged-image crop entry, multi-image ambiguity, tests, and production build - **Status:** complete ### Phase 30: Native-Resolution Vector Crop Fidelity - [x] Reproduce the CAD SVG crop stroke/text degradation with a deterministic regression seam - [x] Crop SVGs by rewriting their viewBox and render once at the final WebP resolution - [x] Preserve SVG structure, transparency, CAD semantics, source immutability, and raster crop behavior - [x] Verify crop output dimensions/display sizing, focused tests, production builds, and browser behavior - **Status:** complete ### Phase 31: Canvas Performance and LeaferJS Evaluation - [x] Build a repeatable browser performance baseline for pan, zoom, selection, and large CAD nodes - [x] Isolate React/DOM reconciliation, SVG rasterization, overlays, persistence, and event-path costs - [x] Map LeaferJS capabilities and integration constraints against the existing canvas domain model - [x] Recommend the smallest migration boundary with measurable acceptance criteria - **Status:** complete ### Phase 32: Leafer Renderer and Vector Texture Cache - [ ] Extend the API/domain/storage contract with an optional vector render-preview URL - [ ] Generate and upload transparent WebP previews while preserving original SVG/CAD source and semantics - [ ] Add a LeaferJS visual renderer adapter for image and manual-frame nodes behind the existing canvas domain - [ ] Move wheel/pan and node drag hot paths to imperative engine/DOM updates with commit-on-end state synchronization - [ ] Preserve crop, export, selection, context menus, overlays, Agent Chat references, and persistence behavior - [ ] Add focused regression/performance coverage and run full frontend/backend/build/browser verification - **Status:** in_progress ## Invariants - Brand Kits are owned and queried only through the authenticated user ID from request context. - A project may reference zero or one Brand Kit owned by the same user. - Selecting None clears the binding without deleting the Brand Kit. - Server-side Agent Chat context is derived from the persisted project binding, never trusted from a client-supplied prompt. - Home may request a Brand Kit ID only from the authenticated user's list; the server revalidates ownership before binding it. - Deleting a Brand Kit clears project bindings to it. - Device sessions are always scoped from the authenticated server-side user identity. - Bulk device removal preserves the current session unless the product contract explicitly requires otherwise. - New access and refresh tokens carry an opaque random session ID; the server validates that session before accepting the bearer token. - Legacy access tokens without a session ID are migrated once from a stable token digest after user validation, preserving existing signed-in users. - Revoked and expired sessions never authenticate or appear in the active-device list. - Device metadata is derived from request headers for display only and never used as an authorization signal. - Device timestamps cross the API boundary as RFC3339 UTC instants and are localized only in the browser's timezone. - Visual annotation actions never mutate the source image node; their copied result node is the only generation target. - Desktop and mobile web-session limits come from server configuration; the API returns the effective values and the UI never hard-codes them. - Imported CAD files are read without mutation; only a sanitized derived SVG is uploaded and rendered, with explicit size, timeout, empty-drawing, and conversion errors. - Repeated and nested CAD block references are represented once and instanced with SVG references; conversion cost must scale with unique block geometry, not insert count. - SVG-backed image nodes use vector-only actions and download their source SVG rather than exposing raster editing actions. - User-message image directives render through one attachment capsule/preview path regardless of raster or SVG source format. - SVG remains the persisted canvas source; transparent WebP derivatives exist only for model-compatible image inputs. - CAD source nodes are immutable during crop; every crop is a sibling asset positioned beside the source. - CAD semantic context is bounded, persisted with the relevant source/crop, and sent as model context without becoming visible user-message copy. ## Errors Encountered | Error | Attempt | Resolution | |-------|---------|------------| | zsh rejected an unmatched `HomePage/*.css` glob | 1 | Replaced the glob with explicit files discovered via `rg --files`. | | Assumed `CanvasWorkspace/index.css`, which does not exist | 1 | Locate the workspace stylesheet from its imports before reading it. | | `rg` treated a pattern beginning with `-- name:` as a flag | 1 | Use `rg -- ` when searching SQLC query annotations. | | Assumed `AuthProvider.tsx` instead of the component directory entry | 1 | Locate the file with `rg --files` before any further auth-provider reads. | | PostgreSQL package test failed after adding project mapping because text conversion helpers were not yet defined | 1 | Add nullable `pgtype.Text` conversion helpers and rerun the package tests. | | One patch to remove a temporary `err = nil` line used the wrong surrounding field name | 1 | Reapplied the patch against the exact current lines; no code change was lost. | | Workspace import patch assumed a nonexistent `BrandMark` import | 1 | Located the actual import block and applied the selector change against current source. | | First i18n patch used an inexact Chinese source string | 1 | Re-read the locale file and patched the exact key/value context. | | Existing account-device test called removal without an authenticated session context | 1 | Update the test to resolve the issued bearer identity and assert the new current-session contract. | | Preview backend rejected `/dev/stdin` because go-zero infers config format from the filename extension | 1 | Use a `.yaml` symlink to stdin so the transformed preview config retains a recognized extension without creating a repository config file. | | A second Next development server refused to start because the existing repo-wide dev lock is active | 1 | Use the already-built Next production server on the alternate preview port. | | BSD `sed` did not support the GNU `0,/pattern/` address used for the preview port substitution | 1 | Replace it with a portable anchored substitution; the backend now listens on 8889. | | The in-app browser runtime reported no available browser backends | 1 | Keep the verified local preview running and report the browser-only visual inspection gap; do not substitute an unrelated browser surface against the browser skill rules. | | Escaping the `psql \d` meta-command through Docker produced an invalid command | 1 | Query `information_schema.columns` and `pg_indexes` with regular SQL instead. | | Desktop limit regression test observed two valid desktop sessions when the configured limit was 1 | 1 | Confirmed the limit was presentation-only; enforce it atomically during session creation and reconcile pre-fix sessions during device listing. | | Launching `mcp-zero` from `server/` used a repository-root relative path | 1 | Relaunch with `../.tools/bin/mcp-zero` while keeping generation output rooted in `server/`. | | NPM lookup for a nonexistent `@loaders.gl/dxf` package returned 404 and stopped the chained lookup | 1 | Use independent package searches and validate the chosen converter package before installation. | | Package inspection commands raced the tar extraction and looked under the wrong transient path | 1 | Inspect the extracted tree first, then run dependent file reads sequentially against the discovered path. | | `npm install` inherited a parent workspace containing an unsupported `workspace:*` URL | 1 | Inspect local package-manager metadata and run installation with workspace discovery disabled or use the repository's actual package manager. | | TypeScript rejected `.length` on the CAD library's entity collection | 1 | Use the collection's declared count/iteration API instead of assuming it is an array. | | Next 16 Turbopack emitted the module worker as raw `.ts` instead of executable bundled JavaScript | 1 | Run Next dev/build through its supported webpack mode, which handles `new Worker(new URL(...))`; retain Vite's native worker bundling. | | Used nonexistent Playwright CLI command `network` | 1 | Use the CLI's documented `requests` and `response-body` commands for network inspection. | | First Vite DXF upload triggered dependency pre-bundling and a development-page reload | 1 | Retry after Vite finishes one-time dependency optimization; production worker output is already bundled. | | Real upstream DXF fixture parsed to an empty model space in `@node-projects/acad-ts` | 1 | Validate the library's document assembly in Node, then replace the DXF path or reader entirely if real fixtures remain empty. | | Directory-wide listing of the WeChat file folder stalled on container-managed storage | 1 | Use the exact user-provided path directly; metadata access confirms the file is readable. | | The exact R12 DXF crashed while serializing the first inherited-color POINT | 1 | Resolve non-renderable ByLayer/ByBlock colors through the entity and layer before calling the third-party SVG writer; lock the case with a minimal R12 regression test. | | Next production minification reduced valid DXF geometry to zero-valued entities | 1 | Restore `acad-ts` constructor names from stable export keys before its name-based metadata lookup; keep the normal minified build and cover the mangled-name path in the regression test. | | Reusing one entity writer and slicing its cumulative string exhausted the Node heap on the exact R12 drawing | 1 | Give each entity a short-lived fragment writer so output assembly stays linear in total geometry size. | | Node's strip-only TypeScript test runner rejected constructor parameter properties | 1 | Use explicit class fields and constructor assignments, which preserve the project test runner's supported syntax subset. | | Vector reference regression suite failed on nested English and Chinese CAD filenames | 1 | Expected red phase confirmed: the directive regex stops before the final filename parenthesis; update it to bind the closing delimiter adjacent to the URL separator. | | First post-fix parser assertion still expected no `index` property | 1 | The production parser correctly emits `index: undefined`; align the regression fixture with the stable result shape. | | In-app browser discovery returned no available browser backend | 1 | Followed the browser skill troubleshooting flow and confirmed an empty backend list; retain automated tests/builds and report the interactive verification gap. | | Combined final planning patch targeted progress lines as task-plan context | 1 | Split the update by file and apply each change against its exact current section. | | Local `mcp-zero` stdio initialization returned no JSON-RPC response | 1 | API validation succeeded; use the documented `goctl api go` fallback with the existing `go_zero` style. | | TypeScript did not infer that a crop existed from the derived crop-bounds check | 1 | Guard the scope line with both `crop` and `cropBounds`; Go targeted tests already pass. | | One combined patch contained a malformed hunk boundary | 1 | Reissued the same two-file edit with valid patch boundaries; no source change was lost. | | Node's strip-types test runner could not resolve the new extensionless runtime import | 1 | Keep the shared semantic types as a stripped type-only import and define the stable metadata element ID locally in the worker module. | | Synthetic text-only CAD fixture did not expose a library bounding box | 1 | Add a simple visible line to establish drawing bounds while keeping the text-encoding assertion focused. | | A second planning/error patch initially repeated the malformed hunk separator | 1 | Removed the empty hunk marker and applied the intended fixture/error update cleanly. | | Isolated Next dev preview was rejected by an existing repository-wide dev lock | 1 | Keep the updated backend on 8889 and run the verified production Next build on 5175 with its API proxy pointed at 8889. | | zsh rejected the optional `frontend/.env*` inspection glob because no env file exists | 1 | Search the concrete Next/Vite config and source paths without an unmatched optional glob. | | Combined toolbar patch expected the wrong `onChooseShapeKind` callback name | 1 | Re-read the exact `WorkspaceToolbar` prop block and apply the component and call-site changes against current code. | | First browser wheel probe stayed at 100% because the stage listener was never attached | 1 | The initial effect ran while the project-loading view had no `.canvas-stage`; re-run listener binding when `project.id` mounts the canvas. | | Playwright `run-code` rejected a bare `await page...` snippet despite the abbreviated skill example | 1 | Read command help and pass an `async (page) => { ... }` function as required by the installed CLI. | | The first mocked route handler used `URL`, which is unavailable in the Playwright callback sandbox | 1 | Derive the pathname from `route.request().url()` with bounded string operations; the corrected route exposed the real canvas. | | Initial combined Phase 30 test/findings patch assumed a generic `# Findings` heading | 1 | Re-read the file header and apply the regression test and findings as separate patches against the exact heading. | | Exact-DWG performance fixture served the raw converter intermediate and Chromium rejected anonymous `` tags | 1 | Use the real browser upload path so `normalizeCadSvg` repairs and sanitizes the final SVG before measuring paint/interaction cost. | | Playwright file-input locator matched both canvas upload and Agent Chat upload controls | 1 | Target the first canvas file input explicitly for the real DWG upload probe. | | Playwright request context bypassed the page's mocked asset route and could not resolve `asset.local` | 1 | Fetch the captured normalized SVG inside the page context so the route handler supplies the bytes. | | Combined performance findings patch matched entries in a different order than the current file | 1 | Re-read the exact section and apply the package/result additions against their real positions. | | Direct CDN import used LeaferJS's package ESM build, whose bare `@leafer/core` specifier is not browser-resolvable | 1 | Load the official prebundled browser distribution for the throwaway benchmark. |