693719e85f
The Review screen now has an explicit 'Lock & Build Title' step. Until the
owner locks, title/short_description stay empty (no 'ready' copy from an
unlocked AI draft). On lock the server validates the taxonomy, sets
metadata_locked=1, builds + persists the deterministic title/short_description
(owner prose wins), and returns the ready state.
- schema.sql: add metadata_locked INTEGER DEFAULT 0 to slabs
- db/__init__.py: bump _SCHEMA_SENTINELS for metadata_locked
- slabs.py: expose metadata_locked in GET; reject it in PUT (server-managed)
- review.py: POST /{id}/review action=lock path; regular review reports
stored (empty) copy until locked
- test_review.py: 8 tests covering lock gate, owner-prose-wins, stale taxonomy
- frontend/api/slabs.ts: Slab.metadata_locked, ReviewResult.locked,
ReviewInput.action, lockSlab() helper
- Review.svelte: lockMetadata() with correct commit-token ordering (bump AFTER
inner commit so the inner PUT isn't superseded), Lock button, locked banner
- e2e/m3-create.spec.ts: pare to single golden path (capture→review→lock→
ready→publish→done); mock respects optimistic concurrency (client_rev) so
stale PUTs 409 like the real backend
163 lines
5.1 KiB
SQL
163 lines
5.1 KiB
SQL
-- SlabUploader SQLite schema (S0)
|
|
-- Every table/column from docs/contracts/DATA-MODEL.md using CREATE TABLE IF NOT EXISTS
|
|
-- Taxonomy tables exist but remain EMPTY (S4 populates). Stdlib sqlite3 only.
|
|
-- Pragmas applied by db factory: journal_mode=WAL, foreign_keys=ON, busy_timeout=5000, synchronous=NORMAL
|
|
|
|
PRAGMA foreign_keys = ON;
|
|
|
|
-- 2. slabs
|
|
CREATE TABLE IF NOT EXISTS slabs (
|
|
id TEXT PRIMARY KEY,
|
|
status TEXT NOT NULL DEFAULT 'draft' CHECK(status IN ('draft','calibrated','ready','publishing','published','failed')),
|
|
sku TEXT UNIQUE CHECK(length(sku) BETWEEN 3 AND 24),
|
|
length_in REAL,
|
|
thickness_in REAL,
|
|
sqft REAL,
|
|
bdft REAL,
|
|
width_min_in REAL,
|
|
width_max_in REAL,
|
|
width_avg_in REAL,
|
|
species_id INTEGER,
|
|
wood_category_ids TEXT, -- JSON array of leaf ids
|
|
edge_type_term_id INTEGER,
|
|
figure_term_ids TEXT, -- JSON array
|
|
grade_term_id INTEGER,
|
|
thickness_term_id INTEGER,
|
|
moisture_term_id INTEGER,
|
|
fig_tag_ids TEXT, -- JSON array optional
|
|
feat_tag_ids TEXT, -- JSON array optional
|
|
price REAL,
|
|
price_source TEXT CHECK(price_source IN ('recommendation','override') OR price_source IS NULL),
|
|
title TEXT,
|
|
short_description TEXT,
|
|
description TEXT,
|
|
content_source TEXT CHECK(content_source IN ('manual','llm','template') OR content_source IS NULL),
|
|
species_confidence REAL,
|
|
figure_confidence REAL,
|
|
grade_confidence REAL,
|
|
-- #89: explicit owner lock of the confirmed taxonomy set. Deterministic
|
|
-- title/short_description are built and persisted only once this is 1.
|
|
metadata_locked INTEGER CHECK(metadata_locked IN (0,1) OR metadata_locked IS NULL),
|
|
inference_status TEXT CHECK(inference_status IN ('inferring','done','failed') OR inference_status IS NULL),
|
|
calibration_photo_id TEXT,
|
|
client_rev INTEGER NOT NULL DEFAULT 1,
|
|
server_rev INTEGER NOT NULL DEFAULT 1,
|
|
woo_product_id INTEGER,
|
|
published_at TEXT,
|
|
deleted_at TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (species_id) REFERENCES woo_categories(id),
|
|
FOREIGN KEY (calibration_photo_id) REFERENCES slab_photos(id)
|
|
);
|
|
|
|
-- 3. slab_photos
|
|
CREATE TABLE IF NOT EXISTS slab_photos (
|
|
id TEXT PRIMARY KEY,
|
|
slab_id TEXT NOT NULL,
|
|
seq INTEGER NOT NULL,
|
|
is_calibration INTEGER NOT NULL DEFAULT 0 CHECK(is_calibration IN (0,1)),
|
|
original_blob BLOB,
|
|
processed_blob BLOB,
|
|
width_px INTEGER,
|
|
height_px INTEGER,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY (slab_id) REFERENCES slabs(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- 4. pricing_rules
|
|
CREATE TABLE IF NOT EXISTS pricing_rules (
|
|
id INTEGER PRIMARY KEY,
|
|
species_id INTEGER NOT NULL UNIQUE,
|
|
price_per_bdft REAL NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (species_id) REFERENCES woo_categories(id)
|
|
);
|
|
|
|
-- 5. Taxonomy cache (empty at S0; columns chosen to support FKs + S4 sync; ambiguity resolved in learnings)
|
|
CREATE TABLE IF NOT EXISTS woo_categories (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
slug TEXT NOT NULL UNIQUE,
|
|
parent_id INTEGER,
|
|
woo_id INTEGER
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS woo_attributes (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
slug TEXT NOT NULL UNIQUE,
|
|
woo_id INTEGER
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS woo_attribute_terms (
|
|
id INTEGER PRIMARY KEY,
|
|
attribute_id INTEGER NOT NULL,
|
|
name TEXT NOT NULL,
|
|
slug TEXT NOT NULL,
|
|
woo_id INTEGER,
|
|
FOREIGN KEY (attribute_id) REFERENCES woo_attributes(id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS woo_tags (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
slug TEXT NOT NULL UNIQUE,
|
|
woo_id INTEGER
|
|
);
|
|
|
|
-- 6. settings (key/value/value_enc per §6; secrets in value_enc = nonce||ciphertext)
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT,
|
|
value_enc TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY);
|
|
|
|
-- 7. prompts + prompt_revisions (append-only revisions)
|
|
CREATE TABLE IF NOT EXISTS prompts (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL CHECK(name IN ('call1','call2')),
|
|
active_revision_id INTEGER,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (active_revision_id) REFERENCES prompt_revisions(id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS prompt_revisions (
|
|
id INTEGER PRIMARY KEY,
|
|
prompt_id INTEGER NOT NULL,
|
|
content TEXT NOT NULL,
|
|
content_hash TEXT NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
created_by TEXT,
|
|
FOREIGN KEY (prompt_id) REFERENCES prompts(id)
|
|
);
|
|
|
|
-- 8. sync_log (append-only)
|
|
CREATE TABLE IF NOT EXISTS sync_log (
|
|
id INTEGER PRIMARY KEY,
|
|
slab_id TEXT,
|
|
woo_product_id INTEGER,
|
|
status TEXT NOT NULL CHECK(status IN ('success','failed')),
|
|
payload_hash TEXT,
|
|
message TEXT,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
-- 9. inference_log (append-only audit)
|
|
CREATE TABLE IF NOT EXISTS inference_log (
|
|
id INTEGER PRIMARY KEY,
|
|
slab_id TEXT,
|
|
call INTEGER NOT NULL CHECK(call IN (1,2)),
|
|
prompt_revision_id INTEGER,
|
|
model TEXT,
|
|
request_payload TEXT,
|
|
response_payload TEXT,
|
|
latency_ms INTEGER,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY (prompt_revision_id) REFERENCES prompt_revisions(id)
|
|
);
|