AI Code2Des — Overview
apps/code2des converts one completed Page Import capture into a versioned native-Figma specification. packages/code2des-core maps captured DOM hierarchy, paint order, geometry, computed styles, SVG, text metrics, image assets, and CSS saturation/grayscale into supported Figma primitives.
The conversion uses no LLM. This keeps output reproducible and makes fidelity failures attributable to capture or deterministic mapping. Unsupported constructs become warnings or unmatched nodes.
flowchart LR
URL[Public URL] --> PIAPI[POST Page Imports]
PIAPI --> PI[Page Import worker]
PI --> CAP[Immutable capture in S3]
CAP --> PLSEL[Plugin selects completed import]
PLSEL --> C2DAPI[POST Code2Des]
C2DAPI --> C2D[Code2Des worker]
C2D --> SPEC[Native-Figma spec in S3]
SPEC --> PL[Guinness Figma plugin]
PL --> FIG[Figma nodes]
The backend owns the two independent PostgreSQL lifecycles. Page Import completion does not enqueue Code2Des. The plugin creates a new Code2Des row from the selected completed import, polls it, materializes the result, and keeps every earlier generation on canvas; the latest root node ID is written back to PostgreSQL.
Code2Des is not Code2WF: Code2Des targets high-fidelity visual reconstruction from a public URL, while Code2WF remains a separate planned low-fidelity repository-to-wireframe feature.
Bulk import and generation
POST /api/v1/organizations/{organization_id}/projects/{project_id}/page-batches accepts up to 500 import or generation requests. PostgreSQL batch and batch_item preserve the request and progress. A scheduled backend dispatcher prepares each child job and saves its SQS payload in the same transaction, then sends one message per page or variation. Expired dispatch leases retry the saved message without creating another child row.
The dev Terraform configuration caps Page Import at 5 concurrent Lambda invocations and Code2Des at 10. Both SQS mappings retain batch_size = 1 and partial-batch failure reporting. Each invocation has a 900-second limit; the whole batch may take longer. Interactive state exploration still happens within one Page Import invocation and remains subject to that limit.
The plugin polls batch progress and prepares results with at most five concurrent requests. It checks fonts across the batch before placing frames sequentially and recording each completed root. Backend processing continues when the plugin closes. Figma placement resumes in the same file and reuses an existing root if the placement acknowledgement was lost. Concurrent jobs retain separate capture and result prefixes.
Deployment and local testing
- Apply backend migration
0012_batches.sql. - Deploy the backend containing the batch API and
POST /internal/page-batches/dispatch. - Apply dev Terraform for the one-minute EventBridge dispatcher, worker concurrency, and CloudWatch alarms. The dispatcher uses the existing
BACKEND_INTERNAL_API_KEY; no new secret is required. - Build and distribute the updated plugin.
For a local backend connected to local PostgreSQL and LocalStack, submit a batch and invoke the dispatcher manually:
curl -X POST http://localhost:8080/internal/page-batches/dispatch \
-H 'X-API-Key: <BACKEND_INTERNAL_API_KEY>'
Repeat the call to dispatch additional work or retry expired leases. The dispatcher claims up to 5 items at a time, processes at most 100 per call, and stops claiming new work after 30 seconds. Queue-delivery leases last 90 seconds and allow three attempts. A failed codebase upload needs a new upload identity before retrying.
CloudWatch alarms cover worker errors, throttles, p95 duration above 8 minutes, queue age above 30 minutes, and DLQ messages. These alarms appear in CloudWatch; no notification destination is configured by this change. If individual browser jobs routinely approach 15 minutes, move the Page Import worker to ECS/Fargate or split state captures into separate jobs.
The dispatcher first checks each worker's configured dead-letter queue (up to 10 messages per queue, with a 10-second SQS budget). It marks matching, still-processing batch jobs failed after validating tenant, job ID, attempt, and nonce, then acknowledges the message after the database transaction succeeds. Completed/failed jobs keep their terminal result; invalid and non-batch messages remain in the DLQ. Dev IAM grants the backend access only to these two DLQs. This does not infer failure from elapsed time. A 500-item batch requires multiple dispatch calls.