Skip to content

PDR-011: Declarative configurator — spec, examples + working prototype - #32

Draft
kas1987 wants to merge 5 commits into
mainfrom
claude/zelex-config-ui-builder-rso32
Draft

PDR-011: Declarative configurator — spec, examples + working prototype#32
kas1987 wants to merge 5 commits into
mainfrom
claude/zelex-config-ui-builder-rso32

Conversation

@kas1987

@kas1987 kas1987 commented Jun 6, 2026

Copy link
Copy Markdown
Owner

What this is

A research-backed PDR + declarative spec + worked examples + a working live prototype for a "Build-a-ZELEX" configurator. The idea: a UI generated from a data spec (not hand-coded), where a buyer's selections form a JSON build document that resolves — through declared rules — to a catalogued body architecture, a SKU, a measurement signature, a Body Family, and a price (or an honest bespoke inquiry).

Files

  • db/configurator_schema.json — the declarative spec the UI is generated from: body / head / component axes, derivations (resolve body_code, classify family, resolve SKU + price), validation rules, and a build_document_schema. Option domains are extracted from existing db/ data; large sets (163 heads) are referenced by source, not inlined.
  • db/configurator_examples.json — three strong-data bodies reverse-engineered into build documents with truthful resolved values (transcribed from db/):
    • ZG170C (I-Series Muse, exact) → GE52_2+ZG170C-Tan @ $3599 (+ Movable-Jaw alt)
    • ZK168B (K-Series Muse, loose — off every family box) → KE03_1+ZK168B-1 @ $3869.10
    • ZX172E (SLE Icon, exact — exercises TPE + skeleton-version SKU suffix) → ZXE215_1+ZX172E-Tan-Sle3.0 @ $2000
    • a bespoke case that fails honestly (body_code:null → inquiry path)
  • configurator.html — a working prototype: a generic renderer that walks groups[].axes[] to emit the kit's controls, and a resolver that runs the four derivations + validation rules live. Reuses assets/site.css tokens and ZX helpers (no new design system); cross-links to the existing cast on the resolved architecture. Resolver logic was sanity-checked against db/ for all three examples + the bespoke case.
  • docs/pdr/PDR-011-declarative-configurator.md — method, data grounding, the reversible derivation core, validation, scope, and acceptance review.
  • .github/scripts/validate-site.mjs — adds configurator.html to the validated KIT_PAGES.

Key design decisions

  • Catalog is truth. Body axes (line/height/cup) resolve to an existing body_code, not free composition — the configurator can't manufacture a body that doesn't exist, protecting the measurement-documented positioning.
  • Inputs vs. derived are strictly separated. Build docs store only choices; family/WHR/BWR/SKU/price are recomputed, never trusted from input.
  • Honest provenance. Every axis is tagged catalog / spec-card / derived / proposed; not-yet-in-db/ options (standing feet, articulated fingers) are flagged proposed.
  • Stored classification wins over live re-derive at family boundaries (see the ZK168B loose case).

Next step (not in this PR)

Backfilling the proposed axes in db/, an image gallery head picker, a build_configurator.py pre-resolve step, and adding the page to the global nav.

Validation

All new/changed JSON parses; configurator.html passes validate-site.mjs (inline script parses + references the shared kit); full site check green locally.

https://claude.ai/code/session_01JFmZDEboYdpgMU11kWyqzB

…xamples

Author PDR-011 proposing a data-driven "Build-a-ZELEX" configurator: a UI
generated from a spec rather than hand-coded, where buyer selections form a
JSON build document that resolves to a catalogued architecture, SKU, signature,
family, and price.

- db/configurator_schema.json: declarative axis model (body/head/components),
  derivations, validation rules, and a build-document JSON Schema. Option
  domains are extracted from existing db/ data; large sets referenced by source.
- db/configurator_examples.json: three strong-data bodies (ZG170C, ZK168B,
  ZX172E) reverse-engineered into build documents with truthful resolved values,
  plus a bespoke failure case.
- docs/pdr/PDR-011-declarative-configurator.md: method, data grounding, rules,
  and acceptance review.

No live UI yet — proves the model round-trips real bodies before building it.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a declarative configurator specification for ZELEX, including a data-driven schema, worked examples, and a detailed PDR. The code review identifies a few issues to resolve: correcting family premiums in the schema to align with db/body_profiles.json, fixing a logical inconsistency in the SLE skeleton version example where the delta unexpectedly alters the head and skin tone, and updating references to the non-existent family_taxonomy.json file to point to db/body_profiles.json instead.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

"rule": "If resolved.sku matches a live variant, use its price (authoritative). Otherwise estimate: base_by_line × material_factor × (1 + family_premium). Mark estimated:true.",
"pricing_model": {
"base_by_line_usd": { "SLE 3.0": 1800, "Fusion": 3200, "I-Series": 3400, "K-Series": 3600 },
"material_factor": { "Silicone": 1.0, "TPE": 0.85 },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a discrepancy between the family premiums defined here and those in db/body_profiles.json. Specifically:

  • The Empress is set to 0.30 (30%) here, but is +40% (40%) in db/body_profiles.json (line 70).
  • The Sculpt is set to 0.25 (25%) here, but is +30% (30%) in db/body_profiles.json (line 84).

Since the note states that these premiums should mirror db/body_profiles.json, they should be updated to match.

Suggested change
"material_factor": { "Silicone": 1.0, "TPE": 0.85 },
"family_premium": { "The Classic": 0.20, "The Muse": 0.25, "The Icon": 0.30, "The Siren": 0.35, "The Empress": 0.40, "The Sculpt": 0.30 },

Comment on lines +83 to +119
"id": "zxe215-zx172e",
"title": "SLE Icon — ZX172E (ZXE215_1 cast)",
"why_chosen": "Exercises the SLE-only axes (TPE material, skeleton_version) and the version/tone SKU suffix; exact-confidence Icon. Shows the same architecture priced two ways across skeleton versions.",
"build": {
"spec_version": "1.0.0",
"body": { "line": "SLE 3.0", "height_cm": 172, "cup": "E", "material": "TPE", "skeleton_version": "SLE 3.0" },
"head": { "head_code": "ZXE215_1", "face_variant": "Standard", "head_tone_match": true },
"components": { "skin_tone": "Tan", "breast_type": "Standard", "standing_feet": false, "fingers": false }
},
"resolved": {
"body_code": "ZX172E",
"family": "The Icon",
"family_confidence": "exact",
"WHR": 0.608,
"BWR": 1.504,
"signature": {
"upper_bust": 86.5, "under_bust": 67, "waist": 57.5, "hip": 94.5,
"neck": 32.5, "shoulder_width": 38, "arm_length": 70.5, "hand_length": 19,
"leg_length": 84.5, "foot_length": 21, "thigh_circ": 48.5, "calf_circ": 34,
"weight_kg": 35.7
},
"sku": "ZXE215_1+ZX172E-Tan-Sle3.0",
"price": "2000.00",
"price_basis": "catalogued live variant (authoritative)",
"estimated": false
},
"skeleton_version_alt": {
"note": "Same body, head, tone, but SLE 2.0 skeleton resolves a separate, lower-priced SKU — proving skeleton_version is a real price axis, not cosmetic.",
"build_delta": { "body.skeleton_version": "SLE 2.0" },
"resolved_sku": "ZXE203_W1+ZX172E-White-Sle2.0",
"resolved_price": "1600.00"
},
"source": {
"signature": "db/body_measurements.json#/bodies/ZX172E",
"family_whr_bwr": "db/body_profiles.json#/profiles (ZX172E)",
"sku_price": "db/live_variants.json (body=ZX172E, head=ZXE215_1, tone=Tan, Sle3.0)"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a logical inconsistency in the zxe215-zx172e example. The skeleton_version_alt block specifies a build_delta that only changes body.skeleton_version to "SLE 2.0". However, the resolved_sku is listed as "ZXE203_W1+ZX172E-White-Sle2.0", which also changes the head code (from ZXE215_1 to ZXE203_W1) and the skin tone (from Tan to White).

To make this a pure skeleton version delta, we can use a head/tone combination that actually exists in both SLE 2.0 and SLE 3.0 in the catalog, such as ZXE201_4 (Ulrica cast) or ZXE204_1 (Arin cast). Using ZXE201_4 allows the delta to resolve cleanly to "ZXE201_4+ZX172E-Tan-Sle2.0" without changing other axes.

      "id": "zxe201-zx172e",
      "title": "SLE Icon — ZX172E (ZXE201_4 cast)",
      "why_chosen": "Exercises the SLE-only axes (TPE material, skeleton_version) and the version/tone SKU suffix; exact-confidence Icon. Shows the same architecture priced two ways across skeleton versions.",
      "build": {
        "spec_version": "1.0.0",
        "body":   { "line": "SLE 3.0", "height_cm": 172, "cup": "E", "material": "TPE", "skeleton_version": "SLE 3.0" },
        "head":   { "head_code": "ZXE201_4", "face_variant": "Standard", "head_tone_match": true },
        "components": { "skin_tone": "Tan", "breast_type": "Standard", "standing_feet": false, "fingers": false }
      },
      "resolved": {
        "body_code": "ZX172E",
        "family": "The Icon",
        "family_confidence": "exact",
        "WHR": 0.608,
        "BWR": 1.504,
        "signature": {
          "upper_bust": 86.5, "under_bust": 67, "waist": 57.5, "hip": 94.5,
          "neck": 32.5, "shoulder_width": 38, "arm_length": 70.5, "hand_length": 19,
          "leg_length": 84.5, "foot_length": 21, "thigh_circ": 48.5, "calf_circ": 34,
          "weight_kg": 35.7
        },
        "sku": "ZXE201_4+ZX172E-Tan-Sle3.0",
        "price": "2000.00",
        "price_basis": "catalogued live variant (authoritative)",
        "estimated": false
      },
      "skeleton_version_alt": {
        "note": "Same body, head, tone, but SLE 2.0 skeleton resolves a separate, lower-priced SKU — proving skeleton_version is a real price axis, not cosmetic.",
        "build_delta": { "body.skeleton_version": "SLE 2.0" },
        "resolved_sku": "ZXE201_4+ZX172E-Tan-Sle2.0",
        "resolved_price": "1600.00"
      },
      "source": {
        "signature": "db/body_measurements.json#/bodies/ZX172E",
        "family_whr_bwr": "db/body_profiles.json#/profiles (ZX172E)",
        "sku_price": "db/live_variants.json (body=ZX172E, head=ZXE201_4, tone=Tan, Sle3.0)"
      }

"id": "classify_family",
"produces": "body.family",
"inputs": ["signature.waist", "signature.hip", "signature.upper_bust"],
"rule": "WHR = waist/hip; BWR = upper_bust/waist; choose the family whose whr_range and bwr_range both contain the point (db/body_profiles.json#/families). On no exact containment, pick nearest range-center and label confidence near|loose (see db/family_taxonomy.json#/confidence_labels).",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The rule references db/family_taxonomy.json for confidence labels, but this file does not exist in the repository or the data_sources mapping. The confidence labels (exact, near, loose) are actually defined and used in db/body_profiles.json. This reference should be updated to avoid confusion.

Suggested change
"rule": "WHR = waist/hip; BWR = upper_bust/waist; choose the family whose whr_range and bwr_range both contain the point (db/body_profiles.json#/families). On no exact containment, pick nearest range-center and label confidence near|loose (see db/family_taxonomy.json#/confidence_labels).",
"rule": "WHR = waist/hip; BWR = upper_bust/waist; choose the family whose whr_range and bwr_range both contain the point (db/body_profiles.json#/families). On no exact containment, pick nearest range-center and label confidence near|loose (see db/body_profiles.json).",

- **`classify_family`** — `WHR = waist/hip`, `BWR = upper_bust/waist`; pick the
family whose ranges contain the point (`body_profiles.json#/families`); on no
containment, nearest-center with `near`/`loose` confidence
(`family_taxonomy.json`). **Prefer the stored classification** over a live

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The PDR references family_taxonomy.json for confidence labels, but this file does not exist in the repository. The confidence labels are defined in body_profiles.json. This reference should be updated to body_profiles.json.

Suggested change
(`family_taxonomy.json`). **Prefer the stored classification** over a live
(body_profiles.json). **Prefer the stored classification** over a live

Add a live "Build a ZELEX" configurator generated entirely from
db/configurator_schema.json: a generic renderer walks groups[].axes[] to emit
the kit's controls, and a resolver runs the four declared derivations + the
validation rules in real time. Selections resolve to a body_code, family,
measurement signature, SKU, and price — or an honest bespoke inquiry — and the
page cross-links to the existing cast on the resolved architecture.

- configurator.html: reuses assets/site.css tokens and ZX helpers (famColor,
  charCard, esc, nav/footer); no new design system. Resolver logic verified
  against db/ for ZG170C, ZK168B, ZX172E, and the bespoke ZK175K case.
- validate-site.mjs: add configurator.html to KIT_PAGES so CI checks it.
- Fix family_confidence for ZK168B: 'loose' (stored value), not 'near' — Muse
  contains neither axis in range. Corrected in examples + PDR.
- PDR-011: status → Implemented; scope/acceptance updated to include the prototype.
@kas1987 kas1987 changed the title PDR-011: Declarative configurator — spec + reverse-engineered examples PDR-011: Declarative configurator — spec, examples + working prototype Jun 6, 2026
claude added 3 commits June 6, 2026 17:44
…pathway

Heads were the one axis with no structured model. Add scripts/build_heads.py
that reverse-engineers the 189 head sculpts from products + variants + characters
+ movable-jaw overrides into db/heads.json (one record per sculpt: line,
neck_class, MJ availability, skin tones, paired bodies, cast, representative
image, price range). Mirrors body_measurements.json.

Wire the configurator head picker to that data:
- head_code now renders as an image gallery (ranked: pairs-this-body first,
  then cast/imaged); Movable Jaw gated to heads that actually offer it; resolve
  panel shows the chosen head thumbnail. Falls back to a dropdown if heads.json
  is absent.

Head↔line coupling + interchangeability: the catalog sells heads line-locked
(every head pairs one line), but the physical constraint is the neck connector,
not the line. Spec-card necks cluster into two classes — slim (~27cm, K-Series)
vs standard (~31-33cm, Fusion/I-Series/SLE). Add an "Interchangeable heads
(cross-line)" toggle: on, the gallery shows every neck-compatible head across
lines (cross-line ones flagged), a cross-line pick resolves to a custom/bespoke
inquiry (never a fake SKU), and mismatched neck classes are blocked. Verified:
no slim/standard leakage; standard body opens 186 heads, K-Series only 3.

PDR-011 §8 documents the coupling + pathway; README pipeline + acceptance updated.
Neck data (interchangeability stops being a buried constant):
- scripts/build_neck_compat.py → db/neck_compatibility.json: the single source of
  truth for neck classes. Explicit per-body neck_class, the classes/compatibility
  matrix, and the threshold in one reviewable place. Each body carries a
  neck_joint_mm slot (null) for the TRUE connector spec — fill it to replace the
  circumference-derived estimate with no code change.
- build_heads.py imports the shared threshold/helpers from build_neck_compat
  (one definition, not two). configurator.html reads body neck_class from the
  data file (runtime calc is only a fallback) and uses the compatibility matrix
  for fit/validation instead of strict equality.

Face & Finish (honest proposed layer):
- No structured face-feature data exists in db/ (the only real per-head face
  attribute is movable-jaw, already modelled). So rather than fabricate per-head
  eye colours, add a Face & Finish group of provenance:proposed axes (eye colour,
  makeup, eyebrows, wig, freckles) — global options that compose as a custom
  finish (no SKU impact) and surface in the resolve panel.
- heads.json gains a face_features slot (null) per head as the explicit backfill
  target; populate it to make these axes head-aware.
- configurator state generalised to one bucket per schema group.

PDR-011 §8/§9 + README pipeline + acceptance updated.
Add scripts/build_data_request.py → docs/data-requests/ (re-runnable): fill-in
CSV templates + cover README for the two data points that close the
estimate/proposed gaps.

- neck-joint-request.csv (21 bodies): neck_joint_diameter_mm + type to replace
  the circumference-derived neck_class.
- head-features-request.csv (189 heads, sorted priority-1 = has cast): per-head
  eye/makeup/eyebrows/wig/freckles defaults + availability for the face_features
  slot.

Each row is pre-filled with known context; returned values load directly into
db/neck_compatibility.json and db/heads.json with no schema change. PDR-011 §9
points to the templates.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants