Files
SlabUploader/docs/contracts/DATA-MODEL.md
T
grokbot_ty 85786f16f2 fix(review): slug-scoped tags, decode names, drop fig picker & AI labels
- Taxonomy sync keeps fig-/feat- by slug (hyphen), not name; HTML-unescape names
- Review: remove AI assist labels; Features picker for feat-*; derive fig_tag_ids
  from Figure term names (casefold match, no bridges); decodeName on display
- Unit tests for scoped_tags, fig match, Review UI contract; docs slug wording
2026-09-26 09:41:30 -07:00

8.0 KiB
Raw Blame History

Data model contract

Status: SPEC · 2026-09-18. Store: SQLite.

Requirements impact

Requirement Impact on this design
APP-5 slab status enum and transitions
APP-20 ids, timestamps, revisions
APP-40 derived numbers stored, never recomputed
CAPTURE-25, CAPTURE-30, CAPTURE-70, CAPTURE-80, CAPTURE-85, CAPTURE-90, CAPTURE-95, CAPTURE-105, CAPTURE-115, CAPTURE-130, CAPTURE-135, CAPTURE-160 slab and photo columns, units, PNG
METADATA-5, METADATA-10, METADATA-15, METADATA-30 taxonomy cache tables and slab taxonomy columns
METADATA-35, METADATA-45, METADATA-55, METADATA-66, METADATA-105, METADATA-106 inference columns, prompts tables, logs, revision interface
PUBLISH-25, PUBLISH-30 purge on success, append-only log
REVIEW-10, REVIEW-25, REVIEW-45, REVIEW-50 prose, price, SKU, field limits
SETTINGS-5, SETTINGS-10, SETTINGS-20, SETTINGS-25, SETTINGS-30, SETTINGS-35, SETTINGS-50, SETTINGS-51, SETTINGS-55, SETTINGS-65, SETTINGS-90 settings, crypto, rates, taxonomy stamps

1. Conventions

  • Slab ids are client-generated UUIDv4; all other ids are integer autoincrement.
  • Timestamps are UTC ISO-8601 text.
  • Slab status is one of draft, calibrated, ready, publishing, published, failed.
  • Inches are stored as the rounded 1/8-inch value the user entered.
  • sqft and bdft are stored to 2 decimals half-up.
  • price is stored to 2 decimals half-up.
  • Multi-select taxonomy values are JSON arrays of Woo ids.
  • Secrets are stored as value_enc = nonce(12) || ciphertext, AES-GCM; the master key is the env SLAB_AES_KEY and is never in the DB.
  • On successful publish the local working slab row is discarded (photos purged, device draft cleared); sync_log and inference_log are append-only and retained. Nothing is deleted from WooCommerce.
  • Soft delete sets deleted_at (text, null until then) on unpublished drafts; deleted rows are excluded from all GET responses.
  • Photo bytes live in slab_photos BLOBs; the /images volume is not a second store. It holds a health-probe file and short-lived staging for 1024 px Call-1 JPEGs and Woo media uploads, purged after use.
  • The store is SQLite through the stdlib sqlite3 module and a checked-in schema.sql (CREATE TABLE IF NOT EXISTS); there is no ORM and no migration framework. Pragmas are journal_mode=WAL, foreign_keys=ON, busy_timeout, synchronous=NORMAL, with one process-wide connection behind a lock. The database path and image directory are explicit so a test runs against isolated storage.

2. slabs

Column Type Constraint / default
id text PK, client UUIDv4
status text enum, default draft
sku text 3–24, alphanumeric and hyphen; unique locally
length_in real 1/8-inch step
thickness_in real 1/8-inch step
sqft real 2 dp
bdft real 2 dp
width_min_in, width_max_in, width_avg_in real 6-inch stations
species_id int FK woo_categories, one leaf
wood_category_ids json one or more leaf ids
edge_type_term_id int one term
figure_term_ids json one or more terms
grade_term_id int exactly one term
thickness_term_id int round-up band term
moisture_term_id int default kiln-dried term
fig_tag_ids json optional
feat_tag_ids json optional
price real 2 dp
price_source text recommendation | override
title, short_description, description text required non-empty; title warn >60, short_description warn >160
content_source text manual | llm | template
species_confidence, figure_confidence, grade_confidence real null below 0.7
inference_status text null | inferring | done | failed
calibration_photo_id text FK slab_photos
client_rev, server_rev int default 1
woo_product_id int null until publish
published_at text null until publish
deleted_at text null until soft-deleted; unpublished drafts only
created_at, updated_at text UTC ISO-8601

No inference_thread_id column exists; provider threads are not used.

3. slab_photos

Column Type Constraint
id text PK, UUID
slab_id text FK slabs
seq int capture order
is_calibration int (bool) exactly one true per slab
original_blob blob retained while draft/retry
processed_blob blob transparent PNG, alpha preserved
width_px, height_px int processed dimensions
created_at text UTC ISO-8601

The calibration photo exists; other batch photos are visual inventory only. Rows are purged on successful publish.

4. pricing_rules

Column Type Constraint
id int PK
species_id int FK woo_categories, UNIQUE
price_per_bdft real flat, no tiers
created_at, updated_at text UTC ISO-8601

5. Taxonomy cache

Cites METADATA-5, SETTINGS-50, SETTINGS-51, SETTINGS-55.

  • woo_categories — species and wood-category leaves only; parents group and are never assigned.
  • woo_attributes / woo_attribute_terms — exactly the five locked attributes Edge Type, Figure, Grade, Thickness, Moisture, with their terms.
  • woo_tags — only tags whose slug starts with fig- or feat-; other store tags are ignored.

Rebuild is one transaction; on failure the previous cache is kept.

6. settings

Key Storage Default
woo_wp_username plain —
woo_app_password value_enc —
inference_base_url value_enc —
inference_api_key value_enc —
inference_model plain —
inference_enabled plain false
content_llm_enabled plain false
brand_voice plain —
geo_context plain —
woo_create_status plain draft
taxonomy_last_sync_at plain —
taxonomy_anchor plain —

WOO_BASE_URL is not a settings row; it is env, HTTPS only, read-only.

7. prompts and prompt_revisions

docs/contracts/PROMPTS.md owns the prompt structure, the seed text, and the revision interface; this section owns the storage shape (METADATA-105, METADATA-106).

  • prompts: id, name (call1 | call2), active_revision_id FK, created_at, updated_at.
  • prompt_revisions: id, prompt_id FK, content text, content_hash text, created_at, created_by.

Revisions are append-only: a save inserts a new row and moves active_revision_id; a row is never updated or deleted, so reset-to-revision and reset-to-default are pointer moves. Each prompt has a revision 0 seeded on first run from the seed text in docs/contracts/PROMPTS.md §6.

inference_log.call records which call ran, and inference_log.prompt_revision_id records the revision it used (METADATA-66).

8. sync_log

Cites PUBLISH-25, PUBLISH-30.

Append-only; never deleted on publish. Columns: id, slab_id, woo_product_id, status (success | failed), payload_hash (sha256 of the outbound payload), message, created_at.

9. inference_log

Append-only audit. Columns: id, slab_id, call (1 | 2), prompt_revision_id FK, model, request_payload, response_payload, latency_ms, created_at.

request_payload is auditable JSON of the model request without media bytes (METADATA-66). Text parts (active prompt, LISTS / fact block, SKU/dims) are stored in full. Each vision attachment is an image_ref (photo_id, seq, bytes, optional infer_long_edge_px, label) pointing at slab_photos — never a data:image/...;base64,... URL. The wire request to the inference API still includes images; only the log is redacted. A byte cap remains as a safety net after redaction; normal Call 1/2 rows are far under it.

10. Status transitions

Cites APP-5.

draft → calibrated → ready → publishing → published, with failed reachable from any step and retryable. calibrated is set when the draft uploads; ready requires the full set.