Skip to content

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 โ†’ platform design_rule row 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 curator
  • llm_draft_adjusted: one-time initial LLM draft, curator-adjusted
  • llm_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_process owns 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 by node_id; allowed-value unions sorted; first-non-null / first-occurrence-wins). It stays webhook-free and has no PG effect: the design_rule doc IS the record.
  • generation-parse pins the project's LATEST revision (highest version, ties broken by newest lineage.processed_at); with no revisions the pin is None, the validator is a no-op and every node is flagged rules_unvalidated (validator_report.status = no_rules). The former inline strict-file pin path is removed (the parse message no longer models design_rule_file_url).
  • generation-assemble reads the pinned revision (keyed (design_rule_id, content_hash)) as the deterministic rules validator AND loads its boards[] 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. version is the ordinal per design_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 in lineage) when the document shape changes.