85786f16f2
- 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
209 lines
26 KiB
Markdown
209 lines
26 KiB
Markdown
# Requirements
|
||
|
||
Normative source of truth. One row per requirement. Enforced by `tools/check_requirements.py`.
|
||
|
||
A requirement is defined here and nowhere else. Capability and contract docs cite IDs; they do not restate them.
|
||
|
||
Format and rules: `docs/STRUCTURE.md`.
|
||
Scope: `shipped` | `M3` | `later`.
|
||
`Test` is a test path, a requirement ID, or `—` (traceability gap). A path given for a `shipped` requirement must exist.
|
||
|
||
Sources reconciled 2026-09-18 from the current branch docs plus the `pre-simplification` tag; decisions recorded in `.audit/2026-09-18-reconciliation-decisions.md`.
|
||
|
||
## CAPTURE — ecommerce-ready capture
|
||
|
||
| ID | Requirement | Scope | Test |
|
||
|---|---|---|---|
|
||
| CAPTURE-5 | Mask, measure, crop, and PNG output are a deterministic function of photo pixels and user inputs, with no inference, model, or network in the math. | shipped | — |
|
||
| CAPTURE-10 | Supported clients are iOS Safari and Android Chrome, plus desktop Chrome, Edge, and Firefox; unsupported clients get a hard stop, and there are no polyfills. | shipped | — |
|
||
| CAPTURE-15 | Capture is online only; offline capture is deferred. | later | — |
|
||
| CAPTURE-20 | Photos are taken on a green or black sheet with the slab fully on the sheet; the first photo is top-down with the lens parallel to the broad face. | shipped | — |
|
||
| CAPTURE-25 | A batch of one or more photos of one slab is uploaded, and the owner selects which photo is the calibration photo. | shipped | — |
|
||
| CAPTURE-30 | The calibration photo drives mask, length axis, measure, and crop; other batch photos reuse the approved sheet, sensitivity, and crop rules. | shipped | — |
|
||
| CAPTURE-35 | Sheet detection samples a 2 to 5 percent strip on all four borders and classifies green or black by HSV; otherwise the owner picks the sheet. | shipped | frontend/src/mask/sheetKeep.test.ts |
|
||
| CAPTURE-40 | Mask controls are exactly Sheet (auto, green, black) and Sensitivity (0 to 100, step 1); Edge offset and Feather are removed. | shipped | — |
|
||
| CAPTURE-41 | Sensitivity 0 to 100 maps to green-sheet chroma tightness and black-sheet brightness cutoff in the worker; both widen as sensitivity rises. | shipped | frontend/src/mask/sheetKeep.test.ts |
|
||
| CAPTURE-45 | A Sensitivity change debounces 2000 ms; a Sheet change runs immediately; the overlay recomputes in a worker and the last good overlay stays visible. | shipped | frontend/src/capture/session.calibrate.test.ts |
|
||
| CAPTURE-50 | The mask overlay renders on the source photo and the owner approves it; approval is blocked when the mask has no slab pixels. | shipped | — |
|
||
| CAPTURE-55 | The mask must be a single contiguous slab contour; otherwise the resolution is a retake. | shipped | — |
|
||
| CAPTURE-60 | The length axis is the long side of the minimum-area bounding rectangle of the mask; the owner rotates it to the prevailing length and confirms. | shipped | frontend/src/geometry/rect.test.ts |
|
||
| CAPTURE-65 | On a nearly square slab the owner chooses which axis is length. | shipped | — |
|
||
| CAPTURE-70 | The owner enters length and thickness in 1/8 inch steps. | shipped | frontend/src/capture/measureInputs.test.ts |
|
||
| CAPTURE-75 | scale equals longest_axis_px divided by measured length, where longest_axis_px is the mask span end to end along the approved axis, not the rectangle edge. | shipped | — |
|
||
| CAPTURE-80 | Widths sample every 6 inches perpendicular to the approved axis, dropping the first and last 6 inches; min, max, and average are reported; if length is 12 inches or less all three are zero. | shipped | frontend/src/measure/measure.test.ts |
|
||
| CAPTURE-85 | sqft equals mask_pixels divided by scale squared divided by 144, rounded half-up to 2 decimals. | shipped | frontend/src/measure/measure.test.ts |
|
||
| CAPTURE-90 | bdft equals sqft times thickness_in, rounded half-up to 2 decimals, with no divide-by-12; 1 sqft at 1 inch equals 1 bdft. | shipped | frontend/src/measure/measure.test.ts |
|
||
| CAPTURE-95 | The inventory PNG is 8-bit RGBA with alpha preserved through encode and upload. | shipped | frontend/src/png/inventory.test.ts |
|
||
| CAPTURE-100 | Crop is 3:4 portrait, slab centered, 90 percent fill at the widest and tallest extremes, on a transparent background. | shipped | frontend/src/png/inventory.test.ts |
|
||
| CAPTURE-105 | After crop the shorter side targets 800 px when the source allows; warn (still encode/include) when 300–799 px; hard-block below 300 px; never upscale. | shipped | frontend/src/png/inventory.test.ts |
|
||
| CAPTURE-110 | A non-calibration photo that yields an empty mask or fails the size floor is excluded with a per-photo error while the rest proceed. | shipped | frontend/src/capture/session.batchApply.test.ts |
|
||
| CAPTURE-115 | Scale, widths, sqft, and bdft stay tied to the calibration photo; non-calibration photos are visual inventory only. | shipped | frontend/src/capture/session.batchApply.test.ts |
|
||
| CAPTURE-120 | The dimension overlay is black and oriented to the approved length axis, with the length graphic beside the wood on the right in axis space. | shipped | frontend/src/png/overlay.test.ts |
|
||
| CAPTURE-125 | Overlay width stations run every 12 inches starting 6 inches from the bottom while station is at most length minus 6; the bottom end follows the calibration photo EXIF orientation. | shipped | frontend/src/png/overlay.test.ts |
|
||
| CAPTURE-130 | The photos-ready lineup order is clean calibration PNG, dimension-overlay copy, then remaining batch photos in capture order. | shipped | frontend/src/capture/photosReadyLineup.test.ts |
|
||
| CAPTURE-135 | The client computes all deterministic math; the server stores the client numbers and never recomputes them as source of truth. | shipped | — |
|
||
| CAPTURE-140 | Load-bearing client APIs are image decode, canvas or OffscreenCanvas, a worker, PNG encode, IndexedDB, and fetch; no OpenCV, ONNX, or U2Net on the shipped path. | shipped | — |
|
||
| CAPTURE-141 | The preview is display-only: it fits the device width with a height cap so controls stay visible, and mask, rectangle, crop, scale, sqft, and PNG encode use source photo pixels, never the preview's CSS size. | shipped | frontend/src/capture/fitDisplay.test.ts |
|
||
| CAPTURE-145 | Capture knob preferences persist in localStorage until reset to default. | shipped | — |
|
||
| CAPTURE-150 | Mask tuning has no apply step and no server round trip. | shipped | — |
|
||
| CAPTURE-155 | Failures such as a broken mask or undersized source never silently guess; the resolution is retake. | shipped | — |
|
||
| CAPTURE-160 | Length is stored and displayed to the nearest 1/8 inch, rounded values are what is stored, and sqin appears nowhere in the UI, tests, or copy. | shipped | — |
|
||
| CAPTURE-165 | U2Net on-demand server matting is deferred. | later | — |
|
||
|
||
## METADATA — listing attributes and copy
|
||
|
||
The listing is built in two inference calls, both optional. Call 1 (vision) reads the photos and proposes taxonomy; Call 2 (text) writes the long description. The product name and short description are assembled deterministically from the listing facts and never come from a model. With inference off, the owner fills the taxonomy by hand and the server produces the description.
|
||
|
||
| ID | Requirement | Scope | Test |
|
||
|---|---|---|---|
|
||
| METADATA-5 | The listing taxonomy is exactly the synced WooCommerce set; the app never hardcodes, invents, or falls back to its own lists. | M3 | — |
|
||
| METADATA-10 | Product categories are exactly one species leaf and one or more wood-category leaves; parents group only and are never assigned. | M3 | — |
|
||
| METADATA-15 | The five locked attributes are Edge Type (1), Figure (at least 1, includes plain), Grade (exactly 1), Thickness (1, round-up band), and Moisture (1, default kiln-dried, never inferred). | M3 | — |
|
||
| METADATA-20 | Missing attributes or terms are skipped, never created. | M3 | — |
|
||
| METADATA-25 | Species, character, length, and free-text thickness are not product attributes. | M3 | — |
|
||
| METADATA-30 | Product tags come from the synced Woo set and are all optional; no hardcoded prefixes, and the bare fig tag is kept. | M3 | — |
|
||
| METADATA-100 | Call 1 returns feature suggestions as feature_tag_ids chosen only from the synced feat-* tag set (display names without the feat- prefix); WooCommerce is the authority for those tags. No free-text observation bags. | M3 | backend/tests/test_inference.py |
|
||
| METADATA-35 | Call 1 runs only when inference is enabled, automatically after a calibrated draft uploads or on demand, and sends the clean processed photos at 1024 px long edge or less plus user metadata and the synced taxonomy. | M3 | backend/tests/test_inference.py |
|
||
| METADATA-40 | Call 1 returns species, wood categories, edge, figure (multi), and feature-tag id suggestions constrained to the synced LISTS, each with confidence. Grade is not suggested by Call 1 (owner judgment). Wood category is decided before edge; cookies/burl prefer Wild edge when present. | M3 | backend/tests/test_inference.py |
|
||
| METADATA-45 | Call 1 confidence at or above the threshold (default 0.7) pre-fills review fields; below it the field stays empty for the owner to fill by hand or re-run Call 1; suggestions are always editable. | M3 | — |
|
||
| METADATA-50 | Typed values always win over inferred values. | M3 | — |
|
||
| METADATA-85 | The primary wood category selects the Call 2 prompt and the title and short-description patterns. | M3 | — |
|
||
| METADATA-80 | The server assembles the name and short description deterministically from the primary wood category's patterns and the dimension fact block. The patterns use only synced taxonomy terms and capture facts. | M3 | — |
|
||
| METADATA-90 | The dimension fact block supplies the exact strings the patterns interpolate and the only numbers Call 2 may print; there is no L by W by T rectangle assumption. | M3 | — |
|
||
| METADATA-55 | Call 2 runs on demand and produces the long description. | M3 | — |
|
||
| METADATA-60 | Call 2 receives the processed photos, the metadata confirmed or changed in review, the primary and secondary wood categories, the parent-category path, the dimension fact block, and brand/geo context. | M3 | — |
|
||
| METADATA-65 | The model never invents or alters a dimension; every number it prints comes from the fact block. | M3 | — |
|
||
| METADATA-95 | The dimension-overlay image is never sent to the model. | M3 | — |
|
||
| METADATA-70 | With inference off, the server produces the long description. | M3 | — |
|
||
| METADATA-66 | Every content generation, from a deterministic pattern or from the model, is logged with its inputs, the model request and response when a model was used, and the final stored content. The logged request keeps full text and photo refs only — never base64 image payloads. | M3 | backend/tests/test_inference.py |
|
||
| METADATA-75 | Inference never blocks create and never auto-publishes. | M3 | — |
|
||
| METADATA-105 | Inference prompts are stored in the database with all revisions; each call uses the active revision, the revision history is retained, and the revision used is recorded with the call. | M3 | — |
|
||
| METADATA-106 | The prompt revision interface is a REST API: append a revision, activate a revision, and read the active prompt with its revision list. There is no prompt editing UI. | M3 | — |
|
||
| METADATA-107 | Before the prompt revision interface is exposed beyond the trusted network it requires authentication. | later | — |
|
||
| METADATA-110 | The Generate text control appears only when content generation is enabled and the inference endpoint is reachable, and is disabled when the text service is down. | M3 | — |
|
||
| METADATA-115 | A runtime inference failure is classified recoverable or unrecoverable for the owner: recoverable offers Retry, unrecoverable gives a plain message explaining it. | M3 | — |
|
||
|
||
## REVIEW — confirm, tweak, ready
|
||
|
||
| ID | Requirement | Scope | Test |
|
||
|---|---|---|---|
|
||
| REVIEW-5 | Submission-ready requires at least two images (clean first pic plus its dimension-overlay copy), SKU, length, thickness, computed widths and sqft and bdft, exactly one species leaf, at least one wood-category leaf, one Edge Type, at least one Figure, exactly one Grade, one Thickness band, one Moisture, and a price. | M3 | — |
|
||
| REVIEW-10 | Title, short_description, and description are required on the product; title and short_description are assembled deterministically and the description is written by the model or the server. | M3 | — |
|
||
| REVIEW-15 | In review the owner declares one primary wood category and any secondary ones before Call 2 runs; primary drives the title and primary plus secondary drive the description; the default primary is the first category selected. | M3 | — |
|
||
| REVIEW-20 | Every empty required field shows an inline nudge as the owner works; the slab cannot reach ready until the full set is filled, and there is no single submit-time gap list. | M3 | — |
|
||
| REVIEW-25 | The price suggestion is bdft times the species rate from Settings; when no rate exists the price starts empty and the owner must enter one before create; the owner may override and the override sticks. | M3 | — |
|
||
| REVIEW-30 | Widths, sqft, and bdft are read-only in production, derived from the mask, and are not overridable. | M3 | — |
|
||
| REVIEW-35 | A picked taxonomy id that disappeared after a refresh shows as no longer valid and is never silently swapped for another value. | M3 | — |
|
||
| REVIEW-40 | The owner can edit any field, including generated title and description, until create, except the calculated measurement fields (widths, sqft, bdft), which are not user mutable. | M3 | — |
|
||
| REVIEW-45 | SKU is 3 to 24 characters, alphanumeric and hyphen only. | M3 | — |
|
||
| REVIEW-50 | Title and short_description warn past 60 and 160 characters and never exceed the store's hard limits; description has no maximum. | M3 | docs/contracts/WOO-API.md |
|
||
|
||
## PUBLISH — store submission
|
||
|
||
| ID | Requirement | Scope | Test |
|
||
|---|---|---|---|
|
||
| PUBLISH-5 | The create sequence validates the ready set, looks up the SKU and stops if it exists, refreshes taxonomy when the last sync is over one hour old, resolves categories and tags and attributes, uploads images, calls POST products, then finalizes. | M3 | — |
|
||
| PUBLISH-10 | SKUs matching SLAB-UAT-* always create a WooCommerce draft regardless of the create-status setting. | M3 | — |
|
||
| PUBLISH-15 | A duplicate SKU blocks create and offers edit-SKU or open-existing; this version has no update path. | M3 | — |
|
||
| PUBLISH-20 | A stale value on submit is a field-level error that shows the current options and never a raw code. | M3 | — |
|
||
| PUBLISH-25 | On success the server sets woo_product_id and returns it on the terminal poll, purges photos, discards the local working slab row, and clears the device draft; the app never deletes from WooCommerce; WooCommerce is the source of truth. | M3 | — |
|
||
| PUBLISH-30 | On failure the submission attempt is marked failed, photos are retained, sync_log is written, and a plain message is shown on the affected field or action; retry works from the same screen. | M3 | — |
|
||
| PUBLISH-31 | If image uploads succeed but the product POST fails, the orphaned WooCommerce media is left in place, the failure is logged, and a manual cleanup note is surfaced; this version does not auto-delete it. | M3 | — |
|
||
| PUBLISH-35 | The owner never sees raw status codes, stack traces, or internal error codes. | M3 | — |
|
||
| PUBLISH-36 | Every publish failure is classified for the owner as recoverable (he can act) or unrecoverable (outside his control), and the plain message states which it is and the next step. | M3 | — |
|
||
| PUBLISH-37 | A recoverable failure points at the field to fix, or offers Retry while the draft and photos are retained. | M3 | — |
|
||
| PUBLISH-38 | An unrecoverable failure says it is outside the owner's control and offers the available next step. | M3 | — |
|
||
| PUBLISH-40 | Images upload as processed PNGs with alpha; the clean primary, the primary dimensioned copy, and every remaining batch photo the owner selected are uploaded, with no upper cap on the number of images. | M3 | — |
|
||
| PUBLISH-45 | The Woo image order is the photos-ready lineup: clean calibration, dimension-overlay copy, then the rest in capture order. | M3 | — |
|
||
| PUBLISH-50 | Published means the store create succeeded, not that the product is live when status is draft. | M3 | — |
|
||
| PUBLISH-55 | Only non-null fields are sent; the payload carries name, type simple, status, sku, slug, regular_price, description, short_description, categories, tags, images, attributes, virtual false, downloadable false, catalog_visibility visible, sold_individually true. The slug is the sanitized product name plus the lowercased SKU. | M3 | docs/contracts/WOO-API.md |
|
||
| PUBLISH-60 | Each published product represents exactly one slab; WooCommerce quantity is one. | M3 | — |
|
||
|
||
## SETTINGS — configured once
|
||
|
||
| ID | Requirement | Scope | Test |
|
||
|---|---|---|---|
|
||
| SETTINGS-5 | WOO_BASE_URL lives in compose or .env, is https only, is injected into FastAPI, and is shown read-only in Settings; it is not a SQLite settings row. | M3 | — |
|
||
| SETTINGS-10 | FastAPI refuses to start when WOO_BASE_URL is missing or not https. | M3 | — |
|
||
| SETTINGS-15 | Woo auth is a WordPress application password for a dedicated low-privilege user, not consumer keys and not the admin login. | M3 | — |
|
||
| SETTINGS-20 | Woo username and application password are entered in Settings and stored AES-GCM in SQLite; the browser never keeps them. | M3 | — |
|
||
| SETTINGS-25 | SLAB_AES_KEY lives in compose or .env as a 32-byte base64 value, never in the DB or repo, and a missing key refuses startup. | M3 | — |
|
||
| SETTINGS-30 | Inference base URL and API key are stored encrypted in SQLite, while inference_model, inference_enabled, content_llm_enabled, brand_voice, and geo_context are settings. | M3 | — |
|
||
| SETTINGS-35 | Species rates are configured in Settings as one flat dollars-per-bdft value per synced species leaf, with no tiers. | M3 | — |
|
||
| SETTINGS-40 | test-woo verifies the connection and always resyncs taxonomy on success. | M3 | — |
|
||
| SETTINGS-45 | test-inference is a two-turn probe returning ok, mode (stateful, stateless, or fail), vision_capable, and detail; fail blocks Call 2 only. | M3 | — |
|
||
| SETTINGS-50 | Taxonomy sync pulls categories, tags, and attributes with terms, rebuilds the cache in one transaction, and on failure returns 502 while keeping the previous cache. | M3 | — |
|
||
| SETTINGS-51 | Taxonomy sync keeps only product tags whose slug starts with fig- or feat- (hyphen required); all other store product tags are ignored so non-wood product metadata does not enter the workflow. | M3 | backend/tests/test_woo.py |
|
||
| SETTINGS-55 | Every successful sync updates taxonomy_last_sync_at; taxonomy_anchor bumps only when the stored-subset hash changes, including deletions. | M3 | — |
|
||
| SETTINGS-60 | The client re-pulls the full taxonomy only when the anchor advances, and publish refreshes when the last sync is over one hour old. | M3 | — |
|
||
| SETTINGS-65 | woo_create_status defaults to draft. | M3 | — |
|
||
| SETTINGS-70 | The vision pixel cap is the only user-facing vision setting; the JPEG quality lives in server config. | M3 | — |
|
||
| SETTINGS-75 | Secrets never appear in any GET response; only a configured boolean is returned. | M3 | — |
|
||
| SETTINGS-80 | test-woo returns ok and detail; it fails closed when credentials are missing or the base URL is not HTTPS, and distinguishes an authentication rejection from an unreachable store. | M3 | — |
|
||
| SETTINGS-85 | The settings API rejects an attempt to write WOO_BASE_URL; the value is env-only and read-only. | M3 | — |
|
||
| SETTINGS-90 | inference_enabled and content_llm_enabled default off. | M3 | — |
|
||
|
||
## APP — device draft and state
|
||
|
||
| ID | Requirement | Scope | Test |
|
||
|---|---|---|---|
|
||
| APP-5 | Slab status runs draft, calibrated, ready, publishing, published, or failed. | shipped | — |
|
||
| APP-10 | The device draft is separate from a WooCommerce draft and is restored on open through Resume; Continue restores the last approved state and Start fresh discards. | shipped | — |
|
||
| APP-15 | The device draft is cleared after a successful create. | M3 | — |
|
||
| APP-20 | Slab ids are client-generated, timestamps are UTC ISO-8601, and client_rev and server_rev support optimistic concurrency. | M3 | — |
|
||
| APP-25 | FastAPI is the only caller of WooCommerce and of the inference endpoint; the browser holds no store or inference credentials. | shipped | — |
|
||
| APP-30 | The API base is /api/v1 and health is at /api/health. | M3 | — |
|
||
| APP-35 | Errors use a uniform envelope and are JSON; lists are returned in full without pagination. | M3 | — |
|
||
| APP-40 | The server stores the client-sent derived numbers and does not recompute them as source of truth. | shipped | — |
|
||
| APP-45 | Health returns status, version, and woo_reachable, and goes non-200 when the database or image volume is not writable. | M3 | — |
|
||
| APP-50 | Upstream WooCommerce 4xx responses are mapped into the uniform error envelope rather than passed through raw. | M3 | — |
|
||
| APP-55 | Structured JSON logs carry a request id threaded from the client through FastAPI to WooCommerce, and the log level is set by a LOG_LEVEL env. | M3 | — |
|
||
| APP-60 | On supported clients, interactive controls are reachable and operable by keyboard with a visible focus ring, and the layout reflows without clipping at 320 px. | M3 | — |
|
||
| APP-65 | No secret material (Woo credentials, inference key, or AES key) appears in any log output. | M3 | — |
|
||
|
||
## OPS — deploy and release
|
||
|
||
| ID | Requirement | Scope | Test |
|
||
|---|---|---|---|
|
||
| OPS-5 | Deploy is Docker Compose plus Caddy on an inventoried host, reachable only over LAN or Tailscale with HTTPS. | shipped | — |
|
||
| OPS-10 | WOO_BASE_URL and SLAB_AES_KEY are supplied as compose environment and never stored in the repo. | shipped | — |
|
||
| OPS-15 | deploy/INVENTORY.md is filled before any port bind, covering host, hostname, ports, and DNS reachability from the phone. | shipped | — |
|
||
| OPS-20 | Services bind to localhost and Caddy is the only public surface. | shipped | — |
|
||
| OPS-25 | Base images are pinned, not latest. | shipped | — |
|
||
| OPS-30 | Backup is a WAL-safe SQLite backup plus a tar of the images volume, with retention of 7 daily and 4 weekly. | later | — |
|
||
| OPS-35 | Restore is stop, restore DB and images, start, and it is tested at least once before backup is considered done. | later | — |
|
||
| OPS-40 | The UAT environment is ubudual5060 at http://100.64.0.9:10881; port 10880 is off limits. | M3 | — |
|
||
| OPS-45 | Inference is a remote, configurable endpoint, not part of the app host. | shipped | — |
|
||
| OPS-50 | The repository on gitea-atd is the source of truth. | shipped | — |
|
||
| OPS-55 | The app has no login of its own; it relies on the trusted LAN or Tailscale network plus HTTPS. | shipped | — |
|
||
|
||
## QUALITY — verification
|
||
|
||
| ID | Requirement | Scope | Test |
|
||
|---|---|---|---|
|
||
| QUALITY-5 | Tests are written with the code; no feature ships without a passing test and its shown output. | shipped | — |
|
||
| QUALITY-10 | Deterministic modules are test-driven and covered before any UI consumes them. | shipped | — |
|
||
| QUALITY-15 | Unit tests for the deterministic core cover vectors TV-1 through TV-10. | shipped | — |
|
||
| QUALITY-20 | The full suite passes with inference disabled, asserting zero inference calls. | M3 | — |
|
||
| QUALITY-25 | Pass or fail is only claimed with raw runner evidence; missing evidence means not run. | shipped | — |
|
||
| QUALITY-30 | Ty reads raw results and owns gate exit; agents never declare a gate green. | shipped | — |
|
||
| QUALITY-35 | Integration tests use a Woo mock and cover success, error, duplicate SKU, stale field, and taxonomy anchor. | M3 | — |
|
||
| QUALITY-40 | Property checks assert bdft is monotonic in thickness and square feet. | shipped | — |
|
||
| QUALITY-45 | The deterministic functions assert the bdft identity and the no-upscale path. | shipped | — |
|
||
| QUALITY-50 | UI and end-to-end tests run against a Compose stack with mocked Woo. | M3 | — |
|
||
| QUALITY-55 | Pipeline vector TV-1: an interior green patch not touching the border survives; U2Net is not invoked. | shipped | — |
|
||
| QUALITY-60 | Pipeline vector TV-2: dark interior wood on a black sheet survives, and flood fill isolates only border-connected regions. | shipped | — |
|
||
| QUALITY-65 | Pipeline vector TV-3: a 96 inch slab, 1.5 inch thick, 0.64 sqft mask yields 0.96 bdft. | shipped | — |
|
||
| QUALITY-70 | Pipeline vector TV-4: width samples at 6 inch stations on a 96 inch axis report min, max, and average perpendicular to the confirmed axis. | shipped | — |
|
||
| QUALITY-75 | Pipeline vector TV-5: one square end and one 45 degree cut, the length axis follows the prevailing length. | shipped | — |
|
||
| QUALITY-80 | Pipeline vector TV-6: 3:4 crop, 90 percent fill at extremes, transparent PNG, shorter side at least 800 when the source allows. | shipped | — |
|
||
| QUALITY-85 | Pipeline vector TV-7: an undersized source warns, is not upscaled, and is not publishable until retake. | shipped | — |
|
||
| QUALITY-90 | Pipeline vector TV-8: bdft 0.96 at 12.50 per bdft gives a 12.00 recommendation, and an override of 150.00 is stored. | M3 | — |
|
||
| QUALITY-95 | Pipeline vector TV-9: a 1 inch thickness rounds up into the synced Woo thickness band, not a hardcoded one. | M3 | backend/tests/test_thickness_band.py |
|
||
| QUALITY-100 | Pipeline vector TV-10: half-up 2 decimal rounding, including 96.04 to 96, 0.641 to 0.64, 11.525 to 11.53, 12.345 to 12.35. | shipped | — |
|
||
| QUALITY-105 | Dishonest proxies that do not count as pass: hardcoded or fixture taxonomy treated as synced, a Woo mock that never hits the real store for the exit walk, unit tests alone without a real SLAB-UAT-* draft, consumer-key auth, inference wired as dead config or required for create, claiming ready with empty fields, or parking work on the wrong branch. | M3 | — |
|
||
| QUALITY-110 | The M3 exit evidence is a real SLAB-UAT-* Woo draft matching the reviewed ready set with inference off, plus test-woo and test-inference results. | M3 | — |
|
||
| QUALITY-115 | Browser end-to-end capture through publish is verified on iOS Safari, Android Chrome, and desktop Chrome, Edge, and Firefox. | later | — |
|
||
| QUALITY-120 | No raw status code, stack trace, or internal code reaches the owner in any tested path. | M3 | — |
|
||
| QUALITY-125 | An encrypted credential stored by Settings decrypts back to the value it was given. | M3 | — |
|