Commit Graph

4 Commits

Author SHA1 Message Date
Ty 693719e85f fix(review): #89 explicit metadata lock — deterministic copy on lock, not on review
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
2026-09-20 23:36:31 -07:00
Ty e36979617b fix(m3): uuid secure-context fallback + middle-tier SKU business rules
- frontend: uuid() helper (crypto.randomUUID -> getRandomValues) for plain-HTTP LAN UAT
- slabs.py: empty SKU at create -> NULL (no-SKU-yet is transient, filled in Review)
- review.py: ready-gate enforces REVIEW-45 (SKU 3-24 alnum+hyphen) in the middle tier
- DB CHECK stays as backstop only (referential/structural), per layering principle
2026-09-20 21:15:29 -07:00
Ty c8d6d5eda0 fix(backend): create accepts capture-only payload; fix INSERT binding count
create_slab required ALL ALLOWED_FIELDS present, but the Review screen fills
taxonomy/content after create (CREATE.md §1). Now requires only the capture
fields (id, sku, dims, widths); the rest stay nullable for Review.

Also fixed a pre-existing off-by-one: vals re-filtered cols by 'in data',
dropping the 6 server-managed columns and under-counting bindings (19 vs 18).
Split allowed_cols/server_cols so vals always matches cols.

Verified: 9/9 slabs tests pass; live POST /slabs with the app's capture-math
payload returns 201.
2026-09-20 14:45:32 -07:00
Ty cc5241f7da feat(slabs): S2 CRUD + optimistic concurrency + soft delete + slab list UI
- backend/slabs.py: create (calibrated, client-derived stored as sent), list (excl
  soft-deleted, most recent), get, update (single compare-and-set on server_rev,
  409 revision_conflict / 409 duplicate_sku / 422 stale_field, status server-derived),
  delete (soft, published rejected). Explicit column allowlists, parameterized SQL,
  JSON array fields, commits.
- backend/app.py: wire slabs router.
- frontend: src/api/slabs.ts client + create/slabs/SlabList.svelte + dev toggle.
- tests: 9 slabs tests (create/dup/list/get/update/rev-conflict/dup-sku/soft-delete/
  published-delete) with full taxonomy seeding.

Verified: pytest 23 passed; check_requirements + check_coverage exit 0; npm build ok.
2026-09-19 16:29:06 -07:00