Files
wursor/HANDOFF.md
T
2026-04-26 21:33:59 +01:00

22 KiB
Raw Blame History

Originmain — Implementation Handoff

Living document. Updated after each layer is completed or substantially advanced. Reference: Originmain-Implementation-Guide-v1_0.md (v1.0, April 2026)


Layer Status Summary

Layer Name Status Notes
0 Infrastructure & DevOps Deferred Skipped for now per plan
1 Canvas UI Shell Complete ArtboardTree, CodebaseFileTree, CodeDiffPanel
2 Live Rendering Engine Complete postMessage protocol, fiber hook, MF config, LiveArtboard iframe
3 Visual Editing & Diff Engine Complete Diff engine (49 tests), SelectionOverlay, history store
4 Origin Graph & Data Store Complete 3 migrations, RLS, Zod types, query helpers
5 Design Language Runtime Complete New package, JSON Schema, Zod validator, token pipeline
6 AI Completion Layer Complete Gateway, 5 features, prompt caching, Claude Opus 4.7
7 Agent Bridge (MCP) Complete 5 tools, HMAC auth, rate limiter, Cursor + Claude Code adapters
8 Multi-Origin Ingestion Complete OriginIngester interface + 4 connectors
9 Multiplayer & Presence Foundation Room schema, adapter interface, cursor palette — no app integration yet
10 Platform & Extensions Foundation Plugin API, enterprise types (SSO/SCIM/audit), white-label theming

Layer 1 — Canvas UI Shell

Completed: 2026-04-25

What was built

Diff Engine integration (pre-Layer 1)

  • packages/diff-engine/src/schemas.ts — Zod schemas for ComponentSnapshot and ComponentDiff
  • packages/diff-engine/src/engine.ts — Core diff computation, Myers LCS, recursive child diffing
  • packages/diff-engine/src/myers.ts — Myers longest-common-subsequence algorithm
  • packages/diff-engine/src/patch.ts — Unified patch generation
  • packages/diff-engine/src/serialize.ts — Snapshot serialization to text
  • 49 tests, ~88% coverage, all passing

Canvas UI Shell components (NEW — additive only)

  • packages/app/src/components/navigator/ArtboardTree.tsx — Fluent 2 Tree/TreeItem/TreeItemLayout for workspace artboard hierarchy
  • packages/app/src/components/codebase/CodebaseFileTree.tsx@pierre/trees standalone codebase file browser (separate from ArtboardNavigator.tsx)
  • packages/app/src/components/diff/CodeDiffPanel.tsx@pierre/diffs PatchDiff renderer with unified diff patch string input

Critical invariants

  • ArtboardNavigator.tsx must never be reversed or have its @pierre/trees FileTree removed
  • ArtboardTree.tsx and CodebaseFileTree.tsx are separate components
  • All new components are additive

Layer 2 — Live Rendering Engine

Completed: 2026-04-25

Files created

  • packages/renderer/src/protocol.tsHostMessage/RendererMessage unions, isHostEnvelope(), isRendererEnvelope(), createHostEnvelope(), createRendererEnvelope(); source discriminants 'originmain-host' / 'originmain-renderer'
  • packages/renderer/src/fiber-hook.tsbuildFiberHookScript(artboardId) generates injectable script that patches __REACT_DEVTOOLS_GLOBAL_HOOK__.onCommitFiberRoot, walks Fiber tree, posts FIBER_TREE_UPDATE envelopes to window.parent
  • packages/renderer/src/module-federation.tscreateRendererHostConfig() and createRemoteConfig() MF host config helpers with shared React singleton
  • packages/renderer/src/index.ts — exports all of the above
  • packages/app/src/components/canvas/LiveArtboard.tsx — React iframe component; sandbox="allow-scripts allow-same-origin allow-forms"; injects fiber hook on READY; sends SET_DESIGN_TOKENS on token change

Layer 3 — Visual Editing & Diff Engine

Completed: 2026-04-25

Files created

  • packages/app/src/store/history.ts — Zustand store: stacks: Record<string, {past, future}>, pushEdit(), undo(), redo(), canUndo(), canRedo(), clearHistory(). Max 100 entries per artboard.
  • packages/app/src/components/canvas/SelectionOverlay.tsx — Transparent cursor: crosshair layer; hitTestFiber() depth-first Fiber tree search by mouse position + domRect; SelectionHandles corner drag-to-resize; commits PropChange[] via pushEdit()

Layer 4 — Origin Graph & Data Store

Completed: 2026-04-25

Files created

  • packages/origin-graph/migrations/001_initial_schema.sql — 5 enum types, 7 tables (workspaces, artboards, origins, intent_diffs, agent_sessions, design_language_files, team_members), FK indexes, set_updated_at() trigger
  • packages/origin-graph/migrations/002_artboard_ancestry.sql — Recursive CTE materialized view artboard_ancestry(artboard_id, ancestor_id, depth), unique index for CONCURRENTLY refresh, trigger on artboard insert/update
  • packages/origin-graph/migrations/003_rls_policies.sqlis_workspace_member(ws_id UUID) SECURITY DEFINER function, SELECT/INSERT/UPDATE/DELETE policies on all 7 tables
  • packages/origin-graph/src/types.ts — Zod schemas for all 7 tables + insert types
  • packages/origin-graph/src/queries.tsDbClient generic interface + 13 query helpers (no hard Supabase dependency)

Layer 5 — Design Language Runtime

Completed: 2026-04-25

New package: packages/design-language

  • src/schema.tsDesignLanguageFileBodySchema with tokens, components, screens, voice, accessibility subsections
  • src/validator.tsvalidateDesignLanguageFile() returns ValidationSuccess | ValidationFailure; checkComponentConstraints() for real-time per-edit validation
  • src/tokens.tsextractColorTokens(), tokensToCssVars(), dlfToCssVars(), dlfSummary()
  • tsconfig.base.json — path alias added: "@originmain/design-language": ["./packages/design-language/src/index.ts"]

Layer 6 — AI Completion Layer

Completed: 2026-04-25

New package: packages/ai-layer

  • src/client.tsgetClient(): Anthropic, MODEL = 'claude-opus-4-7'
  • src/gateway.tsAIGateway class: 60 RPM rate limiter, 3 retries on 429/529/5xx, cost tracking (computeCost()). Note: thinking: {type: 'adaptive'} pending SDK ≥0.58 upgrade
  • src/prompts/system.tsbuildSystemPrompt({role, dlfJson?}) returns TextBlockParam[]; DLF block has cache_control: {type: 'ephemeral'} as stable system prompt prefix
  • src/completion-zone.tsfillCompletionZone(): 3-retry JSON validation loop, temp 0.3
  • src/diff-summary.tsgenerateDiffSummary(): temp 0.3, 128 tokens max
  • src/artboard-query.tsqueryCrossArtboard(): temp 0.3
  • src/drift-report.tsgenerateDriftReport(): base64 image, temp 0.3
  • src/agent-qa.tsanswerAgentQuestion(): temp 0.7

SDK note

@anthropic-ai/sdk v0.56.0 is installed. Adaptive thinking (thinking: {type: 'adaptive'}) requires ≥0.58 — a TODO comment marks all 5 feature files for upgrade. All features remain fully functional at temperature level.


Layer 7 — Agent Bridge (MCP)

Completed: 2026-04-26

New package: packages/agent-bridge

  • src/protocol.ts — JSON-RPC 2.0 types: JsonRpcRequest, JsonRpcSuccess<T>, JsonRpcError, MCP_ERROR constants (UNAUTHORIZED: -32001, RATE_LIMITED: -32002), AuthRequest/AuthAck handshake, textResult()/jsonResult<T>() helpers
  • src/auth.tsissueWorkspaceToken(workspaceId, agentType) HMAC-SHA256 base64url token; verifyWorkspaceToken(token) with timing-safe comparison, 30-day TTL; uses Node crypto built-in
  • src/rate-limiter.ts — Sliding window: 100 requests/60-min/workspace; in-process Map of timestamp arrays (replace with Redis for multi-instance)
  • src/tools.tsToolContext interface (db + ai adapters), McpTool interface, 5 tools: get_pending_diffs (rate-guarded), get_artboard_context, ask_design_agent, update_diff_status, get_design_language; TOOLS array, TOOL_MAP, getToolList()
  • src/adapters/cursor.tsgenerateCursorConfig(): generates .cursorrules markdown + cursor_settings.json MCP server entry with Bearer auth
  • src/adapters/claude-code.tsgenerateClaudeCodeConfig(): generates CLAUDE.md section + .claude/settings.json MCP server declaration with SSE transport

Notes

  • Added @types/node devDep + "types": ["node"] in tsconfig for crypto/Buffer/process
  • AgentType: 'CURSOR' | 'CLAUDE_CODE' | 'GENERIC'

Layer 8 — Multi-Origin Ingestion

Completed: 2026-04-26

Package: packages/integrations

  • src/types.tsOriginIngester<TPayload> interface: parsePayload(raw): TPayload + ingest(payload): IngestionResult; IngestionResult carries origin: InsertOrigin, artboardTitle, optional renderUrl
  • src/connectors/linear.tslinearIngester: Zod-validates Linear Issue webhook payload; maps to LINEAR_ISSUE origin; prefers attachment URL for renderUrl
  • src/connectors/slack.tsslackIngester: Zod-validates Slack message_posted event; extracts first image attachment; maps to SLACK_MESSAGE origin
  • src/connectors/github.tsgithubIngester: Zod-validates GitHub pull_request webhook (opened/synchronize/reopened/closed); maps to GIT_COMMIT origin with PR metadata
  • src/connectors/intercom.tsintercomIngester: Zod-validates Intercom conversation.user.created webhook; extracts annotated screenshot attachment; maps to URL origin with user context

Design note

Each connector validates untrusted webhook JSON at the boundary with Zod (parsePayload throws ZodError on invalid input). The origin output maps directly to InsertOrigin from @originmain/origin-graph — no workspace_id on origins (that scoping lives on the artboards table via origin_id).


Package Inventory

Package Status Notes
packages/app Active Next.js 15 app; Layers 13 complete
packages/diff-engine Complete 49 tests, ~88% coverage
packages/ui Active Theme + FluentProvider
packages/renderer Complete Layer 2: postMessage protocol, fiber hook, MF config
packages/origin-graph Complete Layer 4: migrations, RLS, Zod types, query helpers
packages/design-language Complete Layer 5: DLF schema, validator, token pipeline
packages/ai-layer Complete Layer 6: gateway, 5 features, prompt library
packages/agent-bridge Complete Layer 7: MCP tools, auth, rate limiter, adapters
packages/integrations Complete Layer 8: OriginIngester + 4 connectors
packages/multiplayer Foundation Layer 9: room schema, MultiplayerAdapter, cursor palette
packages/platform Foundation Layer 10: Plugin API, SSO/SCIM/audit types, theming
packages/e2e Not started Playwright E2E; Phase 1 completion target

Key Technical Decisions

Decision Choice Rationale
Artboard tree Fluent 2 Tree/TreeItem ARIA, Fluent token integration, <100s of nodes
Codebase file tree @pierre/trees Virtualization for 5k+ files, git status, search
Diff renderer @pierre/diffs PatchDiff Shiki syntax highlight, split/unified modes
Canvas state Zustand Lightweight, supports undo/redo middleware
TS imports in Next.js extensionAlias .js → .ts ESM-compatible workspace packages
AI model Claude Opus 4.7 Max intelligence for design engineering tasks
Prompt caching DLF as stable system prompt prefix ~60% input token cost saving per session
MCP auth HMAC-SHA256 workspace token Stateless, 30-day TTL, timing-safe verify
Rate limit In-process sliding window Simple; swap for Redis in multi-instance deploy
Webhook validation Zod at connector boundary ZodError → HTTP 400 before any DB write
exactOptionalPropertyTypes Conditional spread ...(v !== undefined ? {k: v} : {}) Required by strict TS config throughout monorepo

Layer 9 — Multiplayer & Presence (Foundation)

Completed: 2026-04-26 (Phase 3 foundation only — no Liveblocks app integration per guide)

New package: packages/multiplayer

  • src/room-schema.ts — Liveblocks room schema types:
    • UserPresence — cursor {x,y}, activeArtboardId, selectedComponentIds, cursorColor; updated ≤50ms throttle
    • ArtboardStorageObject — CRDT-backed artboard state (viewport, locked, lastModifiedAt)
    • WorkspaceStorageartboards: Record<id, ArtboardStorageObject> + artboardOrder
    • LiveblocksRoomTypes — generic type params for createRoomContext<Presence, Storage, UserMeta, RoomEvent>
    • RoomEvent — broadcast events: ARTBOARD_LOCKED, DIFF_EXPORTED, COMPLETION_ZONE_ACCEPTED
    • workspaceRoomId(workspaceId) — room ID convention: "workspace:<uuid>"
  • src/adapter.tsMultiplayerAdapter interface matching Zustand store API surface (presence reads/writes, artboard storage, subscribe); createLocalAdapter() stub for Phase 1/2
  • src/cursor-colors.tscursorColorForUser(userId) deterministic 12-colour palette assignment

Phase 3 integration note

Actual createClient / createRoomContext calls go in packages/app/src/lib/liveblocks.ts (Phase 3). @liveblocks/client and @liveblocks/react are declared as optional peer deps — not installed yet.


Layer 10 — Platform & Extensions (Foundation)

Completed: 2026-04-26 (Phase 4 foundation — types and contracts only)

New package: packages/platform

  • src/plugin-api.ts — Plugin API:
    • PluginManifest — reverse-DNS id, version, entryUrl, bundleHash, permissions[]
    • PluginPermission — 7 scopes: artboards:read, artboards:write, diffs:read, diffs:export, completion-zones:register, ingesters:register, design-language:read
    • PluginReadAPI / PluginWriteAPI — async APIs exposed to sandboxed plugin via postMessage
    • PluginContextread, write (null unless artboards:write granted), notify()
    • CustomCompletionZoneDefinition — custom zone type with systemPromptAddendum + outputSchema
    • CustomIngesterDefinition — custom connector handleWebhook() interface
    • PluginRegistrylist / install / uninstall / setEnabled per workspace
  • src/enterprise.ts — Enterprise features:
    • SsoConfig — SAML 2.0: idpEntityId, idpSsoUrl, idpCertificate, enforced, emailDomains; providers: okta/azure/google-workspace/onelogin/custom
    • ScimUser / ScimGroup — SCIM 2.0 normalized objects with role mapped to TeamRole
    • AuditLogEntry — 30 audit actions across auth/workspace/artboard/diff/AI/plugin/DLF/agent-bridge
    • buildAuditEntry() — convenience constructor with auto-timestamp
  • src/theming.ts — White-label theming:
    • BrandTokensprimaryColor, secondaryColor, logoUrl, productName, faviconUrl
    • brandVariantsFromHex(hex) — derives 16-shade Fluent 2 BrandVariants from single hex colour via HSL interpolation (shade10=lightest → shade160=darkest)

Known Pending Items

  1. @anthropic-ai/sdk upgrade to ≥0.58 — unlocks thinking: {type: 'adaptive'} in all 5 AI feature files (marked with TODO comments)
  2. Redis-backed rate limiterpackages/agent-bridge/src/rate-limiter.ts is in-process only; needs Redis for multi-instance MCP server deployment
  3. Layer 0 — Infrastructure (Vercel, Supabase, Render, GitHub Actions) deferred per plan
  4. Layer 9 Phase 3 — Wire MultiplayerAdapter into app components; add createClient/createRoomContext in packages/app/src/lib/liveblocks.ts; install @liveblocks/client + @liveblocks/react
  5. Layer 10 Phase 4 — Plugin sandbox runtime (iframe + postMessage bridge); SCIM webhook endpoint; SSO provider registration UI; audit log Supabase table + extension
  6. packages/e2e — Playwright E2E tests not yet created


Product Bug Audit (2026-04-26) — Work In Progress

The following is a second-pass audit of all real product gaps, independent of layer status. These are being worked on in the current session.

Completed — Session 1

  • .env.example fixed: CLERK_PUBLISHABLE_KEYNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, added AGENT_BRIDGE_SECRET, GITHUB_WEBHOOK_SECRET, INTERCOM_WEBHOOK_SECRET
  • project_id added to ArtboardSchema in types.ts and getArtboards query
  • createArtboardMutation exported from useArtboards.ts, used in Canvas.tsx artboard tool
  • useArtboards returns rawArtboards: Artboard[] (full DB rows) alongside canvas-mapped shapes
  • Inspector props tab now shows real metadata_jsonb fields from selected artboard
  • ArtboardNavigator shows real artboard names (not hardcoded FILE_PATHS)
  • Canvas keyboard shortcuts: V/H/A/Z for tools, Escape to cancel
  • All 4 error boundary files created (global, workspaces, workspace, canvas)
  • Design Language upload UI in workspace page
  • Webhook project_id: null placeholder added (awaiting full project param support)
  • PATCH /api/artboards/[id] + DELETE /api/artboards/[id] + origin-graph helpers
  • POST /api/workspace/[id]/tokens — workspace token issuance for IDE integration
  • WorkspaceCard.tsx + ProjectCard.tsx extracted as 'use client' components → fixed server-side exception on breadcrumb click

Completed — Session 2 (2026-04-26)

  • Server-side crash fixed: workspaces/page.tsx and workspace/[wid]/page.tsx confirmed using WorkspaceCard/ProjectCard client components (no onMouseEnter in server components)
  • Inspector fully rewired: removed useDiff/ARTBOARD_SNAPSHOTS hardcoded data; now uses useDiffs (real DB diffs) + useHistory (pending local changes)
  • Export Diff button: Inspector Diff tab shows pending PropChange[] from history store with "Export diff →" button that calls POST /api/diffs
  • renderUrl inline editor: Inspector Props tab has inline input for renderUrl; saves via PATCH /api/artboards/[id]; patchArtboard() added to useArtboards.ts
  • Inspector status bar: now reads liveArtboardIds from canvas store — shows pulsing green dot when live render connected, grey + hint when not
  • Empty canvas state: removed DEMO_ARTBOARDS fallback from useArtboards.ts; Canvas shows dashed hint "Press A to create an artboard" when empty
  • Zone tool drag preview: Canvas draws live dashed rectangle + dimension label during zone drag; cleans up on mouse-up
  • Workspace settings page: GET+PATCH /api/workspace/[id], /workspace/[wid]/settings page + WorkspaceSettingsForm client component (rename, IDE token issuance with full config snippets, danger zone)
  • Settings link in workspace page: gear icon button added next to "New project"

🔧 Remaining — In Order of Priority

CRITICAL — all done

HIGH

  • Replace useDiff.ts with real useDiffs.ts
  • renderUrl edit UI
  • Export Diff button
  • Zone tool canvas drag preview
  • Empty canvas state
  • Workspace settings page
  • Artboard content fallback — Artboard.tsx still shows UUID as a placeholder title when no fiber tree is connected

MEDIUM

  • handleComponentSelected → canvas selectComponent() → Inspector shows selected component's fiber props
  • Graph tab wired to real fiber tree (currently shows hardcoded DashboardCard tree)
  • Navigator files tree selectable + artboard delete/rename actions
  • Artboard drag-to-move (label drag → PATCH /api/artboards/[id] with new x/y in metadata_jsonb)
  • Artboard delete (trash icon → confirm → DELETE /api/artboards/[id])
  • Artboard rename (double-click label → inline edit → PATCH name)
  • Viewport persistence to localStorage
  • Webhook project_id from ?project= query param
  • @anthropic-ai/sdk upgrade to ≥0.58 for thinking: {type: 'adaptive'}

LOW

  • Project settings page
  • Team invitation UI (in workspace settings — POST /api/workspace/[id]/invite exists)
  • Drift Report UI (in Inspector AI section)
  • Cross-Artboard Query UI (in navigator)
  • Vitest test files (diff-engine + origin-graph)
  • Multiplayer basic Liveblocks wiring
  • Plugin system stub page
  • Zone tool: after drag, show prompt input → POST /api/ai/completion-zone with bounds

Key Technical Notes for Next Agent

Artboard.tsx and component selection: The handleComponentSelected callback inside Artboard.tsx receives the clicked FiberNode (from LiveArtboard). It needs to call useCanvas.getState().selectComponent(node.id, node). The Inspector's PropsTab should check selectedComponentId/selectedComponentData and show the fiber node's props when a component is selected (vs. showing artboard metadata when nothing is selected).

Artboard drag-to-move: The Artboard component label area needs onMouseDown → tracks mouse delta → PATCH /api/artboards/[id] with { metadata_jsonb: { ...meta, x: newX, y: newY } }. Use patchArtboard(id, { metadata_jsonb: ... }) from useArtboards.ts then queryClient.invalidateQueries.

Zone tool completion: After the zone drag ends (mouse-up), show a floating prompt input at the zone position. On submit → POST /api/ai/completion-zone with { artboard_id, bounds: {x,y,w,h}, prompt }. Show result inline in the canvas.

@anthropic-ai/sdk upgrade path: All 5 AI feature files in packages/ai-layer/src/ have // TODO: upgrade to adaptive thinking when SDK ≥0.58 comments. After pnpm add @anthropic-ai/sdk@latest in packages/ai-layer, replace the temperature-based calls with thinking: {type: 'adaptive'} on the gateway.complete() calls.

Last updated: 2026-04-26 — Session 2 complete