Skip to content

AI Code2WF — I/O Definition

This document defines the planned contract for apps/code2wf/. Code2WF has one asynchronous generation attempt and a later client-side Figma materialization step. Page execution and capture belong to Page Import.


Overview

flowchart LR
  PI[("Completed Page Import")] --> API["Backend"]
  API -->|"resolved immutable result"| Q[("Code2WF SQS")]
  Q --> W["Code2WF worker"]
  W --> OUT[("Code2WF result artifact in S3")]
  W -->|"ai-status"| API
  API -->|"validated result artifact"| PL["Figma plugin"]
Concern Contract
Source selector HTTP pageImportId
Source eligibility same organization/project, non-deleted, completed
PostgreSQL source reference page_import_id only
Worker source reference exact Page Import result URL/hash/capture hash resolved at dispatch
Attempt fixed at 1 in the MVP
Worker database access none
DocumentDB not used
Figma writes plugin only

Shared Primitives

Generation status

Code2WF reuses the existing wf2des_status type for Figma generation rows and uses this subset:

Value Meaning
"0" processing
"1" completed
"2" failed

The enum's existing rejected/cancelled values are not used because Code2WF has no confirm, reject, or cancel transition.

There is no phase or materialization status. attempt is the literal integer 1.

Hash and object rules

  • Content hashes are lowercase SHA-256 with the sha256: prefix.
  • Source references in worker contracts use canonical s3://bucket/key URLs; terminal manifest/result pointers use bare keys resolved against RESULT_BUCKET, matching WF2Des.
  • Result JSON uses UTF-8 and deterministic key ordering/serialization before hashing.
  • Unknown fields are rejected in the Code2WF SQS message and outer result envelope. The shared WF2Des Pydantic models reject unknown node discriminators but currently ignore extra object properties, so the Code2WF producer emits only the documented shared fields and tests that exact emitted shape.

Result keys

{organization_id}/{project_id}/code2wf/{code2wf_id}/1/result/result.json
{organization_id}/{project_id}/code2wf/{code2wf_id}/1/result/manifest.json
{organization_id}/{project_id}/code2wf/{code2wf_id}/1/result/failed.json
{organization_id}/{project_id}/code2wf/{code2wf_id}/1/result/failed-manifest.json

Unlike WF2Des's timestamped multi-phase artifacts, Code2WF has one fixed attempt per row. Its deterministic per-attempt keys let an SQS redelivery validate and reuse the same artifact pair.

On success, the worker writes the client-facing spec first and its terminal manifest last. On failure, it writes the client-facing failed artifact first and its failed terminal manifest last. All writes are create-only. A duplicate delivery validates and reuses the existing pair; it never overwrites either object.

The terminal manifest is callback-only. It follows the WF2Des convention: the webhook carries its URL, and the backend reads row_effects.code2wf and stores the inner client-facing result_url on the PostgreSQL row. PostgreSQL never stores the manifest URL.

{
  "manifest_schema_version": 1,
  "job_id": "019ffa75-01c0-75a2-8123-456789abcdf0",
  "attempt": 1,
  "nonce": "8bed02b2-c93d-4854-8f4f-1c91744dc419",
  "job_type": "generation",
  "result_url": "1/7/code2wf/019ffa75-01c0-75a2-8123-456789abcdf0/1/result/result.json",
  "flag_count": 0,
  "row_effects": {
    "code2wf": {
      "status": "1",
      "result_url": "1/7/code2wf/019ffa75-01c0-75a2-8123-456789abcdf0/1/result/result.json",
      "flag_count": 0
    }
  }
}

The failure manifest uses result_url for failed.json and row_effects.code2wf.status = "2". The backend validates the job ID, attempt, nonce, job type, allowed S3 prefix, row effect, and status before applying it. On success, flag_count equals the number of validated spec outcomes with flagged=true.


Input 1: Backend Trigger

{
  "pageImportId": "0195f16e-6f11-7ea8-b45c-f6712ed9d8a3",
  "figmaFileKey": "aBc1DeFg2HiJ3kLmNoPqRs",
  "screenId": "AUTORACE_DATABASE",
  "placementTarget": "128:9001"
}

The backend generates code2wfId as UUIDv7. Every valid trigger creates a new row and dispatches a new job, including two triggers with identical bodies. The HTTP request does not accept a caller-provided job ID.

The backend stores the Figma destination and page_import_id, then reads and verifies the completed Page Import's immutable normalized manifest once while building the SQS message. Those exact bytes supply one consistent result URL/hash/capture-hash pin set. Page Import URLs and hashes are not copied into the code2wf row, and no PostgreSQL/S3 transaction is claimed.

The exact HTTP contract is defined in Trigger Code2WF.


Input 2: Conversion SQS Message

{
  "code2wf_id": "019ffa75-01c0-75a2-8123-456789abcdf0",
  "organization_id": 1,
  "project_id": 7,
  "attempt": 1,
  "nonce": "8bed02b2-c93d-4854-8f4f-1c91744dc419",
  "page_import_id": "0195f16e-6f11-7ea8-b45c-f6712ed9d8a3",
  "page_result_url": "s3://bucket/1/7/page-import/0195f16e-6f11-7ea8-b45c-f6712ed9d8a3/normalized/manifest.json",
  "page_result_hash": "sha256:44a7f61a6f90f476c349b264c0cbd824ce45ecad0d9985f0750f8a251aa9592a",
  "capture_hash": "sha256:90e54d8b9cf4ff0fdc2b927b4453d59f4c3e219c236bb398e660aca50a62ae27",
  "screen_id": "AUTORACE_DATABASE"
}
Field Type Validation
code2wf_id UUID row identity and result prefix
organization_id positive integer must match Page Import result and S3 prefix
project_id positive integer must match Page Import result and S3 prefix
attempt integer must equal 1
nonce UUID transport correlation only; not stored and not a PostgreSQL CAS field
page_import_id UUID must match the Page Import result
page_result_url S3 URL exact immutable completed Page Import result resolved by backend
page_result_hash SHA-256 exact UTF-8 bytes at page_result_url
capture_hash SHA-256 must match the Page Import result's normalized capture hash
screen_id string 1–255 characters; copied from the trigger row

Figma destination fields, credentials, source code, HTML, CSS, and presigned URLs do not enter SQS.


Processing Contract

For each SQS record, the worker:

  1. strictly validates the message and requires attempt = 1;
  2. reads the exact Page Import result and validates its byte hash;
  3. validates organization, project, Page Import ID, capture hash, and the allowed S3 prefix;
  4. loads only the normalized visible evidence declared by Page Import;
  5. maps grouping, controls, and annotations into the existing layout_frame / text tree and uses unmatched only for irreducible visible placeholders;
  6. builds deterministic annotation wording from captured evidence;
  7. validates the exact existing WF2Des DesignSpecModel, node union, and locked Code2WF limits;
  8. writes immutable result.json and then its terminal manifest, or immutable failed.json and then its failed terminal manifest; and
  9. reports terminal success or failure through ai-status.

The MVP has no Code2WF-only annotation model, node type, asset map, or rendering contract. Retryable processing failures are limited to transient storage, queue, or webhook dependencies.


Output 1: Code2WF result artifact and existing DesignSpecModel

{
  "job_id": "019ffa75-01c0-75a2-8123-456789abcdf0",
  "attempt": 1,
  "organization_id": 1,
  "project_id": 7,
  "screen_id": "AUTORACE_DATABASE",
  "status": "success",
  "generated_at": "2026-08-13T09:31:00Z",
  "inputs": {
    "page_import_id": "0195f16e-6f11-7ea8-b45c-f6712ed9d8a3",
    "page_result_url": "s3://bucket/1/7/page-import/0195f16e-6f11-7ea8-b45c-f6712ed9d8a3/normalized/manifest.json",
    "page_result_hash": "sha256:44a7f61a6f90f476c349b264c0cbd824ce45ecad0d9985f0750f8a251aa9592a",
    "capture_hash": "sha256:90e54d8b9cf4ff0fdc2b927b4453d59f4c3e219c236bb398e660aca50a62ae27"
  },
  "spec": {
    "spec_version": "1.0",
    "parse_confirmed": true,
    "style_bindings": {},
    "root": {
      "node": "layout_frame",
      "layer_path": "root",
      "auto_layout": {
        "direction": "horizontal",
        "gap": 32.0,
        "padding": 0.0,
        "sizing": "fixed",
        "gaps": [],
        "primary_align": "",
        "counter_align": "",
        "wrap": "",
        "counter_gap": 0.0
      },
      "fill": "#ffffff",
      "corner_radius": 0.0,
      "bbox": { "x": 0.0, "y": 0.0, "w": 1824.0, "h": 1860.0 },
      "lineage_wf_node_ids": [],
      "children": [
        {
          "node": "layout_frame",
          "layer_path": "root/screen",
          "auto_layout": {
            "direction": "vertical",
            "gap": 16.0,
            "padding": 24.0,
            "sizing": "fixed",
            "gaps": [],
            "primary_align": "",
            "counter_align": "",
            "wrap": "",
            "counter_gap": 0.0
          },
          "fill": "#ffffff",
          "corner_radius": 0.0,
          "bbox": { "x": 0.0, "y": 0.0, "w": 1440.0, "h": 1860.0 },
          "lineage_wf_node_ids": [],
          "children": [
            {
              "node": "layout_frame",
              "layer_path": "root/screen/details-button",
              "auto_layout": {
                "direction": "horizontal",
                "gap": 8.0,
                "padding": [12.0, 16.0, 12.0, 16.0],
                "sizing": "fixed",
                "gaps": [],
                "primary_align": "",
                "counter_align": "CENTER",
                "wrap": "",
                "counter_gap": 0.0
              },
              "fill": "#e5e7eb",
              "corner_radius": 4.0,
              "bbox": { "x": 24.0, "y": 24.0, "w": 240.0, "h": 48.0 },
              "lineage_wf_node_ids": [],
              "children": [
                {
                  "node": "text",
                  "layer_path": "root/screen/details-button/label",
                  "content": "View race details",
                  "style_token": "",
                  "style_refs": {},
                  "font_size": 16.0,
                  "font_weight": 600,
                  "color": "#111111",
                  "bbox": { "x": 16.0, "y": 12.0, "w": 160.0, "h": 24.0 },
                  "confidence": 1.0,
                  "flagged": false,
                  "source": { "kind": "none", "component_key": null },
                  "lineage_wf_node_ids": []
                },
                {
                  "node": "text",
                  "layer_path": "root/screen/details-button/A001",
                  "content": "[A001]",
                  "style_token": "",
                  "style_refs": {},
                  "font_size": 12.0,
                  "font_weight": 600,
                  "color": "#111111",
                  "bbox": { "x": 184.0, "y": 12.0, "w": 40.0, "h": 24.0 },
                  "confidence": 1.0,
                  "flagged": false,
                  "source": { "kind": "none", "component_key": null },
                  "lineage_wf_node_ids": []
                }
              ]
            }
          ]
        },
        {
          "node": "layout_frame",
          "layer_path": "root/annotations",
          "auto_layout": {
            "direction": "vertical",
            "gap": 12.0,
            "padding": 16.0,
            "sizing": "fixed",
            "gaps": [],
            "primary_align": "",
            "counter_align": "",
            "wrap": "",
            "counter_gap": 0.0
          },
          "fill": "#f3f4f6",
          "corner_radius": 4.0,
          "bbox": { "x": 1472.0, "y": 0.0, "w": 352.0, "h": 120.0 },
          "lineage_wf_node_ids": [],
          "children": [
            {
              "node": "text",
              "layer_path": "root/annotations/A001",
              "content": "[A001] Navigates to /proto-pages/auto-race/at_db_rslt03",
              "style_token": "",
              "style_refs": {},
              "font_size": 14.0,
              "font_weight": 400,
              "color": "#111111",
              "bbox": { "x": 16.0, "y": 16.0, "w": 320.0, "h": 48.0 },
              "confidence": 1.0,
              "flagged": false,
              "source": { "kind": "none", "component_key": null },
              "lineage_wf_node_ids": []
            }
          ]
        }
      ]
    }
  }
}

Result envelope contract

Field Contract
job_id Code2WF row UUID, following the WF2Des result artifact convention
attempt literal 1
organization_id, project_id positive integers matching input
screen_id 1–255 characters; exact trigger value
status literal success, following the WF2Des result artifact convention
generated_at UTC ISO 8601 terminal-write time
inputs exact Page Import ID, immutable result pointer/hash, and capture hash from the SQS message
spec exact existing WF2Des DesignSpecModel object

The worker persists the nested spec with the same DesignSpecModel.model_dump(mode="json") convention used by WF2Des. Default-valued shared fields are therefore present in the artifact, including lineage_wf_node_ids, style_refs, source.component_key, and every AutoLayout default shown above. Code2WF does not add a second serializer or omit defaults selectively.

Existing DesignSpecModel contract

Field Contract
spec_version Code2WF requires the current shared version 1.0; the shared model itself stores this as a string
parse_confirmed literal true; compatibility field because Code2WF has no confirm phase
style_bindings empty object in the low-fi MVP; existing text fallback fields carry neutral typography
root one existing SpecNode; Code2WF emits an existing layout_frame root

Node contract

Code2WF does not define a second Figma schema. The nested spec is validated by the same AI DesignSpecModel and is assignable to the plugin's existing AssemblySpec. The full existing node union is layout_frame, instance, compose, text, and unmatched; Code2WF emits only layout_frame, text, and unmatched in the MVP.

Field Contract
node existing layout_frame, text, or unmatched
layer_path unique slash-delimited path, 1–1024 characters
bbox finite x/y in -1,000,000…1,000,000 and w/h in 0…32,768

layout_frame uses the same auto_layout, fill, corner_radius, bbox, lineage_wf_node_ids, and ordered children field names as WF2Des. Direction is horizontal or vertical; padding is one number or [top,right,bottom,left]; sizing is the existing hug or fixed value; gaps is empty when uniform gap applies; and no frame has more than 1,000 direct children.

text uses the existing required fields content, style_token, confidence, and source, plus existing fallback fields such as font_size, font_weight, color, and bbox. Code2WF-generated text uses source.kind="none"; the existing wireframe source kind remains reserved for WF2Des wireframe fallback. Low-fi controls are nested layout_frame / text nodes, not a new primitive. Unsupported media or shapes become visible unmatched nodes with the existing placeholder {role,text,bbox}, flagged=true, and source.kind="none" contract.

Annotations are also ordinary existing nodes. The root contains a screen frame and an annotation-panel frame; a stable marker such as [A001] is a sibling of the original control label and appears again on its annotation text row, so the captured copy is unchanged. Numbering follows normalized DOM order. Wording uses fixed evidence-only templates such as Navigates to {href}, Submits {METHOD} to {action}, Input: {type}; required, and Capture warning: {code}. It never infers uncaptured JavaScript behavior.

Geometry conversion

  • The screen frame uses the Page Import document_width and full document_height, including below-the-fold content; the viewport height is not used as a crop.
  • Normal flow, flex, and grid evidence is converted into ordered nested layout_frame nodes using the existing direction, padding, gap, alignment, wrap, and bounding-box fields.
  • A child bbox.x/y is source evidence and a validation aid, not a new absolute-positioning instruction. Placement inside a frame is determined by the existing auto-layout tree. Code2WF does not add an absolute-position field or node case.
  • If overlap, transform, fixed/sticky positioning, or another relationship cannot be represented without hiding or reordering visible content, the worker emits an existing visible unmatched placeholder at that location and sets flagged=true instead of claiming unsupported fidelity.

Unknown node discriminator values fail shared DesignSpecModel validation. Extra properties are currently ignored by the shared Pydantic models, so Code2WF does not rely on them and emits only the documented shared fields.

The existing planner, node cases, materializer, wf2des stamp/index, and rebuild-and-swap path are reused; no Code2WF renderer or node schema is added. The current builder already creates these node cases, fills frames, applies padding/gaps/alignment, pins widths, writes text content, and renders unmatched placeholders. Before Code2WF release, the same shared builder must finish support for fields that already exist in the shared schema: sizing="fixed" pins both bbox.w and bbox.h; sizing="hug" keeps the current bbox.w pin with auto height; wrap="WRAP" is authoritative, while an empty wrap keeps only the current overflow-safety wrap; and counter_gap maps to Figma counter-axis spacing whenever wrapping is active. Text fallback applies font_size, font_weight, color, and bbox.w before style bindings, allows text height to grow rather than clip, and lets a successfully resolved binding override its field. An unavailable requested weight uses the existing shared font fallback and diagnostic. The shared plugin type must also accept source.component_key: null, matching the existing Pydantic serialization. These are shared-contract/materializer alignments, not a parallel Code2WF path.

The planned plugin wiring resolves the row's optional placementTarget and passes the target's absolute current-page rectangle to the existing placeRoot. The shared function places the generated root at that rectangle's x/y; it does not replace, resize, or delete the target. An absent, stale, or other-page target uses the existing viewport-center fallback. The MVP accepts the existing root label wf2des · 1.0 and existing plugin-data namespace.

Locked V1 limits

Limit Value
UTF-8 result.json size 10 MiB
Total nodes 5,000
Tree depth 64
Visible annotation rows 500

Exceeding a structural limit is a terminal result_limit_exceeded failure; the worker does not silently drop interactive content. The existing spec has no Code2WF-only assets, annotations, or warnings collection.


Output 2: Terminal Webhook

Success reports the immutable terminal-manifest pointer:

{
  "type": "code2wf",
  "job_id": "019ffa75-01c0-75a2-8123-456789abcdf0",
  "attempt": 1,
  "nonce": "8bed02b2-c93d-4854-8f4f-1c91744dc419",
  "manifest_schema_version": 1,
  "status": "succeeded",
  "result_manifest_url": "1/7/code2wf/019ffa75-01c0-75a2-8123-456789abcdf0/1/result/manifest.json"
}

A stable worker failure uses the same envelope with status: "failed", result_manifest_url pointing to failed-manifest.json, and safe error: {message}, matching the WF2Des producer contract. The backend validates the manifest, stores its inner failed-artifact result_url and safe error, and leaves row flag_count null while (id, attempt, status="0") matches. Immutable artifacts plus the row CAS make a duplicate event after the row is terminal an accepted no-op.


Error Handling

Code Retryable Meaning
invalid_message No SQS body violates the closed contract
source_not_found No resolved Page Import result/object is absent
source_hash_mismatch No source bytes do not match the dispatch hash
source_scope_mismatch No tenant, import ID, capture hash, or prefix differs
unsupported_source_schema No Page Import result version is unsupported
invalid_result No generated spec violates the closed schema
result_limit_exceeded No generated spec exceeds a locked V1 limit
storage_unavailable Yes transient S3 dependency failure
webhook_unavailable Yes terminal event was not accepted

Annotation model errors are absent because the MVP creates annotation text deterministically and calls no annotation-only model.


Idempotency & Fencing

  • Every accepted trigger receives a new backend-generated UUIDv7. Identical request bodies create separate rows and jobs; the caller cannot supply or reuse a job ID.
  • The MVP fixes attempt at 1. Backend terminal updates use (id, attempt=1, status="0") as the PostgreSQL compare-and-set fence.
  • The worker writes the client-facing result or failure artifact first and its terminal manifest last. Both writes are create-only. A duplicate delivery validates and reuses the exact existing pair; different identity or Page Import pins fail closed and are never overwritten.
  • The backend validates the terminal manifest and applies row_effects.code2wf only while the processing-row fence matches. A duplicate or conflicting callback after a terminal transition is an accepted no-op.
  • nonce is webhook transport correlation only. It is not stored in PostgreSQL and is not a row fence.
  • Placement is idempotent through the existing row-level materialized_at convention. The MVP intentionally has no cross-session materialization claim or lease.

Runtime Configuration

Config Value / Source Notes
RESULT_BUCKET shared result bucket Resolves allowed Page Import s3:// inputs and owns immutable Code2WF result/manifest keys
WEBHOOK_BASE_URL private backend origin Code2WF posts terminal status to the existing ai-status endpoint
WEBHOOK_API_KEY deployment secret Sent only as the existing X-API-Key webhook header

The Code2WF queue is attached through the Lambda event-source mapping; the worker does not need a queue URL. The worker has no model, DocumentDB, PostgreSQL, Figma token, or browser configuration.


Logging

Structured logs include code2wf_id, organization_id, project_id, attempt, screen_id, terminal outcome, safe error code, source/result hashes, node and flag counts, and read/convert/validate/write/webhook durations. Logs and failure payloads never include raw source code, DOM or page copy, object bytes, cookies, credentials, authorization headers, browser storage, or presigned URLs.


Field Reference (Lookup Table)

— means the field is absent from that surface.

HTTP field PostgreSQL code2wf SQS message Result artifact Notes
code2wfId id code2wf_id job_id backend-generated UUIDv7
pageImportId page_import_id page_import_id inputs.page_import_id references the completed Page Import row
path organization/project IDs organization_id, project_id organization_id, project_id organization_id, project_id every access is tenant-scoped
— attempt attempt attempt literal 1 in the MVP
— — nonce — webhook transport correlation only
— — page_result_url, page_result_hash, capture_hash matching fields under inputs one verified immutable Page Import pin set; not copied into PostgreSQL
figmaFileKey figma_file_key — — used by backend/plugin discovery only
screenId screen_id screen_id screen_id exact trigger value
placementTarget placement_target — — optional plugin placement reference only
— result_url — written at the deterministic result key backend stores the manifest row effect's inner client-facing artifact URL
— flag_count — derived from spec count of validated shared-spec outcomes with flagged=true
placement response materialized_at — — set only after successful plugin placement