Design Rule Collection
Overview
Collection holding the design-rule revisions the generation pipeline consumes.
The rule_process worker writes it: from the designer-SELECTED Figma guideline
boards (one rule_upload event) it renders each board via the Figma REST API,
runs one vision extraction per board, and merges the fragments deterministically
into a single machine-checkable RuleSet (webhook-free, no PG effect). The
collection IS the rule revision history: one immutable document per
(design_rule_id, content_hash of the MERGED RuleSet), version being the
ordinal. Each revision stores BOTH the merged rules โ the validator program
generation-assemble executes to auto-fix or flag spec violations โ AND the
source boards (image refs), which attach to every per-section selection call as
the selection LLM's vision reference.
Table Definition
| Logical Name | Physical Name | Column Name | Data Type | Primary Key | Relation | Unique | Nullable | Default Value | Remarks |
|---|---|---|---|---|---|---|---|---|---|
| Design Rule | design_rule | _id | string | โฏ | uuid, stored as the DocumentDB _id (surrogate) |
||||
| organization_id | number | Tenant owner | |||||||
| project_id | number | project:id | Project scope; every read/write filters organization_id + project_id |
||||||
| design_rule_id | string | Platform design_rule row id (by value, never an FK). The collection IS the version history: one IMMUTABLE doc per (design_rule_id, content_hash); version = ordinal; a generation pins (design_rule_id, content_hash) for reproducibility. lineage.processed_at = last-sync marker |
|||||||
| version | number | rule_process assigns version = (current max for design_rule_id) + 1; idempotent via unique (design_rule_id, content_hash) โ a replay of unchanged content maps to the existing doc/version; concurrent registrations serialized by the single rule_upload event |
|||||||
| content_hash | string | โฏ | Version identity โ sha256 (canonical JSON, sorted keys) of the MERGED RuleSet; unique (design_rule_id, content_hash) โ identical extractions converge on the existing revision. e.g. d4e5f6... |
||||||
| draft_source | string | Provenance of the rule content โ human / llm_draft_adjusted / llm_extracted. Board extraction lands as llm_extracted, pending designer review. e.g. llm_extracted |
|||||||
| rules | object | Machine-checkable rules the validator executes. Shape: { spacing: {scale[], section_gap}, typography: [{style_token, max_lines, usage, size_px, size_min_px, size_max_px, line_height_pct, weight, applies_to[]}], colors: {entries[{token, value, usage}], allowed[]}, usage_rules: [], layout_patterns: [], rich_layouts: [], globals: {}, component_inventory: [], component_specs: [{component, variants[], states[], seen_on_screens[], slot_rules}], screen_group_policies: [{group, covers, screen_ids[], device, content_width_px, padding_x_px, padding_y_px, section_gap_px, element_gap_px, columns_min, columns_max, section_separator, full_bleed, source_text}] }. Every rule class names the spec field it binds to. component_inventory is superseded by component_specs and kept for older revisions. slot_constraints was removed โ it bound a wf_role to allowed platform_design_ids, but no extraction pass ever produced one, so the field was always empty and the binding never ran |
|||||||
| llm_usage | object | What this ingestion 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. Recorded rather than reconstructed, so retries are visible |
|||||||
| boards | array | Source guideline boards the rules were extracted from. Per element: { node_id: string, title: string, image_url: string (S3 render location), content_hash: string (sha256 of the image), mime: string }. Loaded from S3 at assemble and attached as the vision reference to every per-section selection call; a missing image fails the generation closed |
|||||||
| extraction_model_id | string | Extraction provenance โ the vision model that extracted the rules from the boards | |||||||
| extraction_prompt_version | string | Extraction provenance โ the rule-extraction prompt version. e.g. re@0.1 |
|||||||
| extracted_at | datetime | Extraction provenance โ when the extraction ran | |||||||
| lineage | object | Shared lineage envelope on every doc. Shape: { source_url: string, source_hash: string, processor_version: string, index_schema_version: number, processed_at: datetime, job_id: string }. source_hash โก content_hash; job_id = the producing rule_process run id |
Relations
- project_id โ project.id
design_rule_idโ platformdesign_rulerow id (by value, never an FK; the registry row is client-created through the platform API, not by this worker).- No direct relation is enforced inside DocumentDB; relations are logical and validated by application code.
Indexes
- PRIMARY KEY (_id)
- UNIQUE INDEX (design_rule_id, content_hash) โ the immutability / idempotency fence; a replay of unchanged content maps to the existing doc/version and never mints a new ordinal.
- SECONDARY INDEX (organization_id, project_id, design_rule_id) โ version-history lookup +
max(version)assignment for the next ordinal.
Type Codes
draft_source:
human: authored/edited by a curatorllm_draft_adjusted: one-time initial LLM draft, curator-adjustedllm_extracted: extracted from the selected guideline boards by the ingestion-time vision extraction; lands pending designer review โ the designer inspects the extracted rules and re-registers after guideline fixes (identical merged rules converge on the same revision; changed rules mint the next version)
Notes
rule_processowns writes to this collection. It extracts the rules from the designer-selected guideline boards: it resolves the board titles via the Figma REST API (a missing board node is a hard error), renders each board as PNG (service-account PAT) and stores the render to S3, runs one vision extraction per board (LLM fence call 4 โ the worker is no longer LLM-free and needs the PAT), then merges the fragments deterministically (boards processed sorted bynode_id; allowed-value unions sorted; first-non-null / first-occurrence-wins). It stays webhook-free and has no PG effect: thedesign_ruledoc IS the record.generation-parse pins the project's LATEST revision (highestversion, ties broken by newestlineage.processed_at); with no revisions the pin isNone, the validator is a no-op and every node is flaggedrules_unvalidated(validator_report.status = no_rules). The former inline strict-file pin path is removed (the parse message no longer modelsdesign_rule_file_url).generation-assemble reads the pinned revision (keyed(design_rule_id, content_hash)) as the deterministic rules validator AND loads itsboards[]images from S3, attaching them as the advisory vision reference to every per-section selection call; a missing pinned doc or a missing board image fails the generation closed (ContractFailure).- Documents are IMMUTABLE โ a new rule revision is a new document, never an in-place edit.
versionis the ordinal perdesign_rule_id. Reproducibility rides the revision's immutability โ no separate set hash. content_hash= sha256 over the canonical JSON (sorted keys) of the MERGED RuleSet; it is both the version identity and the idempotency key โ identical extractions converge on the existing revision, changed rules mint the next version.- Bump
index_schema_version(carried inlineage) when the document shape changes.