Skip to content

AI Des2WF — Stores & DB Definition

Des2WF shares platform stores with WF2Des and owns its kit, result and graphic-override data. This page is the des2wf-scoped contract; canonical per-table pages under development/database/ follow the frozen shapes.

Ownership rule, non-negotiable: the backend owns PostgreSQL and the worker never writes it. Workers hold no PG credentials; every PG effect is applied by the ai-status handler or a product endpoint.


PostgreSQL — the shared wf2des generation row

Des2WF does not get its own table. It extends the existing one, because one table keeps job ids globally unique across both edges, which is what keeps the plugin's single idempotency index safe by construction.

Column Type Notes
edge enum '0' wf2des, '1' des2wf, default '0'. Named edge, not direction: that name is already taken in the worker schemas for auto-layout axis. Dispatch routes on it as a tagged union, never on which optional fields happen to be populated
wf_node_id string the input node — a wireframe node on the wf2des edge, the design frame on the des2wf edge. One column, read together with the edge beside it
screen_id string NOT NULL on the shared row, and never read as a screen-grammar match on this edge, so a des2wf run stores the source node id in it
score, score_version numeric, text the sortable scalar + the formula version, the same role flag_count plays. Written only when the worker reports one, so the wf2des edge — which reports none — is not stamped 0 on a sortable column. The BAND is deliberately not in PG: it lives in the result document, where the ratchet reads it
other columns — status, phase, attempt, result refs, timestamps, audit fields — one shape for both edges

The open-generation fence. The partial unique index is (project_id, figma_file_key, wf_node_id, edge) WHERE status = '0' — at most one live run per node, per project, per edge. Two consequences:

  • edge is part of the key, so an in-flight run blocks a second run on the same node for the same edge only: a design frame may run through des2wf while its own wireframe runs through wf2des;
  • the fence has no sweep. A row stranded at status = '0' keeps its node key occupied until the run is cancelled, and des2wf's cancel accepts a run that is still processing in any phase — wf2des's own cancel additionally requires the confirm checkpoint, which a one-shot edge never reaches.

DocumentDB

Collection Shared? Des2WF's use
wireframe shared with WF2Des the output. The emitted WireframeDoc: based_on = 1 (design), type = 0 (page), memos = [], summaries computed
wireframe_component des2wf-owned the wireframe kit — the output's building blocks. Written by the shared component sweep under its wireframe target, read at run time by selection
design_component shared, owned by WF2Des read-only here. Selection consults it for the name, slots, size and axes of the design component it must express, and never writes back
project_figma_file shared per-file style captures and sweep watermark; scopes the sweep walk
des2wf_generation_result des2wf-owned the run's own document, _id = the shared row id, committed under an (_id, attempt) fence so a redelivered older attempt cannot overwrite a newer one. The render review amends it in place, merging a render_review block and keeping any previous one under render_review_previous. Separate from the wf2des result collection because the two edges answer different shapes — a wireframe spec + score against a design spec + confidence — and one collection holding both would make every reader branch on the edge
des2wf_verdict des2wf-owned Explicit human overrides only: record_type=graphic_override_v1, filtered by organization, project and file. Legacy verdict records are ignored by generation; no automatically cached AI judgments

Explicit graphic overrides

The storage name remains des2wf_verdict (config: DES2WF_VERDICT_TABLE_NAME). Its current runtime records are graphic_override_v1, not the legacy metadata-only verdict schema.

Field Contract
_id graphic: + SHA256 of JSON [organization_id, project_id, file_key, target, identity, variants], serialized with sorted keys
record_type graphic_override_v1
organization_id, project_id, file_key Required scope on lookup and operator changes; no cross-tenant or cross-file reuse
target node or component
identity Exact source node ID, or actual master component ID
variants Exact component variant mapping; default {}, not a wildcard
action preserve or simplify
reason Required explanation, 1–600 characters

Node overrides take precedence over component overrides. Conflicting duplicates prefer preserve. Component overrides apply to every matching appearance in this file; prefer node targets for context-specific exceptions. An explicit simplify produces an ink placeholder, not decoration removal. The classifier itself never writes an override.

Run the operator CLI from guinness-ai-v2, using the worker's DOCUMENTDB_* environment. The set and clear commands below are intentional database writes; substitute the intended scope and review it first. clear removes only that exact scoped override, leaving other variants and legacy records untouched.

uv run python apps/des2wf/scripts/graphic_override.py --org 1 --project 7 --file FIGMA_FILE_KEY list
uv run python apps/des2wf/scripts/graphic_override.py --org 1 --project 7 --file FIGMA_FILE_KEY set --node '123:456' --action preserve --reason 'Navigation meaning'
uv run python apps/des2wf/scripts/graphic_override.py --org 1 --project 7 --file FIGMA_FILE_KEY set --component '123:400' --variants '{"State":"Default"}' --action simplify --reason 'Redundant artwork beside its label'
uv run python apps/des2wf/scripts/graphic_override.py --org 1 --project 7 --file FIGMA_FILE_KEY clear --node '123:456'

Legacy verdict.py / propose_verdicts.py records are not consumed by generation. If override loading fails, classification remains best-effort and does not reactivate legacy demotions.

The result's graphic_classification stores per-appearance evidence and decisions separately from these human rules. Inspect both the proposal and post-kit rendered_kind / covered_by before concluding a source glyph was visibly replaced. See I/O Definition.

Why wireframe_component is a separate collection

The WF component library is a different library from the design system — not a subset of it — so it does not belong in the design registry. Two further reasons, both mechanical:

  1. WF2Des's candidate generator is offered the whole registry for every section, with the kind gate config-defaulted off. Putting a second vocabulary in that collection makes WF2Des's behaviour depend on a filter being correct at every read site, against a pipeline that is already the weaker of the two.
  2. Leakage in that direction is already observed in the plugin's own code, which names it self-reinforcing corruption. A separate collection makes the failure impossible rather than filtered.

One sweep serves both libraries. It is parameterised by a registry target carrying the three facts that differ: the collection, the identity rule ({project_id}_{component_key} for the kit, the node composite for the design system), and whether the semantic enrichment pass applies — it does not for a kit, whose atoms are already named by their own shape, and running a design vocabulary over them would invent semantics the kit never claimed. Registry reads stay bound to the design accessor, so a read cannot drift onto the kit and quietly make its atoms design candidates.

wireframe_component — fields

The document shape is the sweep's, shared with design_component; what selection reads of it:

Field Purpose
_id {project_id}_{component_key} — composite on the componentSet publish key, never a node id (per-file-copy), never a name (measured collisions covered 56% of instances by use). A master with no publish key yet falls back to the node composite rather than going unregistered. Shared by every variant of the set, so it is not what selection votes for
name display only, explicitly not identity
family the board the entry was swept from — the kit's own grouping. Used to collapse interchangeable stand-ins, never matched against a design layer name
component_key, component_node_id how the plugin reaches the master. The kit's pages are copied into the working file, so its masters are local: importComponentByKeyAsync has nothing to import, and the node id is the only way to instance them. An entry carrying neither is dropped at load as unreachable
variant_properties, variant_defaults the set's axes and the value each falls back to. A variant value the set does not enumerate is never accepted from the model — Figma would refuse it at materialize time
variants per-variant variant_props, size, text_slots, nested_components — the selectable rows
text_slots, default_size the set's own; read only for a document that declares no variants
lineage source file, hash, processor version, index_schema_version — identifies which kit (and which version of it) an entry came from: the kit is designer-chosen and will change, so a swap is a re-registration and stale entries must be tellable from current ones

The selectable unit is a variant, not a component. A component set cannot be instanced; one of its variants can. Selection therefore expands each document into one row per entry in variants — falling back to the top level only for a document that declares none — and votes for, and resolves by, a key unique to that row. Keying by _id instead hands the guards an arbitrary sibling of the variant that was shortlisted, and it measures the wrong object: the top level states the default variant's slots and size, while 44 of 415 registry documents hold a variant with more text slots than the top level reports.

Slot capacity is the number of distinct slot layer paths. The materializer addresses a slot by its path and writes it, so two slots declaring one path are one destination — the second write overwrites the first, and that design string is gone. The guards count paths, never declarations: on the live kit 52 variants declare a repeated path, and one declares five slots that all address a single layer.

The kit's pages are copied into the working file, so its masters are local — the same installation the design system has on the wf2des side. The setup view reads the registered kit back as a summary: how many entries carry a node id (without one nothing can be instanced at all), how many carry a publish key, and how many declare variant axes and text slots.

The wireframe collection — two hazards

  1. _id collision — impossible by construction. Des2WF writes the edge-scoped {project_id}_{figma_file_key}_{node_id}~des2wf; wf_parse keeps the existing unscoped id. The silent last-write-wins overwrite between the two edges is impossible by construction, and the wf2des parse-cache lookup is untouched.
  2. based_on is written and never read. The output carries based_on = 1 (design), which is the only thing that tells a machine-made wireframe from one a designer drew. Nothing on the consuming side refuses it: a des2wf wireframe can reach WF2Des as if a designer had drawn it, and only a consumer-side check on this field can stop that.

Kit volatility

The kit is per-project data and will change. Two store-level rules follow:

  • selection reads the registry at run time rather than a precomputed table, so a swapped kit takes effect on the next run: an entry absent from the currently registered kit is simply not among the atoms a run loads, and the design component it expresses is decided again;
  • nothing kit-specific is ever encoded in code or in a schema default; a kit swap must be expressible entirely as a data change in wireframe_component.

S3

Path Content
{org}/{proj}/des2wf/snapshots/{file}/{node}.{source_hash}.json Frame-only REST envelope with pruned components, componentSets and styles maps
{org}/{proj}/des2wf/snapshots/{file}/{node}.{source_hash}.{render_hash}.png Optional immutable source PNG from the snapshot's Figma version, with absolute bounds; generation's design_png_url
{org}/{proj}/des2wf/{job_id}/parse.json Parsed tree relied on by the run
{org}/{proj}/des2wf/{job_id}/result.json Spec, score fields and graphic_classification audit
{org}/{proj}/des2wf/{job_id}/manifest.json Artifact index and row_effects.wf2des, referenced by the webhook
{org}/{proj}/des2wf/renders/{job_id}/design.png Plugin-provided design image for post-terminal review
{org}/{proj}/des2wf/renders/{job_id}/wireframe.png Plugin-provided generated wireframe image for that review

Source-evidence keys include content hashes. The review pair uses per-job paths, not immutable content-addressed keys, and can be replaced on re-review. Do not confuse these two image lifecycles. An unsuccessful source export/upload leaves design_png_url absent rather than pointing to a missing object. The worker also caps fetched source PNG bytes at 20 MiB and decoded pixels at 32,000,000; failure preserves artwork.

The retained snapshot maps are part of the identity contract: publish keys and named style identity cannot be recovered after they are discarded.