AI Des2WF — I/O Definition
Contract for apps/des2wf/ in guinness-ai-v2. Des2WF converts one Figma design frame into a
wireframe spec and a structure record. Changes to these fields or behaviors also belong in the
Test Case Design and the Backend Contract.
Run families
Generation is one-shot: parse chains into assembly in the same invocation, with no confirm checkpoint,
awaiting_confirm park or rule-ingestion step. The worker never writes PostgreSQL. A separate
post-terminal render-review message can amend the completed result after plugin materialization.
Shared contract primitives
The edge discriminator
Generation messages retain edge=des2wf on the dedicated DES2WF queue. The webhook uses
type=des2wf. Neither is inferred from optional-field presence. The shared PostgreSQL table remains
wf2des, so manifest effects are keyed row_effects.wf2des for both edges.
Generation message
{
"edge": "des2wf",
"job_id": "01J...",
"attempt": 1,
"nonce": "...",
"phase": "parse",
"organization_id": 1,
"project_id": 7,
"figma_file_key": "...",
"source_node_id": "64:6716",
"design_snapshot_url": "s3://bucket/path/design.json",
"design_png_url": "s3://bucket/path/version-matched-source.png",
"source_hash": "<frame-subtree-sha256>",
"screen_id": null,
"mode": "primitives",
"layout": "auto"
}
| Field | Contract |
|---|---|
edge |
Required literal des2wf |
job_id, attempt, nonce |
Nonempty shared row ID, attempt ≥ 1, nonempty attempt nonce |
phase |
parse (default) or assemble; the standard flow runs both in one invocation |
organization_id, project_id |
Positive IDs |
figma_file_key, source_node_id |
Input file and design frame identity |
design_snapshot_url, source_hash |
Required S3 snapshot URL and frame-subtree hash |
design_png_url |
Optional S3 URL, default null. Backend-captured, version-matched source evidence; older messages remain valid |
screen_id |
Optional, null in the standard DES2WF flow |
mode |
primitives (default) or kit |
layout |
absolute (API/worker default) or auto; current plugin explicitly sends auto, as above |
The two wireframes
mode |
Output | Model use |
|---|---|---|
primitives |
Neutral primitives, verbatim copy, preserved meaningful graphics | Contextual graphic classification when usable source PNG evidence is available |
kit |
The same policy plus accepted wireframe-kit variants | Graphic classification plus sampled majority-voted kit selection |
Kit selection is per distinct design-component context, not a mapping table baked into code. Missing/unusable kit data or rejected proposals leave the primitive representation. Both modes can use a model; neither promises byte-identical output on repeated runs.
The two layouts
absolute puts drawn rows at their source coordinates under the root without reflow.
auto preserves the source's container ownership, including one-child wrappers, native
itemSpacing, padding, alignment, baseline and wrapping. It does not drop these wrappers and
measure new sibling gaps. Absolute overlays remain outside the flow; per-axis sizing and min/max
dimensions travel through source_layout. Both layouts preserve copy.
The snapshot envelope — retain the maps
The stored document is the design frame subtree only. Maps are pruned to references used by that
subtree, retaining publish keys and named style identity. sectionNodeId only narrows the backend
fetch; an invalid scope falls back to full-file lookup without storing siblings or board annotations.
source_hash hashes the frame subtree.
The backend keeps the top-level Figma version from the response that supplied the frame, then best-effort exports the source PNG at that same version with absolute bounds. The requesting user's PAT is used for both capture operations. Missing version, export or upload failure leaves the PNG URL absent rather than substituting a newer, unpinned image. The immutable PNG key contains both source and render hashes; see Stores.
Generation — parse phase
Parse reads the JSON snapshot and deterministically extracts the tree: identity, bounds, literal
copy, paint, named style keys, component IDs/variants, native layout, source_layout and
text_geometry. Component masters use derive_component_context, not the page-tree extractor,
so variant partitions are retained.
| Invalid input | Error |
|---|---|
| Source does not resolve to a FRAME | invalid_input.not_a_frame |
| No visible authored descendants | invalid_input.empty_frame |
| Snapshot exceeds configured cap (default 20 MB) | invalid_input.snapshot_too_large |
The plugin can normalize an accepted frame-like selection before capture; this does not broaden the
worker's FRAME contract. There is no design-ness detector. based_on=1 records provenance on output;
do not treat that field alone as enforcement by every downstream consumer.
Generation — assemble phase
The current order is optional kit selection → classify_sync → census → apply_kit →
record_rendered_policy → emit → score. The census kinds are text, box, region, rule,
ink, ornament, instance, glyph; emitted specs still use the existing five node cases.
The graphic classifier receives a screen overview, graphic and containing-control crops, names, variants, ancestor context, bounds and local labels. Candidate detection is structural; name, size or image format does not itself authorize replacement.
| Decision | Applied output |
|---|---|
| Direction, action, state, identity, list marker, unknown or uncertain | glyph: preserve source artwork |
High-confidence simplify, no carried information, role content_image or redundant_artwork |
ink: placeholder; redundant artwork also requires a real local label_node_id |
Same guards, role decoration |
ornament: no paint, with transparent flow space where required |
| Explicit human override | Scoped preserve → glyph or simplify → ink; node before exact component/variant match |
The worker reads only graphic_override_v1 overrides, not legacy cached verdicts. Classification is
per appearance and AI decisions are not automatically reused across contexts.
Batches contain 6 candidates, with 3 concurrent calls and a 180-second classification budget;
all candidates are eligible through recursive interleaving. PNG evidence is limited to 20 MiB
downloaded bytes and 32,000,000 decoded pixels, with bounds/aspect validation. Missing or invalid
evidence, invalid/incomplete answers, unknown/duplicate IDs, provider failures and timeouts preserve
artwork and record fallback reasons.
Kit proposals must pass unreachable, copy_truncated, slot_overflow, imagery_heavy,
size_implausible and unknown_atom guards. Drawn rows must remain represented in lineage;
intentional decoration suppression is an explicit exception, not arbitrary node deletion.
Text stays verbatim. Source-aware text retains typography and neutral literal color; font fallback or necessary reflow can affect wrapping. Ordinary surfaces become neutral boundaries; rules and selected-state markers can be filled. Preserved glyphs are not forcibly recolored. See Abstraction Architecture for materialization details.
Outputs
| Output | Contract |
|---|---|
| Canvas frame | Plugin materialization; default des2wf · {source_name}, spec-version fallback for an empty source name; explicit spec.name wins |
WireframeDoc |
DocumentDB wireframe; based_on=1, type=0, memos=[], computed summaries; no generated annotations |
| Result artifact | S3 result.json: spec, score fields and graphic_classification |
| Result document | des2wf_generation_result: run identity/scope, spec, nested score, flag_count, lineage, graphic_classification |
| Manifest | S3 artifact references and row_effects.wf2des |
| Terminal PG row | Updated by backend ai-status handler, not the worker |
render_review |
Optional post-terminal addition to the result document |
PG wireframe / structure, component output |
Deferred; the queryable output is the DocumentDB document |
The stored WireframeDoc.name still uses wf · {design frame name}; this is separate from the
plugin's corrected canvas naming policy. The output document uses
{project_id}_{figma_file_key}_{node_id}~des2wf, leaving WF2Des's unsuffixed document untouched.
Spec additions
Des2wfSpec is {spec_version, edge, source_name, source_file_key?, name?, root}.
There are no style_bindings or parse_confirmed fields. source_file_key scopes source glyph
and text lookup; a mismatched file must not silently clone an unrelated node with the same ID.
| Field | Shape |
|---|---|
source_layout (optional on source-aware nodes) |
horizontal / vertical: FIXED, HUG or FILL; positioning: AUTO or ABSOLUTE; strokes_in_layout, hidden; optional min_width, max_width, min_height, max_height |
text_geometry (optional on text) |
font_family, auto_resize (NONE / HEIGHT / WIDTH_AND_HEIGHT / TRUNCATE), line_height, line_height_pct, pixel letter_spacing, leading_trim (NONE / CAP_HEIGHT), vertical_align (TOP / CENTER / BOTTOM), paragraph_spacing, paragraph_indent |
glyph_source (on a layout frame) |
Source node reference for preserving actual artwork; clone actual instances and overrides, not the default master |
Fields are opt-in; legacy specs without source geometry retain their existing materializer behavior. Cloning can retain read-only text features such as PALT. Font availability remains a runtime constraint.
Graphic audit
graphic_classification defaults to {} for older result documents. New runs record:
| Field | Meaning |
|---|---|
prompt_version, model |
Classifier identity; current prompt des2wf-graphics-visual-2 |
status |
not-needed, fallback, partial or complete |
source_render_sha256 |
Source PNG hash, or null |
candidate_count, decisions |
Candidate count and per-appearance records |
fallback_count, simplified_count |
Optional aggregate counts; absent on the early no-candidate return |
Each decision has node_id, applied action (preserve/simplify), status
(fallback/override/classified) and reason. Evidence-backed entries can add evidence_sha256 and
a proposal containing node_id, role, action, confidence, carries_information,
reason and optional label_node_id.
After kit application, rendered_kind, rendered_reason and covered_by explain the final
representation, including covered-by-source / covered. A child proposed for simplification
can still be covered by its preserved source parent; proposal counts are not counts of visible
placeholder nodes.
Render review
The review message carries edge=des2wf, kind=render_review, job_id, organization_id,
project_id, required S3 design_png_url and wireframe_png_url, and optional nonce.
Unlike generation's optional source PNG, both review images are required.
The plugin binds captures to the original source and project at generation start and checks the
materialized job ID. The backend checks write access, scoped row ownership, matching body jobId
(400 on mismatch), and COMPLETED status (409 otherwise) before upload/enqueue.
The worker validates result scope before image download, model calls or review writes.
The result is amended with render_review; a prior block moves to render_review_previous.
reviewed includes findings; skipped includes a reason and null finding_count, not zero.
Exports are bounded for the 8000px review limit. Review failure does not revert a completed generation.
Webhook
POST /v1/webhooks/ai-status, authenticated with X-API-Key, carries
{type: "des2wf", job_id, attempt, nonce, status}, plus result_manifest_url on success.
The manifest's row_effects.wf2des carries status, result_url, flag_count, score
and score_version. Score fields are additive to the shared WF2Des shape.
parse_done reports the parse boundary, succeeded applies completion and result references,
and failed applies the typed failure. Effects are attempt-guarded; stale or repeated terminal
events must not overwrite a newer result.
Score
ds@1.0 = emittability gate × verbatim content preservation as a multiset. Gate failures are
unmatched, strings_invented, text_displaced, cross_over_text, boxes_coincident,
and depth beyond 2 for absolute only. strings_lost reduces preservation, not the gate.
integrity, flatness, ink_covered and boundary_fidelity are reported at weight 0.
The result document contains {score, score_version, gate, band, terms}; PG receives only the
sortable score/version pair. Band margin is 0.07 when llm_assisted is true from kit selection or
graphic classification, otherwise 0.0. This is not a mode-based reproducibility guarantee.
A 1.0 structural score does not establish icon accuracy or visual fidelity. The JSON-only offline corpus checks fallback and layout; mocked model tests check guards, not recognition accuracy. Visual assessment needs real-provider runs and inspection of regenerated output.
Idempotency & fencing
- The backend creates the shared row before enqueue and is the sole completion-time PG writer.
- Result commits are fenced by job ID and attempt; terminal re-emission short-circuits.
- The open-generation fence includes project, file, node and edge. A terminal state or cancel frees it; no periodic sweep is implied.
- Render review amends an existing scoped result without upserting an unknown job.