Design Generation Result Collection
Overview
The PRIMARY generation output collection โ one document per wf2des generation
run, holding the pinned inputs, the confirmed parse, the assembled DesignSpec,
the validator/confidence records, and the plugin placement + feedback
bookkeeping. The wf2des worker writes it across the parse and assemble phases
(fenced on (_id, attempt)); wf2des-api reads it for preview, review, and
materialization. It is never dropped.
Table Definition
| Logical Name | Physical Name | Column Name | Data Type | Primary Key | Relation | Unique | Nullable | Default Value | Remarks |
|---|---|---|---|---|---|---|---|---|---|
| Design Generation Result | design_generation_result | _id | string | โฏ | wf2des:id | The wf2des PG row id (the run identity โ one result doc per generation, 1:1 with the wf2des row; CAS write fence on (_id, attempt)). PRIMARY collection, never dropped |
|||
| organization_id | number | Tenant owner; every read/write filters it | |||||||
| project_id | number | project:id | Tenancy scope; every read/write filters it | ||||||
| attempt | number | The run attempt โ the write fence (CAS on (_id, attempt)) compares it to reject a superseded write; always 1 today โ nothing bumps it |
|||||||
| screen_id | string | โฏ | From the generation request (e.g. AC_RGST01); generation rows always carry it |
||||||
| request | object | Request snapshot echoed from the SQS message. Shape: { screen_id: string, prompt: string \| null, placement_target: string \| null, auto_confirm: boolean } |
|||||||
| request.screen_id | string | Requested screen id | |||||||
| request.prompt | string | โฏ | Optional selection prompt, ranked below memo intent | ||||||
| request.placement_target | string | โฏ | Optional node id echoed for placement | ||||||
| request.auto_confirm | boolean | false normal / true auto โ parse+assemble in one invocation |
|||||||
| inputs | object | Pinned generation inputs (the FIRST act of the parse invocation; rehydrated fail-closed on any hash mismatch at assemble). components = the exact set selected from, so a generation reproduces WHICH components (not only a registry hash). Shape: { wireframe {figma_file_key, node_id, source_hash}, design_rule {design_rule_id, content_hash}, components [{platform_design_id, content_hash}], llm {model_id, prompt_version, screen_review_model_id, screen_review_reasoning_effort, screen_review_enabled} } |
|||||||
| inputs.wireframe | object | { figma_file_key, node_id, source_hash } โ the pinned wireframe identity; source_hash โก the parse-cache key wf_content_hash and lineage.source_hash |
|||||||
| inputs.design_rule | object | โฏ | { design_rule_id, content_hash } โ the pinned validator program revision (design_rule doc key) |
||||||
| inputs.components | object[] | [{ platform_design_id, content_hash }] โ flat; the exact registry component set selected from (no registry hash). platform_design_id = the design (type=component) row id / design_component._id |
|||||||
| inputs.llm | object | { model_id, prompt_version, screen_review_model_id, screen_review_reasoning_effort, screen_review_enabled } โ the pinned model + prompt version (e.g. mp@0.4) and screen-review configuration; pins matching and review configuration so retries do not change provider/model behavior |
|||||||
| parse | object | The confirmed parse record. The full parse tree lives in the immutable parse.json artifact (S3, artifact_urls.parse) โ this block holds only the confirmation + snapshotted memo influences. Shape: { confirmed: boolean, confirmed_by: string \| null, confirmed_at: datetime \| null, rejected: {reason_code, note} \| null, memo_influences: [{memo_node_id, text}] } |
|||||||
| parse.confirmed | boolean | false interactively until the designer confirms |
|||||||
| parse.confirmed_by | string | โฏ | Confirming user (user:cognito_sub); null until confirmed |
||||||
| parse.confirmed_at | datetime | โฏ | The SINGLE home for the confirmation timestamp; null until confirmed | ||||||
| parse.rejected | object | โฏ | { reason_code, note } recorded on reject (row status 3), fed to curation. reason_code: wrong_roles / wrong_memos / wrong_sections / other. Null unless rejected |
||||||
| parse.memo_influences | object[] | [{ memo_node_id, text }] โ memo text SNAPSHOTTED so it survives a later wireframe reparse |
|||||||
| spec | object | DesignSpec โ the assembled, self-contained spec. root is a SpecNode tree of 5 node types (layout_frame / instance / compose / text / unmatched). Specs over ~1MB spill to an S3 pointer (artifact_urls.spec). Shape: { spec_version, parse_confirmed, style_bindings {<token>: string}, root: SpecNode } |
|||||||
| spec.spec_version | string | Spec schema version | |||||||
| spec.parse_confirmed | boolean | Mirror of the parse confirmation | |||||||
| spec.style_bindings | object | { <token>: styleId } โ token โ Figma style/variable id, derived FROM project_figma_file.style_captures |
|||||||
| spec.root | object | SpecNode tree root. layout_frame { auto_layout, children }; instance { component_key, component_node_id, component_name, variant_props, text_slots, assets, confidence, flagged, source{kind:registry, component_key}, lineage_wf_node_ids } (the platform_design_id lives here on the instance node, not on spec_nodes_flat); compose { primitives + small components composed under rules; ALWAYS flagged; source{kind:composed}, lineage_wf_node_ids }; text { content, style_token, confidence, flagged, lineage_wf_node_ids }; unmatched { placeholder{role,text,bbox}, confidence, flagged:true, source{kind:none}, lineage_wf_node_ids } |
|||||||
| spec_nodes_flat | object[] | Flattened per-content-node view for tuning / flag queries. Element: { layer_path: string, case: string, component_key: string, wf_role: string, confidence: number, flagged: boolean, lineage_wf_node_ids: [string] }. case: instance / compose / text / unmatched. NO platform_design_id (that stays on spec.root instance nodes). Stays inline even when spec spills |
|||||||
| selection | object | Per-section selection record (deterministic filter + section-parallel LLM choice; no similarity/retrieval). Shape: { sections, candidates_considered, composed_count, unmatched_count, asset_fills, truncated } |
|||||||
| selection.sections | number | Section count | |||||||
| selection.candidates_considered | number | Candidates considered across sections | |||||||
| selection.composed_count | number | compose-case node count |
|||||||
| selection.unmatched_count | number | unmatched-case placeholder count |
|||||||
| selection.asset_fills | number | Image slots filled from the pinned asset set | |||||||
| selection.truncated | boolean | Candidate-set truncation flag | |||||||
| screen_plan | object | โฏ | Grounded whole-screen review plus accepted/rejected proposals retained for replay | ||||||
| spec_hash | string | โฏ | Canonical SHA-256 of the base spec; identity fence for plugin render review | ||||||
| assembly_state_hash | string | โฏ | Hash of the pinned stitch/validation state stored at artifact_urls.assembly |
||||||
| layout_fidelity | object | โฏ | Layout preservation metrics: {frames_total, frames_preserved, wrap_total, wrap_preserved, units_collapsed, lost[]} |
||||||
| validator_report | object | Rules-validator result (violations auto-fixed where safe, else flagged). status: ok / no_rules โ no_rules means no registered rule doc, so the validator is a no-op and every node is flagged rules_unvalidated. Shape: { status, violations: [{rule, layer_path, action, detail}], unfixed_flagged } |
|||||||
| validator_report.status | string | ok / no_rules |
|||||||
| validator_report.violations | object[] | [{ rule, layer_path, action, detail }]; action: auto_fixed / flagged |
|||||||
| validator_report.unfixed_flagged | number | Count of violations left flagged (not auto-fixed) | |||||||
| confidence | object | Computed confidence only (model self-report is banned). flag_count reaches the wf2des row via the completion webhook. Shape: { min, avg, flag_count, formula_version } |
|||||||
| confidence.min | number | Minimum node confidence | |||||||
| confidence.avg | number | Average node confidence | |||||||
| confidence.flag_count | number | Flagged-node count; mirrored to wf2des.flag_count via the manifest row_effects.wf2des |
|||||||
| confidence.formula_version | string | Confidence formula version (e.g. cf@0.2) |
|||||||
| placement | object | Plugin build record. Shape: { design_area, placed_node_id, materialized_at, revision, spec_hash, review_id, materializer_report }. design_area is worker-written; the remainder is plugin-written and fingerprint-fenced |
|||||||
| placement.design_area | object | { x, y, w, h } โ the resolved DESIGN-area rect (worker-written) |
|||||||
| placement.placed_node_id | string | โฏ | The materialized node id (plugin-written) | ||||||
| placement.materialized_at | datetime | โฏ | Materialization timestamp (plugin-written) | ||||||
| placement.materializer_report | object[] | โฏ | [{ layer_path, event, detail }]; event: name_fallback / ordinal_fallback / build_error / font_fallback / prop_rejected / unmatched / preserved |
||||||
| feedback | object | โฏ | Feedback bookkeeping only โ changed_nodes + diff for progress/quality tracking; no learning loop. status is recorded onto wf2des.feedback_status by the backend feedback endpoint. Shape: { status, changed_nodes: [string], diff_url, at, by } |
||||||
| feedback.status | string | fixed / adopted |
|||||||
| feedback.changed_nodes | string[] | Changed layer paths | |||||||
| feedback.diff_url | string | S3 key of the feedback diff (โก artifact_urls.feedback_diff) |
|||||||
| feedback.at | datetime | Feedback timestamp | |||||||
| feedback.by | string | Feedback author (user:cognito_sub) |
|||||||
| llm_usage | object | What this run cost, from the provider's own report: { calls, requests, input_tokens, output_tokens, cached_input_tokens, stages: [{stage, calls, requests, input_tokens, output_tokens, cached_input_tokens}] } โ per stage, costliest first. Written by the ASSEMBLE phase (it owns the expensive calls); a parse-only doc carries the empty default |
|||||||
| timings | object | Run-lifecycle timestamps + per-phase durations. Shape: { created_at, parse_done_at, assembled_at, <phase>_ms } โ created_at is the run-start anchor (distinct from lineage.processed_at, the terminal-write time); parse stamps created_at / parse_done_at, assemble stamps assembled_at; <phase>_ms are per-phase latencies (e.g. snapshot_ms, parse_ms, selection_ms, assemble_ms, validate_ms) |
|||||||
| artifact_urls | object | Immutable artifact S3 keys. Shape: { result, assembly, spec, parse, feedback_diff }; assembly stores hash-pinned stitch inputs for bounded rendered-output review |
|||||||
| artifact_urls.assembly | string | โฏ | Pinned assembly-state artifact whose canonical hash is assembly_state_hash |
||||||
| artifact_urls.result | string | โฏ | Client-facing result artifact key ({org}/{proj}/wf2des/{id}-{ts}-result.json) |
||||||
| artifact_urls.spec | string | โฏ | Spec spill key, present only when spec > ~1MB |
||||||
| artifact_urls.parse | string | โฏ | Immutable parse.json key โ the AUTHORITATIVE parse a run used |
||||||
| artifact_urls.feedback_diff | string | โฏ | Feedback diff key | ||||||
| lineage | object | Shared lineage block on every doc (source S3 key/hash, processor version, processed time). job_id = the producing RUN id: the wf2des row id for generation runs, the job doc _id for internal runs. Shape: { source_url, source_hash, processor_version, index_schema_version, processed_at, job_id } |
|||||||
| lineage.source_url | string | The source snapshot S3 key | |||||||
| lineage.source_hash | string | Source content hash (โก inputs.wireframe.source_hash) |
|||||||
| lineage.processor_version | string | Producing processor version (e.g. wf-parse@1.0) |
|||||||
| lineage.index_schema_version | number | Document schema version for this collection | |||||||
| lineage.processed_at | datetime | Terminal-write timestamp (distinct from timings.created_at, the run-start anchor) |
|||||||
| lineage.job_id | string | The producing run id |
Relations
_id= the wf2des PostgreSQLwf2desrow id โ 1:1 with the row (the row is created first, the doc is written by the run).project_idโproject.id;organization_idโ the tenant owner.inputs.components[].platform_design_idโ the platformdesign(type=component) row id /design_component._id.inputs.design_rule.design_rule_idโ thedesign_ruledoc business key (withcontent_hash).inputs.wireframe.{figma_file_key, node_id}locate thewireframecache doc (composite_id = {project_id}_{figma_file_key}_{node_id}).parse.confirmed_byandfeedback.byare Cognito subs (user:cognito_sub), stored by value.- No relation is enforced inside DocumentDB; relations are logical and validated by application code.
Indexes
- PRIMARY KEY (
_id) โ unique;_idIS the wf2des row id (no surrogate). - Write fence is a CAS on
(_id, attempt)โ a zombie/superseded attempt's write is rejected (not a DB unique index; enforced by the worker's conditional write). - No secondary indexes are defined;
spec_nodes_flatandconfidence.flag_countare the flag/tuning query fields but are scanned within the single-doc-per-run model, not indexed cross-doc.
Notes
- Written by the generation worker across two phases: parse writes
inputs+parse(+artifact_urls.parse,timings,lineage); assemble writesspec/spec_nodes_flat/selection/validator_report/confidence/placement.design_area(+artifact_urls.result, spec spill). All writes are fenced on(_id, attempt). - Read via
wf2des-apifor preview, memo review, and materialization; the preview/review surfaces read this doc + the immutableparse.jsonartifact, NEVER the overwritablewireframecache doc. - The worker NEVER writes PostgreSQL.
confidence.flag_count, the completion status, and result refs reach thewf2desrow only through the ai-status webhook handler (the sole completion-time PG writer), fed from the S3 result manifest. parse.confirmed_atis the single home for the confirmation timestamp (not duplicated intotimings).parse.memo_influences[].textis snapshotted so it survives a later wireframe reparse.placement.placed_node_id/materialized_at/materializer_reportand thefeedbackblock are written later by the plugin path viawf2des-api, not by the generation worker.- A COMPLETED generation is never reprocessed โ a full replace would overwrite primary human-event data with a different nondeterministic spec. A terminal row is final; re-running a wireframe creates a NEW row.
specover ~1MB spills toartifact_urls.spec;spec_nodes_flatand the summary blocks stay inline. This is the only byte limit that applies to the doc.