feat(backend): S0 foundation (schema + stdlib sqlite3 db factory + health/meta + JSON logs)

- backend/db/schema.sql: all 12 tables from DATA-MODEL.md (taxonomy empty)
- backend/db: stdlib sqlite3 factory, WAL/FK/busy_timeout/synchronous pragmas,
  single locked connection, explicit SLAB_DB_PATH/SLAB_IMAGES_DIR
- backend/app.py: /api/health (non-200 on storage not writable), /api/v1/meta,
  JSON logging with request-id threading + secret redaction, startup guards
  (SETTINGS-10 WOO_BASE_URL https, SETTINGS-25 SLAB_AES_KEY)
- tests: schema-loads, health happy/failure, startup guards (TDD)
- docs/contracts: add requirement-ID citations to 12 sections so
  check_requirements.py passes (pre-existing gate debt)

Gates: check_requirements.py + check_coverage.py exit 0; pytest 5 passed
This commit is contained in:
2026-09-19 15:34:15 -07:00
parent 9b181a937b
commit 0eb2b0f697
12 changed files with 592 additions and 6 deletions
+6 -4
View File
@@ -75,7 +75,7 @@ There is no `412 validation_module_stale` path.
Multipart create of a calibrated draft. Returns `201` with the slab at `status: "calibrated"`.
Stores the client-sent derived numbers and never recomputes them. Auto-runs Call 1 only when
`inference_enabled` is true; inferred values persist only at confidence ≥ 0.7. `409 duplicate_sku`
if the SKU already exists.
if the SKU already exists. Cites APP-20, CAPTURE-135, METADATA-45, PUBLISH-15.
`SlabCreate` fields:
@@ -116,7 +116,7 @@ Partial update of editable fields plus `client_rev`. Stores client-sent derived
not recompute them. Checks run in order: `409 revision_conflict`, then `409 duplicate_sku` (SKU
field actions `edit_sku` / `open_existing`), then `422 stale_field` (current options included).
`status` becomes `ready` only when the full required set is present; it is server-derived and a
client never sets it.
client never sets it. Cites APP-20, REVIEW-40, PUBLISH-15, PUBLISH-20.
### `DELETE /api/v1/slabs/{id}`
Soft delete of unpublished drafts only (sets `deleted_at`); `204`. Deleted rows are excluded from
@@ -141,7 +141,7 @@ Runs Call 2 on user action only. Fresh request carrying the processed photos, th
metadata, the primary and secondary wood categories, the parent-category path, the dimension fact
block, and brand/geo context; no provider thread id and no raw Call 1 result. Requires
`test-inference` `ok`. Returns `202`; the client polls. The model writes the long description only
and never touches dimensions.
and never touches dimensions. Cites METADATA-55, METADATA-65.
Call 1 result fields: `species_id`, `species_confidence`, `wood_category_ids`,
`edge_type_term_id`, `figure_term_ids`, `figure_confidence`, `grade_term_id`, `grade_confidence`,
@@ -158,7 +158,7 @@ Preconditions are the full ready set, else `422 unpublishable` with field keys.
the ready set; look up the SKU and stop if present; refresh taxonomy when the last sync is over
one hour old; resolve categories, tags, and attributes; upload the processed PNGs to Woo media;
`POST /products` per `docs/contracts/WOO-API.md`; finalize. `SLAB-UAT-*` always creates a draft.
Returns `202`; the client polls.
Returns `202`; the client polls. Cites PUBLISH-5, PUBLISH-10, PUBLISH-15, PUBLISH-25.
- Success: `woo_product_id` set and returned on the terminal poll, photos purged, the local working
slab row discarded, device draft cleared. Nothing is deleted from WooCommerce.
@@ -187,6 +187,8 @@ One flat `price_per_bdft` per species leaf, no tiers.
## 10. Settings
Cites SETTINGS-15, SETTINGS-20, SETTINGS-25, SETTINGS-80, SETTINGS-85.
- `GET /api/v1/settings` → `SettingsView` with secrets masked.
- `PUT /api/v1/settings` — partial update. `woo_base_url` is not writable; the server rejects the
write. Woo credentials are write-only and stored AES-GCM. Inference base URL and key are