Files
SlabUploader/docs/contracts/DATA-MODEL.md
T
Ty f0f891f80e docs(m3): add the prompt revision interface, seed, and auth gap
UAT requires prompt tuning, so the iteration system is M3; only the editing
UI is deferred.

- METADATA-105 now covers storage, append-only revisions, and recording the
  revision used.
- METADATA-106 adds the REST revision interface (append, activate, read); no
  prompt UI.
- METADATA-107 records the missing authentication as a later row; there is no
  M4 yet, so the PoR carries it.
- PROMPTS.md owns the seed text as revision 0, the seed mechanism, and the
  interface; sections renumbered after inserting them.
- DATA-MODEL.md §7 cites PROMPTS.md, adds content_hash, and states the
  append-only rule.
- task_breakdown.md notes the S7 interface and the deferred auth.

155 requirements; both gates pass.
2026-09-19 12:29:08 -07:00

163 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
- `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 the fig-* and feat-* families; 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`
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`.
## 10. Status transitions
`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.