AI Code2WF — System Workflows
This document describes the planned Code2WF MVP. Page capture happens before this flow through Page Import.
Workflow 1: Trigger Generation
sequenceDiagram
participant P as Figma plugin
participant B as Backend
participant PG as PostgreSQL
participant Q as Code2WF SQS
P->>B: POST pageImportId + Figma fields
B->>PG: Validate completed Page Import
B->>PG: INSERT code2wf (status 0, attempt 1)
B->>PG: Resolve immutable Page Import result
B->>Q: Send result URL/hash/capture hash
B-->>P: 201 processing row
- The plugin submits
pageImportId,figmaFileKey,screenId, and optionalplacementTarget. - The backend verifies Write access and loads the completed, non-deleted Page Import in the same organization/project.
- The backend generates UUIDv7 and inserts the minimal
code2wfrow withstatus = "0"(the existingwf2des_statusenum) andattempt = 1. - It reads and verifies the immutable normalized manifest once and derives the result URL, result hash, and capture hash from those exact bytes as one pin set.
- It sends those exact source values to SQS and returns the row.
The code2wf row keeps only page_import_id; it does not copy source manifests, hashes, names, viewports, or dispatch nonces. Missing or corrupt manifest bytes fail before SQS dispatch; this read does not claim a PostgreSQL/S3 transaction.
Every valid trigger creates a new backend-generated row and worker job. If SQS dispatch fails after insert, the backend changes that row from processing to failed using (id, attempt=1, status="0"). Another trigger creates another row; the failed row is not redispatched.
Workflow 2: Convert the Imported Page
sequenceDiagram
participant Q as Code2WF SQS
participant W as Code2WF worker
participant S as S3
participant B as Backend webhook
participant PG as PostgreSQL
Q->>W: attempt 1 + exact Page Import result
W->>S: Read and hash-check source
W->>W: Deterministic existing-schema node tree
W->>W: Deterministic annotation text nodes
W->>S: Conditional PUT spec, then terminal manifest
W->>B: succeeded + result_manifest_url
B->>PG: CAS id + attempt + processing status
The worker validates tenant identity, Page Import ID, source hash, capture hash, and the S3 prefix before conversion. It does not open the original page or connect to PostgreSQL/DocumentDB.
The structural transform and annotation wording are deterministic. Controls and annotations use existing layout_frame / text nodes; unsupported media uses the existing visible unmatched placeholder. No annotation-only model or node schema is added.
The worker writes the client-facing result artifact first and its terminal manifest last with create-only conditional writes. The result uses the same outer job_id, attempt, tenant, screen, status, and generated_at names as WF2Des, with Code2WF Page Import pins under inputs. The manifest carries row_effects.code2wf; the webhook references it with result_manifest_url, and the backend stores the row effect's inner result.json URL. On duplicate SQS delivery:
- if the existing pair is valid for the same job/source, reuse the manifest URL and resend the same terminal event;
- if the existing object is invalid or has different lineage, fail closed; and
- never overwrite an existing result.
The webhook updates PostgreSQL only while (id, attempt=1, status="0") matches. A terminal row makes later duplicate or conflicting events no-ops. The SQS/webhook nonce is transport metadata, not persisted product state and not part of the row compare-and-set.
Workflow 3: Discover Completed Work
sequenceDiagram
participant P as Figma plugin
participant B as Backend
participant PG as PostgreSQL
participant S as S3
loop Until offset-zero page is empty or no progress is possible
P->>B: List status=1, materialized=false, figmaFileKey=current, limit=50, offset=0
B->>PG: Re-query remaining unmaterialized rows
B-->>P: Up to 50 rows + total
loop Each returned row, sequentially
P->>B: GET result
B->>S: Read and validate row.result_url spec
B-->>P: Validated Code2WF result artifact
P->>P: Materialize artifact.spec with shared builder
P->>B: POST placement (placedNodeId)
end
end
Generation does not require an open plugin, but discovery requires an authenticated plugin session in the target Figma file. Code2WF must add an open-time flow that repeatedly lists completed rows for the current file with materialized=false using limit=50&offset=0. For each returned row it fetches the immutable result before materialization, builds sequentially, records successful placement, and queries offset zero again until the page is empty. Re-reading offset zero is intentional because the filtered set shrinks after placement; advancing an offset could skip rows. Job IDs are deduplicated within the session, and a bounded no-progress guard stops a loop when no row can be completed. A failed result/build is left unmaterialized, reported to the user, and does not block other rows. An expired session prompts login and retries discovery; a row for another file is never fetched or materialized.
For each row, the plugin fetches the full immutable result artifact and passes its validated artifact.spec to the shared materializer. placementTarget keeps the existing WF2Des column and meaning. If it resolves to a node on the current page, the plugin passes that node's absolute page rectangle to the shared placeRoot, which places the generated root at the rectangle's x/y. The target is reference-only and is never resized, replaced, or deleted. Missing, stale, nested cross-page, or other-page targets use the existing viewport-center fallback. Code2WF adds the discovery/API wiring, not a different placement model.
The MVP has no cross-session claim. Two authenticated plugin sessions can discover the same row before either records placement; this is a known limitation. Each session still uses the existing job-ID stamp/index and rebuild-and-swap behavior, and placement remains idempotent, but the MVP does not claim atomic cross-session materialization.
There is no server claim, lease, retry count, or materialization status. Discovery relies on the same row-level materialized_at convention as WF2Des.
Workflow 4: Materialize and Record Placement
sequenceDiagram
participant P as Figma plugin
participant F as Figma file
participant B as Backend Code2WF API
participant PG as PostgreSQL
P->>F: Build Code2WF staging root
P->>B: POST placement (placedNodeId)
B->>PG: SET materialized_at
B-->>P: 200 existing/new timestamp
The planned Code2WF client wiring passes the existing DesignSpecModel to the shared WF2Des planner/materializer and rebuild-and-swap contract. The shared builder must be extended to honor the already-defined text fallback and layout sizing fields; no Code2WF-only node case or renderer is added. The root key wf2des stores JSON {jobId, specVersion, role: "root"}, while document key wf2des_index maps the job ID to the root ID, and the MVP accepts the existing wf2des · 1.0 root label. A caught build failure discards its staging root; cleanup after an abrupt plugin shutdown must be verified by an integration test. On a later session, Code2WF rebuilds from the immutable result and replaces the prior stamped root only after the replacement succeeds.
After the screen is complete, the plugin sends the generated screen's placedNodeId. The backend requires a completed row and sets materialized_at, following the same row-level placement convention as WF2Des.
If materialized_at is already set, a repeated placement request returns 200 with the existing timestamp and does not register or stamp again.
Failure Ownership
| Failure | Owner and behavior |
|---|---|
| Page Import incomplete or out of scope | backend rejects trigger |
| SQS dispatch failure | backend marks attempt 1 failed |
| Source/hash/schema invalid | worker reports failed |
| Unsupported visual/media content | worker emits the existing visible unmatched placeholder and completes |
| Temporary S3/webhook failure | SQS retry; no PostgreSQL write by worker |
| Caught plugin build failure | staging root is discarded; plugin rebuilds from the immutable result in a later session |
| Abrupt plugin shutdown | row remains unmaterialized; reopening rebuilds, while the integration test records whether an unidentifiable partial unstamped node remains; automatic cleanup is not claimed until a reuse-safe mechanism exists |
| Placement request/backend failure | materialized_at remains null; the plugin can retry later |
Failed generation is run again with a new job ID.