E-commerce Image Optimization

PixelLift

Turn Pixels Into Purchases

PixelLift is an automated photo-enhancement SaaS that instantly batch-optimizes product images for marketplaces. It serves solo e-commerce sellers juggling dozens of listings, removing backgrounds, applying pro edits, and generating A/B-ready variations for bulk export. PixelLift cuts editing time up to 80%, shrinks per-image costs to cents, and lifts CTR 12–25%.

Subscribe to get amazing product ideas like this one delivered daily to your inbox!

PixelLift

Product Details

Explore this AI-generated product idea in detail. Each aspect has been thoughtfully created to inspire your next venture.

Vision & Mission

Vision
To empower independent sellers to convert more customers by instantly turning raw photos into high-performing, A/B-ready marketplace images.
Long Term Goal
Within 4 years, empower 100,000 independent sellers to process 10 million images annually and boost average listing conversion rates by 10%.
Impact
Helps solo e-commerce sellers cut image editing time by up to 80%, reduce per-image costs to cents (saving $200–$2,000 monthly), and lift listing click-through rates 12–25%, enabling faster bulk listings and measurable conversion gains across dozens of products.

Problem & Solution

Problem Statement
Solo e-commerce sellers and small brands listing dozens of products lack time and design skills to produce consistent, conversion-focused photos; manual retouching is slow or costly and marketplace tools yield low-quality, non-A/B-ready images.
Solution Overview
Automatically batch-optimizes raw product photos into marketplace-ready images to eliminate manual retouching and speed listings. It delivers instant background removal and exportable A/B-ready variation sets for fast split-testing and bulk uploads.

Details & Audience

Description
PixelLift is an automated photo-enhancement SaaS that instantly optimizes product images for marketplaces with batch edits and export presets. It serves independent online sellers and small e-commerce brands needing fast, scalable edits. PixelLift slashes editing time up to 80%, cuts per-image costs to cents, and increases click-through rates 12–25% to boost listing conversions. Its intelligent A/B-ready variation engine generates multiple marketplace-ready image versions automatically for fast split testing.
Target Audience
Solo e-commerce sellers (22–45) juggling listings who need fast, conversion-focused, scalable image edits
Inspiration
At midnight, hunched over my laptop, I spent hours removing backgrounds and tweaking shadows for 120 handmade coasters, watching competitors with polished photos outsell me. After reluctantly paying $500 for a single retouch and running split tests, I realized there had to be a faster, cheaper way: automate pro-level edits and generate test-ready image variations so solo sellers can list quickly and compete.

User Personas

Detailed profiles of the target users who would benefit most from this product.

S

Social Shop Sasha

- Age 24–33; urban-based; runs a microbrand selling accessories via TikTok Shop. - Role: Solo operator managing 50–150 SKUs; monthly revenue $5k–$30k. - Devices: iPhone 14/15, ring light, tripod; edits on phone, rarely desktop. - Markets: US/UK; sells on TikTok, Instagram, occasional Facebook Marketplace.

Background

Started flipping thrift finds on Depop, then migrated to TikTok Shop during 2023 surge. Learned basic editing in CapCut but struggled with messy lighting and ratios. Now prioritizes speed to post multiple daily product drops.

Needs & Pain Points

Needs

1. One-tap background cleanup on mobile uploads. 2. Vertical presets for TikTok Shop compliance. 3. Batch export to Instagram and Facebook catalogs.

Pain Points

1. Inconsistent lighting ruins on-phone product shots. 2. Wrong ratios cause crop or rejection. 3. Hand-editing slows daily drop cadence.

Psychographics

- Chases trends, ships content before perfection. - Obsessed with watch-time and tap-through metrics. - Prefers phone-first, hates desktop detours. - Values authenticity with clean, scroll-stopping visuals.

Channels

1. TikTok Shop ads 2. Instagram Reels feed 3. YouTube Shorts tutorials 4. Discord groups 5. Facebook Groups

B

Brand-Consistent Bailey

- Age 28–40; D2C founder or marketing lead; boutique CPG/apparel. - Team size 3–10; in-house photography occasional; outsources retouching. - Revenue $500k–$5M; Shopify Plus or Advanced; sells on Amazon and own site. - Tools: Figma, Klaviyo, Meta Ads, Google Drive DAM.

Background

Former brand designer who transitioned to growth after bootstrapping their label. Burned by inconsistent agency edits and color drifts across channels. Built a lightweight style system to scale visuals without losing identity.

Needs & Pain Points

Needs

1. Locked brand presets with role permissions. 2. Fast A/B variant generation at scale. 3. Color-consistent exports across channels.

Pain Points

1. Inconsistent shadows and margins across shoots. 2. Color shifts between web and marketplace. 3. Manual QA drains team bandwidth.

Psychographics

- Protects brand consistency like sacred ground. - Measures creatives by ROAS and CTR lift. - Prefers systems, templates, and locked presets. - Demands pixel-perfect, repeatable execution.

Channels

1. LinkedIn Ads 2. Shopify App Store 3. X ecom 4. Product Hunt launches 5. Indie Hackers forum

C

Catalog Control Casey

- Age 30–45; Operations/Catalog Manager at mid-market retailer. - Catalog 5k–50k SKUs; sells on Amazon, eBay, Walmart, Zalando. - Stack: PIM/ERP (Akeneo, Salsify), DAM, SFTP automations. - KPI: rejection rate, SLA adherence, cost per image.

Background

Cut teeth in data operations, then inherited imagery compliance after repeated Amazon takedowns. Built rulebooks for sizes, margins, and backgrounds but lacked reliable execution at scale.

Needs & Pain Points

Needs

1. Rule-based presets per marketplace and locale. 2. Scheduled bulk queues with throughput guarantees. 3. Audit logs and change history.

Pain Points

1. Rejections triggering delistings and lost revenue. 2. Manual rework across thousands of SKUs. 3. Nightly jobs failing without alerts.

Psychographics

- Risk-averse, compliance over creativity. - Lives by SLAs and dashboards. - Automates anything repeatable, auditable. - Prefers vendor accountability over promises.

Channels

1. LinkedIn Operations 2. Amazon forums 3. G2 Comparisons 4. BrightTalk webinars 5. Reddit FBA

M

Mobile Merch Max

- Age 22–38; full-time or side-hustle reseller. - Sources from liquidations, thrift, estate sales; high SKU churn. - Devices: Android/iPhone; hotspot; low-light environments. - Platforms: eBay, Facebook Marketplace, Poshmark, Mercari.

Background

Started flipping sneakers in college, scaling into mixed-category reselling. Time is money; editing on desktop broke flow, so mobile-first became mandatory.

Needs & Pain Points

Needs

1. One-tap cleanup in poor lighting. 2. Offline queue with auto-sync later. 3. Direct uploads to eBay and Poshmark.

Pain Points

1. Weak signal stalls cloud editors. 2. Tiny-screen precision edits are painful. 3. Night photos look muddy and dull.

Psychographics

- Hustle over polish, but photos must pop. - Hates friction; one tap or bust. - Frugal, tracks cost per sale. - Loves practical, offline-friendly tools.

Channels

1. YouTube Reselling 2. TikTok flips 3. Reddit Flipping 4. Facebook Marketplace 5. WhatsApp Business

M

Mockup Maker Maya

- Age 26–42; runs Etsy/Shopify POD storefront. - Integrations: Printful/Printify; basic Photoshop/Canva user. - Catalog 200–1,500 variants; apparel, mugs, wall art. - Revenue $100k–$800k; seasonal spikes Q4.

Background

Learned design from YouTube and Canva templates; scaling required faster variant production. Inconsistent mockups hurt trust, so standardized visuals became a growth lever.

Needs & Pain Points

Needs

1. Batch colorway and size variant generation. 2. Scene/background template library for apparel. 3. Auto-centering and consistent margins.

Pain Points

1. Manual mockup placement is soul-crushing. 2. Inconsistent sizing across product variants. 3. Slow turnarounds kill trend windows.

Psychographics

- Creative tinkerer, relentless tester. - Balances aesthetics with conversion data. - Loves templates and reusable scenes. - Seeks studio look without studio costs.

Channels

1. Etsy Handbook 2. YouTube Tutorials 3. Canva community 4. Printful blog 5. Pinterest boards

W

Wholesale-Ready Whitney

- Age 32–50; wholesaler/manufacturer catalog or marketing role. - Channels: Faire, NuORDER, Shopify B2B, distributor portals. - Assets inherited from suppliers; mixed quality; legacy PSD archives. - Team 2–6; tight seasonal calendars; tradeshow-driven.

Background

Cut her teeth producing print catalogs; now manages digital linesheets and portal compliance. Supplier imagery varies wildly, forcing endless Photoshop marathons before seasonal deadlines.

Needs & Pain Points

Needs

1. Bulk standardization of legacy supplier images. 2. Presets for Faire and NuORDER specs. 3. CMYK-safe, print-ready exports.

Pain Points

1. Manual Photoshop backlog before tradeshows. 2. Distributor rejections for minor spec misses. 3. Shadow and angle inconsistencies.

Psychographics

- Professional polish over flashy gimmicks. - Deadline-driven and detail-obsessed. - Values predictable, repeatable workflows. - Collaborative, shares playbooks internally.

Channels

1. LinkedIn Marketing 2. Faire portal 3. NuORDER webinars 4. Adobe forums 5. G2 DAM

Product Features

Key capabilities that make this product valuable to its target users.

Policy Pulse

Stay ahead of marketplace rule changes with live policy monitoring and automatic preset updates. Policy Pulse flags what changed, shows which presets and SKUs are impacted, and offers a one‑click update or sandbox “what‑if” run to simulate failures before they happen—preventing surprise rejections and rework.

Requirements

Real-Time Marketplace Policy Ingestion
"As a multi-marketplace seller, I want PixelLift to ingest policy changes in real time so that my presets stay aligned without my manual research."
Description

Continuously monitor and ingest listing image policy updates from major marketplaces (e.g., Amazon, eBay, Etsy, Walmart, Shopify) via official APIs, change feeds, or documented sources. Normalize policies into a unified internal schema covering dimensions, aspect ratios, background rules, color space, file formats/sizes, overlays/watermarks, margins, and category-specific exceptions. Version every policy with effective dates, region/category scopes, and deprecation windows. Implement resilient polling and webhook handlers with rate limiting, retries, backoff, and source health checks. Provide admin tools for manual overrides and emergency inputs when feeds are unavailable. Store policies in a queryable, auditable repository optimized for diffing and downstream impact analysis.

Acceptance Criteria
Webhook Policy Update Ingestion
Given a configured marketplace webhook with verified HMAC secret When a valid policy change webhook is received Then the system acknowledges with HTTP 200 within 2 seconds and enqueues processing Given duplicate deliveries of the same webhook (same event ID or payload hash) When processed Then only one new policy version is created and subsequent duplicates are ignored idempotently Given a webhook with invalid signature or schema When received Then the system responds with 401 or 400 respectively and no policy changes are persisted Given a transient processing failure When processing a webhook Then the job is retried with exponential backoff and jitter up to the configured max attempts and is moved to a dead-letter queue on exhaustion
Scheduled Polling with Rate Limiting and Backoff
Given configured per-source rate limits (e.g., Amazon, eBay, Etsy, Walmart, Shopify) When polling for updates Then requests never exceed the configured permits per time window Given a 429 or 5xx response from a source When polling Then the system applies exponential backoff with jitter and resumes normal cadence upon next 2xx Given a source that supports conditional requests When polling Then the system uses ETag and/or If-Modified-Since headers to avoid re-downloading unchanged content Given a healthy, updating source When a new policy is published Then the system ingests and stores it within 10 minutes of availability
Source Health Monitoring and Alerting
Given a monitored source When no successful ingestions occur for more than 15 minutes during expected availability Then the source health is set to Degraded and an alert is sent to the on-call channel Given sustained ingestion errors exceeding threshold (e.g., >30% over 15 minutes) When evaluating health Then the source is marked Critical, polling frequency is reduced, and a runbook link is attached to the alert When a source recovers to normal success and latency thresholds Then health status auto-resets to Healthy and normal polling cadence is restored Given the health API endpoint When queried Then it returns per-source metrics including last_success_at, error_rate, backlog_depth, and SLA compliance
Policy Normalization to Unified Schema
Given raw policy inputs from Amazon, eBay, Etsy, Walmart, and Shopify When normalized Then each record contains populated fields for dimensions, aspect_ratios, background_rules, color_space, file_formats, file_sizes, overlays_watermarks, margins, and category_exceptions passing schema validation Given source units and formats variability (px, in, cm; RGB/CMYK) When normalizing Then values are converted to standardized units (pixels, RGB) while retaining original values in an immutable audit field Given unknown or marketplace-specific fields When encountered Then they are preserved in an extensions map without blocking ingestion Given malformed or logically inconsistent inputs When detected Then records are rejected with explicit error codes and do not prevent other valid records from being processed
Policy Versioning and Effective Dating
When any normalized policy field changes for a marketplace-scope tuple (marketplace, region, category) Then a new immutable version is created with incremented version_id and the prior version remains queryable Each version stores effective_start, optional effective_end, regions[], categories[], and deprecation_window_days and passes validation for non-overlapping invalid ranges Given a query with a specific at_date, region, and category When resolving policy Then the correct version for that timeslice is returned deterministically Given a deprecation_window_days on a new version When active Then both the new and prior versions are marked accordingly and overlap only within the deprecation window
Auditable Repository with Diff and Query Performance
Given two versions of the same policy When a diff is requested Then the API returns field-level changes including additions/removals within category_exceptions Given any write (ingest or admin) When committed Then an immutable audit log entry captures actor (system/admin), timestamp, source, reason, and payload hash Given an attempt to mutate a historical version When performed Then the system rejects with 409 Conflict and instructs to create a superseding version Given read queries by marketplace, region, category, and at_date When executed at scale (>=1M versions) Then p95 latency is <=200 ms and p99 <=500 ms
Admin Manual Override and Emergency Input
Given an authorized admin user When creating or editing an override Then the UI/API enforces required fields, schema validation, and scope (marketplace, region, category) Given an active override When resolving policy Then the override takes precedence over ingested versions until its expiry_at, after which the latest ingested version is used Given governance requirements When saving an override Then a two-step confirmation or role-based approval is required and a full audit entry is recorded Given the need to revert an override When rollback is triggered Then the system restores the prior state within 2 minutes and records the rollback in the audit log
Policy Diff and Impact Analyzer
"As a seller, I want to see what changed and which presets and SKUs are affected so that I can quickly prioritize fixes where rejection risk is highest."
Description

Automatically compute human-readable diffs between policy versions and map each change to PixelLift rule primitives (e.g., background color/none, canvas size, aspect ratio, export format, compression quality, watermark prohibition). Identify impacted presets and associated SKUs by analyzing their configured parameters and recent export history. Provide severity scoring (e.g., critical/blocking vs. advisory), risk estimation for imminent rejections, and blast-radius metrics. Expose an in-app impact view with filters by marketplace, category, region, preset, and SKU, plus CSV/API export for operations. Maintain references back to source policy text for transparency and review.

Acceptance Criteria
Human-Readable Policy Diffs Mapped to Rule Primitives
Given two policy versions (v_old, v_new) for a marketplace When the analyzer runs a diff Then it produces a list of diff entries where each entry maps to one PixelLift rule primitive in {background, canvas_size, aspect_ratio, export_format, compression_quality, watermark_prohibition} And each entry is labeled change_type in {added, removed, modified} with normalized before_value and after_value (px for sizes, ratio as X:Y, quality 0–100, background as hex or "none") And each entry contains source_reference {policy_version_id, section_id or heading text, canonical_url, line_range} And the diff is generated within 30 seconds for policy documents up to 1 MB And the diff output is human-readable and machine-readable (JSON schema v1) simultaneously
Impacted Presets and SKUs Resolution
Given a set of presets with configured parameters and their linked SKUs, and a computed policy diff When impact analysis runs Then the system lists each impacted preset where at least one parameter conflicts with any diff entry, with conflict_reason in a controlled vocabulary {ASPECT_RATIO_MISMATCH, BACKGROUND_NOT_ALLOWED, FORMAT_BLOCKED, QUALITY_TOO_LOW, WATERMARK_FORBIDDEN, CANVAS_SIZE_OUT_OF_RANGE} And for each impacted preset, all associated SKUs with exports in the last 30 days or pending jobs are listed as impacted, with last_export_at and last_result And the analyzer outputs counts and percentages: impacted_presets, impacted_skus, share_of_last_7_days_exports And on a seeded validation dataset of 500 labeled cases, preset/SKU impact detection achieves ≥95% precision and ≥95% recall
Severity, Risk, and Blast-Radius Scoring
Given computed impacts When severity and risk are calculated Then each diff entry and impacted item receives severity in {critical, advisory} using deterministic rules: violations of hard requirements are critical; recommendations are advisory And each impacted SKU receives risk_score in [0,100] with explanation fields {primary_rule, evidence, last_noncompliant_export_at} And blast_radius metrics are computed: total_impacted_presets, total_impacted_skus, percent_of_catalog, percent_of_last_7_days_exports And risk and blast-radius recompute within 5 minutes of policy change detection
In-App Impact View with Filters and Drill-Down
Given a user opens the in-app impact view When filters are applied by marketplace, category, region, preset, and SKU (multi-select, combinable) Then results update to reflect the filters with response time ≤1s on datasets up to 50k SKUs And results can be sorted by severity, risk_score, and last_export_at; pagination supports page sizes up to 1000 And selecting a row opens a details panel showing before/after values, source_reference link, severity, risk_score, and impacted entities And the view displays "last computed at" timestamp and policy_version_id; all controls are keyboard accessible and meet WCAG 2.1 AA contrast
CSV and API Export of Impact Data
Given any current filter set in the impact view When the user exports to CSV Then the CSV contains one row per impacted entity with columns {policy_version, marketplace, category, region, primitive, change_type, before_value, after_value, severity, risk_score, preset_id, sku_id, last_export_at, conflict_reason} And the CSV row count exactly matches the in-app total for the same filters And an authenticated API endpoint GET /api/policy-impact exports the same data with identical filter semantics, supports pagination (limit, cursor), and responds within 2s for pages of up to 1000 rows And unauthorized requests receive 401; CSV download links expire after 24 hours
Data Freshness, Versioning, and Error Handling
Given Policy Pulse ingests a new policy version When the analyzer pipeline runs Then diff, impact, severity, risk, and blast-radius data are computed and available in-app within 5 minutes And if parsing or analysis fails, the system records an error with correlation_id, surfaces an "Analysis failed" banner with retry option, and prevents stale results from being shown as current And each dataset version is immutable and identified by {policy_version_id, analysis_run_id, computed_at}; users can trigger a manual recompute and see the new version And if the source policy URL is unreachable, the details panel shows a cached snapshot link and displays snapshot_hash for auditability
One-Click Preset Auto-Update
"As a seller, I want a one-click way to update or clone presets with compliant settings so that I can fix policy issues without manual editing."
Description

Offer a guided, one-click flow that applies compliant adjustments to impacted presets based on the latest policy diffs. Generate a draft preset clone with proposed changes (e.g., adjust canvas to 1600x1600, enforce white background, switch to JPEG sRGB, remove watermark, update compression). Validate changes against all linked marketplaces to avoid regressions; when conflicts arise, suggest branching into marketplace-specific variants. Present a before/after diff with estimated effects on export size and visual framing. Support bulk application across presets/SKUs, scheduling, and rollback to prior versions. Enforce role-based permissions and require confirmation for high-severity updates.

Acceptance Criteria
Draft Preset Clone Generation and Compliant Adjustments
Given policy diffs are available and impacted presets are listed When the user triggers One-Click Update for selected presets Then the system creates a draft clone per preset with the suffix "-PolicyUpdate-YYYYMMDD-HHMM" And applies compliant adjustments per diff (e.g., canvas=1600x1600, background=#FFFFFF, format=JPEG, color profile=sRGB, watermark=removed, compression=updated) And the source preset remains unchanged And a changelog enumerates each modified field with old and new values
All-Marketplace Compliance Gate
Given each draft clone is linked to one or more marketplaces When cross-marketplace validation runs Then the system executes compliance rules for all linked marketplaces And returns a Pass/Fail status per marketplace with rule IDs and messages And disables Apply if any marketplace has Fail status And displays a summary badge with counts of Pass and Fail per marketplace
Conflict Handling with Variant Branching Suggestions
Given validation identifies conflicting requirements across marketplaces When conflicts are present for a draft clone Then the system proposes marketplace-specific variant presets with minimal required field deltas And shows a field-by-marketplace matrix of proposed changes And upon confirm, creates variant presets, links them to the appropriate marketplaces/SKUs, and preserves the base for non-conflicting marketplaces And variants inherit all unchanged fields from the draft clone
Before/After Diff and Impact Estimates
Given a draft clone has been generated When the user opens the Before/After view Then the system displays side-by-side differences for dimensions, background color, file format, color profile, watermark, and compression And indicates cropping/padding deltas in pixels And estimates export size change as bytes and percentage for at least three representative sample images per preset And renders a visual framing preview for the primary sample
Bulk Apply with Scheduling and Progress Tracking
Given the user selects multiple impacted presets and SKUs When the user chooses Apply Now or Schedule Then Apply Now executes updates immediately with a progress indicator showing total, succeeded, failed counts and percentage And Schedule allows selecting a UTC datetime, creating a queued job visible in the schedule list And partial failures produce a downloadable CSV report including preset ID, SKU, marketplace, error code, and message And re-runs are idempotent: already updated presets are skipped with reason "Already Updated"
Rollback to Prior Versions
Given updates from One-Click Update have been applied When the user initiates Rollback on a specific preset version Then the previous version becomes active and the updated version is archived And marketplace/SKU associations revert to the previous version And an audit log captures actor, timestamp, version IDs, and rollback reason And subsequent exports use the restored settings
Role-Based Permissions and High-Severity Confirmation
Given role-based permissions are configured When a user attempts to apply updates Then only users with PresetAdmin (or higher) can apply; Editors may create drafts but cannot apply And high-severity updates (e.g., dimension or color profile changes) require typed acknowledgment and successful 2FA before apply And the confirmation displays counts of affected presets, SKUs, and marketplaces And an immutable audit record stores user ID, role, severity level, and confirmation artifacts
Sandbox What-If Simulation
"As a seller, I want to simulate policy compliance for my images and presets so that I can prevent rejections before they happen."
Description

Provide a safe sandbox to preflight presets and sample or full SKU sets against upcoming or newly effective policies without altering production presets. Execute asynchronous simulations that generate pass/fail reports, reasons (e.g., background not pure white, aspect ratio off by 0.1), and visual overlays indicating violations (margins, cropping, watermarks). Allow selection by marketplace, category, and effective date, with result caching for repeated checks. Summarize failure rates, top violation types, and estimated remediation effort/cost to guide decision-making before applying changes.

Acceptance Criteria
Run sandbox simulation without modifying production presets
Given a user with permission to run sandbox simulations selects a preset, marketplace, category, effective date, and a SKU set When the user starts a what‑if simulation Then no production presets, preset versions, images, or listing metadata are modified And the last-modified timestamps and checksums of production presets and assets remain unchanged And the simulation uses only read-only access to production data and isolated sandbox compute queues And the run is explicitly labeled as “Sandbox” in UI and API responses
Asynchronous job execution with progress and notifications
Given the user submits a sandbox simulation request When the API receives the request Then it responds within 2 seconds with a job_id and status "Queued" And progress is retrievable via GET /simulations/{job_id} including fields: status in [Queued, Running, Succeeded, Failed, Canceled], pct_complete (0–100), processed_count, total_count, eta_seconds And the user can cancel while status is Queued or Running; canceled jobs persist a summary with status "Canceled" And on completion, the user receives an in-app notification; if email alerts are enabled, an email is sent And the system supports at least 10,000 SKUs per run with average throughput ≥ 20 images/sec in sandbox, with progress updates no less frequent than every 15 seconds
Per-SKU pass/fail reporting with policy-versioned reasons
Given a simulation run completes When results are generated Then each SKU has pass boolean, violations[], and checked_rules[] And each violation includes rule_id, rule_name, severity, reason_text with quantitative deltas (e.g., "aspect_ratio off by 0.10"), and measured vs required values And each result includes marketplace, category, policy_version, and effective_date used And rule evaluation is deterministic for identical inputs and parameters And the count of failed SKUs equals the number of SKUs with violations > 0
Violation visual overlays with pixel-accurate annotations
Given violations are detected for a SKU image When overlays are generated Then an overlay image is produced showing annotation layers for margins, cropping bounds, and detected watermark boxes And overlay measurements are accurate within ±2 pixels of computed rule measurements And overlays are viewable in the UI and downloadable (PNG/JPEG) per SKU; file size ≤ 1.5 MB each And each annotation includes a legend/color key and pixel distance labels where applicable And overlay generation adds no more than 5 seconds per SKU to processing time on average
Scoped selection by marketplace, category, effective date, and SKU set
Given the user configures a simulation scope When selecting filters Then marketplace is required; category is optional and supports multi-select And effective_date accepts upcoming or newly effective dates and is used to load the corresponding policy version And the SKU set supports: sample(N), explicit SKU list, or full catalog within scope And sample(N) returns exactly N unique SKUs if available; if fewer exist, returns all available and flags a warning And the pre-run summary displays total SKUs in scope before submission
Result caching with TTL and invalidation on changes
Given an identical simulation scope (preset version, marketplace, category set, effective_date, SKU set definition) When a subsequent run is requested within the cache TTL of 24 hours Then the API returns cached results within 1 second with cache_hit=true and references the original job_id And the cache invalidates if any included SKU image is updated, the preset version changes, or the policy_version for the scope changes And cache metadata includes cached_at, ttl_seconds, and invalidation_reason when applicable
Summary dashboard with failure rates and remediation estimates
Given a simulation run has results When viewing the summary Then the dashboard shows total_skus, failed_skus, failure_rate% rounded to 0.1%, and trend vs previous run of the same scope if available And top 5 violation types are listed with counts and % of failed SKUs And estimated remediation effort is shown using per-violation minute weights, total hours (± range), and total cost computed from the organization hourly_rate (default $25 if unset) And users can export summary and per-SKU details as CSV and JSON And summary metrics reconcile exactly with the underlying per-SKU data
Targeted Alerts and Notifications
"As a seller, I want targeted alerts about meaningful policy changes and impacted items so that I can act quickly without being overwhelmed."
Description

Deliver timely, relevant notifications when policy changes occur or when simulations/auto-updates reveal risks. Support in-app banners, email, Slack/Teams, and outbound webhooks with per-user and per-workspace preferences. Allow subscriptions by marketplace, category, severity, and affected presets/SKUs. De-duplicate and rate-limit alerts, provide daily/weekly digests, and include deep links to the diff, impact view, and one-click update or sandbox run. Localize timestamps and content, and expose a notifications API for integration into merchant workflows.

Acceptance Criteria
Triggering Alerts on Policy Changes and Risk Findings
- Given a new marketplace policy change, a simulation “what‑if” run, or an auto‑update outcome is detected that affects at least one subscribed preset/SKU, When the event is processed, Then a notification is created within 5 minutes with type (policy_change_diff | simulation_failure | auto_update_applied | auto_update_failed), severity, marketplace, category, impacted count, and correlation ID. - Given a notification is created, When recipients are resolved, Then only users/workspaces with matching subscriptions receive it; non‑matching recipients receive none. - Given an auto‑update outcome lacks a severity, When the alert is created, Then the system assigns severity using configured rules and includes it in the notification.
Subscription Filters by Marketplace, Category, Severity, Presets/SKUs
- Given a user defines subscription filters by marketplace(s), category(ies), severity threshold, and specific presets/SKUs, When saved, Then the filters persist at the user level and can override workspace defaults. - Given an incoming event, When filters are evaluated, Then only alerts matching all selected dimensions are sent; severity threshold excludes lower‑severity events. - Given multiple values are selected within a dimension, When an event matches any selected value in that dimension, Then it qualifies (OR within a dimension; AND across dimensions).
Channel Delivery and Preferences
- Given per‑user and per‑workspace channel preferences (in‑app banner, email, Slack, Teams, webhook), When an alert is dispatched, Then it is delivered only to the enabled channels for that recipient. - Given email is enabled, When the alert is sent, Then the email arrives within 5 minutes with localized subject/body and timestamps adjusted to the user’s timezone. - Given Slack or Teams is enabled with a valid destination, When the alert is sent, Then a message posts to the configured channel with localized text, timestamps, and summary fields (severity, marketplace, impacted count). - Given in‑app banners are enabled, When the user opens the app, Then the banner appears within 2 seconds and marking it dismissed sets the notification as read across sessions.
De‑duplication and Rate Limiting Across Channels
- Given multiple events share the same dedup key (marketplace + rule/version + preset/SKU set) within a 60‑minute window, When alerts are dispatched, Then only one alert per recipient is sent per channel with a coalesced “X similar events suppressed” count. - Given a recipient exceeds 10 alerts per channel within any rolling 60‑minute window, When additional alerts occur, Then they are suppressed, added to the next digest, and the suppression total is recorded. - Given suppression or coalescing occurs, When the daily/weekly digest is generated, Then the digest includes totals for suppressed/coalesced alerts.
Daily and Weekly Digest Generation
- Given a user opts into daily and/or weekly digests, When the scheduled time occurs in the user’s local timezone, Then a digest is generated and delivered to selected channels with counts by severity, marketplace, category, and top 20 impacted presets/SKUs. - Given alerts were rate‑limited or de‑duplicated, When the digest is generated, Then it lists suppressed alerts, representative examples, and deep links to the full impact and diffs. - Given digest preferences are changed, When the next schedule runs, Then the new cadence and send time are honored.
Actionable Deep Links
- Given an alert is delivered through any channel, When “View diff” is clicked, Then the UI opens to the policy diff view pre‑filtered to the marketplace, rule, and impacted presets/SKUs from the alert. - Given an alert includes “One‑click update,” When clicked by an authorized user, Then the update flow launches with impacted presets/SKUs preselected and can be confirmed in a single submit. - Given an alert includes “Run sandbox,” When clicked, Then a sandbox simulation is queued with the same parameters and the user is navigated to the run status page. - Given deep links are generated, When opened, Then they carry a signed token valid for 24 hours and preserve context post‑login; expired tokens redirect to request a fresh link.
Notifications API and Webhook Reliability
- Given an integration calls the Notifications API, When it requests notifications with filters (time window, marketplace, category, severity, preset/SKU), Then the API returns results with stable pagination, read/unread flags, and deep‑link URLs. - Given a client acknowledges a notification via the API, When the request succeeds, Then the notification is marked read and excluded from unread counts across channels. - Given an outbound webhook is configured, When an event occurs, Then a JSON payload is delivered signed with HMAC‑SHA256 using the workspace secret; 2xx responses mark success; 5xx responses trigger retries with exponential backoff up to 24 hours; idempotency keys prevent duplicate processing.
Compliance Audit Trail and Rollback
"As a seller, I want a complete audit log and easy rollback options so that I can demonstrate compliance and recover quickly from unintended changes."
Description

Record an immutable audit trail for policy ingestions, diffs, impact assessments, user actions, and automated preset updates, including who, what, when, and why. Store snapshots of presets before and after changes with links to the triggering policy version. Enable one-click rollback to prior preset versions and restoration of previous export settings. Provide exportable compliance reports for marketplace disputes and internal reviews, respecting data retention and privacy requirements. Integrate with SSO and role-based access controls to ensure traceability and least-privilege access.

Acceptance Criteria
Immutable Audit Logging for Policy and Preset Events
- Given any of the following occurs: policy ingestion, policy diff calculation, impact assessment, user action on presets, or automated preset update, When the event completes, Then an audit record is appended to a WORM store with fields: event_id, event_type, actor_id or 'system', actor_auth_provider, timestamp_utc (ISO 8601 ms), policy_version_id (if applicable), entity_type, entity_id(s), before_ref, after_ref, reason, correlation_id. - Given an audit record was written, When any client attempts to update or delete it, Then the operation is rejected with 403 and a new audit record of the denied attempt is created. - Given an audit record exists, When integrity verification runs, Then its content hash and chain hash validate successfully and prove no tampering. - Given an event is written, When queried, Then it is discoverable within 5 seconds and p95 write latency is <= 200 ms. - Given the audit store is temporarily unavailable, When an event occurs, Then the event is queued durably and delivered once available with no loss or reordering within the same correlation_id.
Preset Snapshotting with Policy Linkage
- Given a preset change is about to be applied by a policy update or user edit, When the change is committed, Then the system stores a before-snapshot and after-snapshot with deterministic IDs and byte-for-byte fidelity. - Given snapshots are stored, When viewed, Then each snapshot includes a reference to the triggering policy_version_id (if any), change_set diff, and export settings at that time. - Given a snapshot exists, When retrieved via UI or API, Then it loads within 2 seconds p95 and is accessible only to authorized roles. - Given retention policy is 24 months (configurable), When the period elapses, Then snapshots are expired according to policy while preserving audit references.
One-Click Rollback of Preset and Export Settings
- Given a user with role Compliance Admin or higher selects a previous preset version, When they click Rollback and confirm, Then the system restores that version and its export settings atomically within 10 seconds p95. - Given a rollback occurs, When complete, Then a new audit record is created linking from current to restored version with reason and actor. - Given dependent policies have changed since the target version, When the user initiates rollback, Then the UI shows a pre-flight impact summary and blocks rollback unless the user also chooses to reapply current policy or run a sandbox validation. - Given a rollback fails partway, When recovery executes, Then the system reverts to the pre-rollback state with no data loss and surfaces an error to the user.
Compliance Report Export for Disputes and Reviews
- Given a user selects a date range, marketplace, presets/SKUs, and report format (PDF, CSV, JSON), When they request Export, Then the report includes audit events, policy versions, diffs, snapshot references, actors, timestamps, and outcomes and is delivered within 60 seconds for up to 50k events. - Given a report is generated, When downloaded, Then it contains a verification checksum and signature metadata to attest integrity. - Given the user’s role lacks Export permission, When they attempt export, Then the action is denied with 403 and logged. - Given PII masking is enabled by default, When exporting, Then email and SSO identifiers are pseudonymized unless the requester has Compliance Admin role.
SSO and Role-Based Least-Privilege Enforcement
- Given SSO is configured via SAML 2.0 or OIDC, When a user signs in, Then their identity and groups map to roles (Viewer, Editor, Compliance Admin) and are recorded on each audit event. - Given a user’s role, When accessing audit logs, snapshots, rollback, or export functions, Then permissions are enforced: Viewer=read-only logs; Editor=read logs and snapshots; Compliance Admin=all including rollback and export. - Given an access token is revoked or expires, When the user attempts any protected action, Then access is denied and logged. - Given an admin updates role mappings, When change is saved, Then it takes effect within 1 minute and is auditable.
Data Retention, Privacy, and Redaction Controls
- Given a configurable retention policy (e.g., 24 months), When records exceed retention, Then audit events and snapshots are securely purged with a deletion receipt logged. - Given privacy controls are enabled, When viewing or exporting logs, Then PII fields are masked by default and can only be unmasked by Compliance Admin with purpose-of-use entered and logged. - Given a data subject erasure request, When executed, Then direct identifiers are removed or pseudonymized while preserving event integrity and traceability of actions. - Given data at rest and in transit requirements, When storing or transmitting audit data and snapshots, Then AES-256 at rest and TLS 1.2+ in transit are enforced.
Search, Filter, and API Access to Audit Trail
- Given the audit log UI and API, When filtering by time range, action type, policy_version_id, preset_id, SKU, actor_id, or correlation_id, Then only matching events are returned with pagination and sort, responding in <= 800 ms p95 for result sets under 10k events. - Given sandbox “what-if” runs, When logged, Then they are explicitly tagged as sandbox and excluded by default filters but can be included via include_sandbox=true. - Given the API rate limit (e.g., 100 requests/min per token), When exceeded, Then 429 is returned with retry headers and events are unaffected. - Given a request includes a non-existent entity ID, When executed, Then the response returns 200 with empty set for list endpoints or 404 for resource fetch and no side effects.

Category IQ

Automatically detect or ingest product category and apply category‑specific image rules (e.g., apparel, jewelry, collectibles). Category IQ merges marketplace and category nuances into your preset—so each batch gets the exact margins, backgrounds, angles, and size constraints required without manual checklisting.

Requirements

Multi-Source Category Detection & Ingestion
"As a solo e-commerce seller, I want PixelLift to auto-detect or ingest my product category so that the right rules apply without me tagging each SKU manually."
Description

Automatically infer a product’s category from image content and available metadata (titles, attributes) while also accepting explicit category inputs via CSV upload, API, or marketplace import. Produce a confidence score for detected categories, support threshold-based fallbacks (e.g., prompt for selection, use seller default), and persist the resolved category to the job context for downstream rule application. Handle multiple marketplaces per SKU, per-variant category differences, and maintain SKU-to-category mappings for reuse across future batches. Provide secure, idempotent ingestion endpoints and normalize incoming categories to a canonical internal taxonomy to ensure consistent processing within PixelLift.

Acceptance Criteria
Autodetect Category from Image and Metadata with Confidence Scoring
Given a batch containing product images plus optional title and attribute metadata When Category IQ runs inference for each SKU/variant Then the system outputs a resolved category mapped to the internal canonical taxonomy and a numeric confidence score between 0.00 and 1.00 per item And the top three candidate categories with scores are included in job metadata for each item And the highest-scoring canonical category becomes the resolved category when no explicit input exists And inference results are deterministic for identical inputs across runs (same inputs produce same resolved category and score within ±0.01)
Explicit Category Ingestion via CSV, API, and Marketplace Import
Given an authenticated caller with workspace scope When the caller ingests explicit categories via CSV upload, REST API, or marketplace import Then all incoming category identifiers/paths are normalized to the internal canonical taxonomy And invalid rows/records are rejected with row-level errors including code, field, and message while valid rows are processed And an Idempotency-Key is required for API/CSV; replays with the same key within 24h return the original result without creating duplicates or side effects And unauthenticated or unauthorized requests receive 401/403 and no data is persisted And marketplace-native category IDs are mapped via connector-specific tables and unmapped IDs are flagged with actionable errors
Threshold-Based Fallbacks and Seller Default Application
Given a workspace-level detection threshold T and a configured seller default category D or a prompt_required flag When the highest inference confidence ≥ T Then the resolved category is the highest-scoring canonical category and the decision reason is "inferred" When the highest inference confidence < T and prompt_required = true Then the job enters "Awaiting Category" state and a UI prompt is generated listing top candidates with scores When the highest inference confidence < T and prompt_required = false Then category D is assigned and the decision reason is "fallback_default" And all decisions log the scores, threshold T, and decision reason for audit
Persist Resolved Category to Job Context for Rule Application
Given a job with resolved categories per SKU/variant/marketplace When downstream image processing runs Then category-specific rules (margins, backgrounds, angles, size constraints) are selected using the resolved category And the resolved category, decision reason, confidence score, and applied rule set version are persisted in job artifacts and exposed via API responses And audit logs capture resolver (inferred/explicit/fallback), actor or system component, and timestamp And rerunning the same job without input changes reuses the persisted resolved category
Multi-Marketplace Per-SKU Handling
Given a SKU listed on multiple marketplaces with different marketplace category IDs When ingestion or inference occurs Then separate category mappings are stored per marketplace within the same SKU context And downstream processing selects the mapping that matches the job's target marketplace context And if a mapping is missing for the target marketplace, inference is attempted and fallback rules apply if below threshold
Per-Variant Category Differences within a SKU
Given a parent SKU with multiple variants where category may differ by variant When ingestion or inference processes the batch Then mappings are stored at variant granularity using the provided variant identifier And if a variant identifier is absent, the parent SKU mapping is used And downstream processing uses the variant-level mapping when present, otherwise the parent mapping
SKU-to-Category Mapping Reuse and Update Semantics
Given previously resolved SKU/variant/marketplace mappings When a new batch includes the same SKU/variant/marketplace without explicit category input and without material metadata changes Then the prior resolved category is reused with decision reason "reused_mapping" When explicit category input is provided for that SKU/variant/marketplace Then it overwrites the prior mapping, increments mapping_version, and archives the superseded mapping with reason "explicit_override" And mapping changes propagate to processing within 5 minutes and are visible via API with fields mapping_version and resolution_source
Category Rule Engine & Versioning
"As a seller, I want category-specific rules applied consistently and versioned so that my images remain compliant even when marketplace policies change."
Description

Define and execute category-specific image rules (margins, background color/texture, aspect ratio, min/max dimensions, file size, allowable angles, shadow/reflection, watermark policy, number of images) with deterministic precedence. Merge layered inputs in this order: marketplace baseline → category constraints → seller preset → per-batch overrides. Support rule versioning with effective dates, rollback, and audit history so that previously processed batches can be reproduced. Expose a policy evaluation service that returns the resolved rule set per item, including source and justification for each rule, ensuring transparency and consistent application across PixelLift’s processing pipeline.

Acceptance Criteria
Deterministic Rule Precedence Merge
Given a marketplace baseline, category constraints, a seller preset, and per-batch overrides that define overlapping rule keys When the policy evaluation service resolves rules for an item Then for each rule key the chosen value is the first non-null from per-batch overrides, else seller preset, else category constraints, else marketplace baseline And the response includes for each key the selected value, the source layer name, and the source version identifier And if a key is undefined in all layers it is absent from the resolved rule set and listed under unsetKeys in the evaluation trace And all selected values conform to the rule schema (type, units, allowed ranges), otherwise the service returns 422 with the offending key and source layer
Versioning with Effective Dates and As-Of Evaluation
Given category rule versions V1 (effective 2025-09-01) and V2 (effective 2025-10-15) When an item is evaluated with processingTimestamp 2025-09-26 and no asOf override Then V1 is applied and V2 is ignored And the response includes the active version IDs per layer and their effective dates When the same item is evaluated with asOf=2025-10-20 Then V2 is applied And both evaluations are reproducible by repeating the same timestamps to produce identical outputs
Rollback and Immutable Audit History
Given an administrator triggers a rollback of category constraints from V3 to V1 When the rollback completes Then a new version V4 is created that clones V1’s content with a new effectiveDate equal to the rollback time And versions V1–V3 remain immutable and queryable And the audit log records actor, timestamp, action (rollback), fromVersion, toVersion, and a diff summary And subsequent evaluations after the rollback use V4 when its effectiveDate is active
Rule Coverage and Normalization Across Supported Properties
Given category constraints define all supported properties (margins, background color/texture, aspect ratio, min/max dimensions, file size, allowable angles, shadow/reflection, watermark policy, number of images) When rules are resolved for an item Then the resolved rule set contains these properties with normalized units (percent for margins, px for dimensions, ratio as W:H, KB for file size) And each property value lies within its allowed range and enum set per schema And missing or out-of-range inputs at lower layers are corrected by higher-precedence valid values or flagged with validation errors
Per-Batch Overrides Scope and Non-Persistence
Given a batch B123 defines per-batch overrides for background color and margins When items in batch B123 are evaluated Then the overrides are applied to those items and reported as source=per-batch overrides When items in a different batch B124 are evaluated Then the overrides from B123 are not applied and do not alter any saved presets or category constraints And the evaluation trace for B124 contains no references to B123
Reproducible Re-evaluation of Previously Processed Batches
Given batch B789 was processed on 2025-09-10 with recorded marketplace baseline version MB1, category constraints version C5, seller preset version P2, and no per-batch overrides When the policy evaluation service re-evaluates with batchId=B789 Then the resolved rule set matches the original output exactly, including values, source layers, and version identifiers And a deterministic checksum of the resolved rule set matches the stored checksum from the original processing
Marketplace Taxonomy Sync & Mapping
"As a seller, I want PixelLift to stay in sync with marketplace taxonomies and map categories across channels so that I don’t have to remap or worry about outdated rules."
Description

Continuously synchronize marketplace taxonomies and media policies (e.g., Amazon, eBay, Etsy, Walmart) via connectors or documented feeds. Map external categories to a canonical internal taxonomy, track deprecations/renames, and automatically migrate mappings with human-in-the-loop review for ambiguous changes. Schedule incremental updates, validate diffs, and notify when policy changes affect existing presets. Provide an admin interface and API to inspect mappings, view change logs, and download policy snapshots, ensuring Category IQ remains current without manual upkeep.

Acceptance Criteria
Scheduled Incremental Sync Executes and Records Diffs
Given a configured marketplace connector with lastSyncAt, when the scheduled job runs at its configured interval, then only nodes changed since lastSyncAt are requested via incremental endpoint or feed token. Given changes are fetched, when diffs are computed, then added, updated, renamed, and deprecated nodes are classified and counted, and a diff record is persisted with id, marketplace, startedAt, endedAt, nodeCounts by type, and source checksums. Given the sync completes successfully, when viewing the job record, then status=Success, duration<=15m for a taxonomy of up to 100k nodes, and zero unhandled exceptions are logged. Given transient HTTP errors (5xx/429) occur, when retries are attempted, then exponential backoff up to 3 retries is applied, and on exhaustion the job status=Failed and an on-call alert is emitted.
Automatic Mapping to Canonical Taxonomy
Given new or changed external category nodes exist in a diff, when the mapping processor runs, then exact-code and declared-rename matches are auto-mapped to the corresponding canonical nodes with status=AutoMapped. Given a node has multiple plausible targets or similarity below the configured threshold, when evaluated, then it is marked Ambiguous and enqueued to the human review queue within 1 minute with candidate options and evidence attached. Given an auto-mapped node is applied, when validating referential integrity, then all related Category IQ presets referencing the external node now reference the canonical node and pass integrity checks (no orphans). Given mapping results are finalized, when auditing, then each mapping change has userId (system or reviewer), timestamp, previousCanonicalId, newCanonicalId, rationale, and source diffId recorded.
Deprecations and Renames Auto‑Migration with Rollback
Given a marketplace marks a category as deprecated with a replacement, when processing the diff, then existing mappings are migrated to the replacement canonical node and flagged Migrated. Given a deprecated node has no replacement, when processing, then presets referencing the node are flagged for attention and not auto-migrated, and a review task is created. Given a migration completes, when viewing the audit log, then a reversible change record with before/after canonicalIds and affected presetIds is present, and invoking rollback restores prior mappings within 2 minutes.
Policy Change Impact Notification to Preset Owners
Given a marketplace updates media policies for a category that affects margins, backgrounds, angles, or size constraints, when the diff is validated, then impacted Category IQ presets are identified by id and rule type changed. Given impacted presets are identified, when notifications are dispatched, then owners receive email and configured webhooks/Slack within 15 minutes including marketplace, categories, policy deltas, presetIds, and recommended actions. Given a notification is sent, when inspecting delivery logs, then each message has a delivery status (Delivered/Failed), retry attempts for failures (up to 3), and a correlationId tying it to the diffId.
Admin UI: Inspect Mappings, Change Logs, and Download Snapshots
Given an admin is authenticated with proper role, when accessing the Mapping UI, then they can filter by marketplace, externalCategoryId, canonicalId, mappingStatus, and updatedAt range, with results returned under 2 seconds for 10k records. Given a diff record exists, when selecting it in the UI, then a human-readable diff shows adds/updates/renames/deprecations with counts and drill-down, and the change log lists who/what/when for each mapping change. Given a snapshot is requested, when downloading, then the file is available in CSV and JSON with a signed checksum, includes policy documents and effective dates, and the action is recorded in the audit log.
API: Access Mappings, Diffs, and Policy Snapshots
Given valid OAuth2 credentials, when calling GET /v1/mappings with filters and pagination, then the API returns 200 with items, totalCount, pageSize, nextPageToken, and respects rate limits (429 with Retry-After). Given a diffId, when calling GET /v1/diffs/{id}, then the API returns marketplace, time range, counts by changeType, and checksums, and ETag/Last-Modified headers support conditional requests (304 on match). Given a snapshot version, when calling GET /v1/policies/snapshots/{version}, then the API streams the file, returns checksum headers, and the checksum matches the stored value.
Preset Merge & Override Controls
"As a seller, I want to preview and override how my preset merges with Category IQ so that I can preserve my brand look while staying compliant."
Description

Offer UI and API controls to preview how Category IQ merges marketplace/category constraints with the seller’s preset. Allow granular toggles and priority overrides for rule groups (e.g., background, margins, angles) with validation that highlights potential compliance risks of overrides. Provide a before/after diff of resolved rules and a small preview sample of the resulting image framing. Support project-level defaults, batch-level overrides, and SKU-level exceptions, with the ability to whitelist items to bypass Category IQ when necessary.

Acceptance Criteria
Merged Rules Preview with Before/After and Framing Sample
Given a seller preset and detected marketplace/category for a batch When the user opens "Preview Merge" from the batch screen Then the UI renders a before/after diff of resolved rule groups (background, margins, angles, size constraints), indicating the winning source for each rule (Seller Preset or Category IQ) Given a batch of up to 200 SKUs When "Preview Merge" is opened Then the diff and at least 3 preview samples load within 2 seconds p95 Given resolved rules When the preview is shown Then each preview sample displays applied framing (crop, margins, background) at a minimum resolution of 512 px longest edge with zoom and pan controls Given a rule conflict exists When the diff is displayed Then conflicting rules are highlighted and a "Resolution: [Priority]" tag is visible for the group
Granular Toggles and Priority Overrides per Rule Group
Given rule groups Background, Margins, Angles, and Size When a user toggles Category IQ Off for a rule group Then the resolved rules for that group are sourced exclusively from the seller preset and the UI badge changes to "Category IQ: Off" for that group Given a user sets priority "Seller Preset > Category IQ" for Background When the diff recalculates Then the winning source for Background updates accordingly within 250 ms and the sample previews refresh within 1 second Given a user adjusts overrides When they save at Project, Batch, or SKU scope Then the changes persist at the selected scope and are reflected on subsequent previews Given a user selects "Reset to default" When confirmed Then group priorities and toggles revert to the default for the current scope
Override Validation and Compliance Risk Highlighting
Given marketplace constraint rules are available for the detected marketplace/category When an override creates a potential violation Then a risk banner is displayed per affected rule group with severity (Warning, Critical) and a human-readable reason Given a Critical severity risk When the user attempts to save Then the save requires an explicit confirmation checkbox and records "proceed with risk" in the audit log; saving is permitted Given a Warning severity risk When the user saves Then the save proceeds without extra confirmation and the warning persists in the preview Given API consumers send overrides that cause risks When calling the preview or save endpoints Then the response includes structured risk items with code, rule_id, severity, message, and affected_skus; HTTP status is 200 for preview and 200/202 for save
Scope Resolution: Project Defaults, Batch Overrides, SKU Exceptions, and Whitelist Bypass
Given project-level defaults, batch-level overrides, and SKU-level exceptions exist When computing effective rules Then precedence is SKU > Batch > Project, and within each scope the configured rule-group priority determines whether Seller Preset or Category IQ wins Given a SKU is whitelisted to bypass Category IQ When previewing or processing that SKU Then Category IQ adjustments are not applied to any rule group, the UI shows "Bypassed", and no Category IQ-derived risk is displayed for that SKU Given a SKU is removed from the whitelist When recomputing effective rules Then Category IQ adjustments and validations reapply immediately and preview updates within 1 second Given a user imports a whitelist via CSV containing valid SKU identifiers When the import completes Then at least 99% of valid SKUs are applied successfully and failures are reported with line numbers and reasons
API Parity for Merge Preview, Overrides, and Scope Controls
Given an authenticated client When calling POST /v1/category-iq/preview with up to 500 SKU IDs or a batch_id Then the response returns effective_rules, before_after_diff, preview_sample_urls (min 3), and risk_items within 3 seconds p95 Given an authenticated client When calling PATCH /v1/rules/overrides with scope (project|batch|sku), rule_group settings (priority, toggle), and optional whitelist updates Then the system persists changes and returns the new effective configuration Given high request volume When rate limits are exceeded Then the API responds 429 with Retry-After and respects the Idempotency-Key header for safe retries Given invalid override input When the API validates Then it returns 422 with field-level errors and does not persist changes
Diff Export and Audit Trail of Rule Resolutions
Given a batch with computed diffs When a user exports the diff Then JSON and CSV downloads are available including fields: sku, rule_group, rule_key, from_value, to_value, winning_source, scope, timestamp, actor Given overrides are created or updated When the audit log is viewed Then entries show who, when, scope, before/after, risk at time of save, and optional justification text Given a batch up to 10,000 SKUs When exporting the diff Then the export job is queued and completes within 2 minutes p95, with a downloadable link and webhook notification upon completion
Batch Processing with Fallback & SLA
"As a high-volume seller, I want category rules applied reliably across large batches with clear fallbacks so that I meet deadlines even when some items are ambiguous."
Description

Apply resolved category rules at scale across large batches with target throughput and latency SLAs. Implement robust fallbacks for uncertain categories (e.g., use nearest category template, defer to seller default) and clearly annotate each item’s processing path. Support partial success, resumable jobs, idempotent re-runs, and concurrency controls to avoid throttling marketplace APIs. Emit operational metrics (queue depth, error rates, time per item), and expose job-level status and per-item outcomes via dashboard, API, and webhooks for integration with seller workflows.

Acceptance Criteria
SLA & Observability
Given a batch of 10,000 product images with resolved categories and sufficient compute capacity When the batch is submitted to Category IQ for processing Then sustained throughput is >= 250 images per minute for the duration of the job And per-item processing time p95 <= 120000 ms and p99 <= 240000 ms And overall job wall-clock time <= 45 minutes And SLA metrics (throughput, p50/p95/p99, success_rate, failure_rate) are recorded and visible in the job summary And operational metrics are emitted every 30 seconds and exposed at GET /metrics with freshness <= 60 seconds And SLO alerts trigger when p95_item_duration_ms > 120000 for 5 consecutive minutes or failure_rate > 2% for 5 consecutive minutes.
Fallback Category Resolution with Confidence Thresholds
Given an item whose detected category confidence < 0.70 When Category IQ selects a ruleset Then the nearest category by taxonomy distance is chosen if distance <= 2 And otherwise the seller default preset is applied And per-item metadata sets category_source to "detected", "nearest_category", or "seller_default" as applicable And fallback_applied is true when a nearest_category or seller_default is used And the applied ruleset_version matches the selected template And the item record stores detected_confidence and selected_taxonomy_distance.
Per-Item Processing Path Annotation
Given any processed item Then the item record contains job_id, item_id, detected_category_id, effective_category_id, category_source, ruleset_version, steps_executed[], started_at, completed_at, duration_ms, fallback_applied, error_codes[] And API GET /v1/jobs/{job_id}/items/{item_id} returns these fields and passes JSON schema validation And the dashboard item detail renders the same fields And webhook item payloads include the same fields.
Partial Success Semantics
Given a batch where at least one item fails and at least one succeeds When processing completes Then job.status is "CompletedWithErrors" And job.success_count + job.failure_count equals total_items And successfully processed assets are persisted and not deleted And failed items are retriable individually via POST /v1/jobs/{job_id}/items/{item_id}/retry.
Resumable Jobs and Idempotent Re-runs
Given a job interrupted after partial completion When the client resumes the same job with the original X-Idempotency-Key and identical inputs Then only incomplete or failed items are re-queued And previously succeeded items are skipped without reprocessing or duplicate exports And output asset URLs and checksums for succeeded items remain unchanged And billing counts only newly processed items And re-running within 24 hours with the same idempotency key and inputs yields identical outputs and metadata.
Concurrency Controls and Throttling Protection
Given marketplace API rate limits of 120 requests per minute per seller token When performing export calls Then the system enforces per-token concurrency such that HTTP 429 responses are < 0.1% of export requests over any 15-minute window And exponential backoff with jitter and up to 3 retries is applied on 429 and 503 responses And no more than 10 concurrent export workers are used per token And under sustained throttling, internal queue_depth growth remains < 5% per minute for 10 minutes.
Job Status APIs, Dashboard, and Webhooks
Given any job lifecycle When querying API GET /v1/jobs/{job_id} Then status is one of {Queued, Running, Completed, CompletedWithErrors, Failed, Canceled} with counts and timestamps (created_at, started_at, completed_at) And the dashboard reflects the same status with UI latency <= 5 seconds And upon job completion a webhook is delivered within 15 seconds containing the job summary and per-item outcomes (batched with max 1000 items per event and pagination cursor if larger) And webhooks are HMAC-SHA256 signed using the tenant secret and retried up to 10 times over 24 hours until a 2xx response And webhook delivery history (last_attempt_at, last_status_code, attempt_count) is viewable in the dashboard.
Compliance Validation & Reporting
"As a seller, I want a clear compliance report with reasons and fixes so that I can quickly resolve issues and avoid listing rejections."
Description

Validate assets pre- and post-processing against the resolved category and marketplace policies, producing a compliance score, pass/fail status, and reason codes (e.g., margin too tight, background off-spec). Offer auto-fix suggestions where possible, and block export on critical violations if configured. Provide downloadable CSV/JSON reports, embed per-image compliance metadata in export bundles, and surface a dashboard summary by marketplace/category to identify systemic issues. Expose validation via API for external pipelines to consume results programmatically.

Acceptance Criteria
Pre-Processing Validation Execution
Given a batch of product images with resolved marketplace_id and category_id When the user triggers "Validate (Pre-Processing)" via UI or calls POST /v1/validation/validate with mode=pre Then for each image_id the system returns a result containing: compliance_score (0–100 integer), pass (boolean), reason_codes[] (non-empty if pass=false), ruleset_id, marketplace_id, category_id, evaluated_at (ISO-8601) And reason_codes are from the published taxonomy and include machine_readable_code and human_readable_message And a batch summary is returned containing pass_count, fail_count, average_compliance_score And results are persisted and retrievable via GET /v1/validation/results/{batch_id}
Post-Processing Validation Outcome and Delta
Given a processed batch with pre-validation results available When post-processing completes or the user invokes "Validate (Post-Processing)" Then each image result includes post.compliance_score, post.pass, post.reason_codes, and delta fields: score_delta (post - pre), status_change (pass->fail | fail->pass | unchanged) And images still failing any rule are flagged for review with their remaining reason_codes And post-validation results are associated to the batch/job and visible in UI and API endpoints
Auto-Fix Suggestions and Export Blocking
Given validation detects violations eligible for auto-fix When the user opens an image’s validation detail Then the system lists auto_fix_suggestions[] with fields: fix_id, description, estimated_impact_score_delta, is_lossy (boolean), can_apply_in_place (boolean) And when the user selects "Apply Auto-Fix" the system reprocesses the image, re-runs validation, and updates the result within the same job context And given export_blocking_config.critical_only=true, when any image has a violation with severity=critical, then bulk export is blocked; UI/API responds with a 409 including blocking image_ids and reason_codes; when no critical violations exist, export proceeds
Downloadable Compliance Reports (CSV/JSON)
Given a completed validation run (pre or post) When the user downloads CSV or JSON from UI or calls GET /v1/validation/reports/{batch_id}?format=csv|json Then the file contains one record per image with fields: image_id, marketplace_id, category_id, phase (pre|post), compliance_score, pass, reason_codes (pipe-delimited in CSV; array in JSON), evaluated_at And filenames follow {batch_id}_validation_{phase}_{timestamp}.{csv|json} And totals include pass_count, fail_count, average_compliance_score (JSON section or final CSV row) And downloads are access-controlled and retained for at least 30 days
Embedded Per-Image Compliance Metadata in Export Bundles
Given export is initiated for a processed batch with validation enabled When the export bundle (ZIP) is generated Then for each image the bundle includes a sidecar {image_basename}.compliance.json containing: image_id, marketplace_id, category_id, compliance_score, pass, reason_codes, evaluated_at, phase=post And the bundle root includes compliance_report.json with batch-level summary and an index mapping image files to their compliance metadata paths And the manifest includes checksum for each sidecar to enable integrity verification
Dashboard Summary by Marketplace and Category
Given validation results exist across multiple batches When the user opens the Compliance Dashboard Then the dashboard shows aggregated metrics by marketplace and category: images_validated, pass_rate, average_compliance_score, top_reason_codes (top 5) And the user can filter by date range, marketplace, category, and phase (pre|post) And the user can sort by pass_rate ascending and drill down to the underlying images and batches for any segment
Validation API for External Pipelines
Given a client with a valid API token When it calls POST /v1/validation/validate with payload {batch_id, image_ids[] or image_urls[], mode: pre|post} Then the API responds 202 with job_id and later 200 on GET /v1/validation/results/{job_id} returning per-image fields: image_id, compliance_score, pass, reason_codes[], marketplace_id, category_id, evaluated_at, phase And invalid tokens return 401; missing/invalid parameters return 400 with error_code and message; unknown image_ids return 404 And rate limits are enforced with 429 and retry_after header when exceeded

Auto‑Fix Pipeline

Define a rules-driven sequence of fixes—background enforcement, subject centering, margin correction, DPI/size normalization, and color profile sync—then run it in bulk. Visual before/after diffs, step-by-step logs, and one-click rollback give speed without losing control.

Requirements

Drag-and-Drop Rule Builder
"As a solo e-commerce seller, I want to assemble a reusable pipeline of fixes via a simple drag-and-drop builder so that I can consistently enforce marketplace rules across all my product photos without manual edits."
Description

Provide an intuitive visual editor to define, order, and parameterize a sequence of auto-fix steps (background enforcement, centering, margin correction, size/DPI, color profile). Support conditional branches via marketplace presets, validation to prevent incompatible or redundant steps, reusable templates, and pipeline versioning. Persist pipelines at workspace level with role-based access, and expose full API parity for programmatic creation and updates. Integrate with batch runner, export targets, and preset libraries, offering safe defaults and a live preview of resulting behavior before execution.

Acceptance Criteria
Drag-and-drop step creation and ordering
Given an empty pipeline canvas, When the user drags Background Enforcement, Subject Centering, Margin Correction, Size/DPI Normalization, and Color Profile Sync from the palette onto the canvas, Then the steps appear in the pipeline in the dropped order with sequential step numbers. Given a pipeline with at least 3 steps, When the user drags any step to a new position, Then the new order is visually updated immediately and is persisted upon Save and reflected in the execution plan. Given a focused step in the list, When the user presses Alt+Up or Alt+Down, Then the step moves up or down one position and the new order is persisted upon Save. Given a saved pipeline, When reopened, Then the step order matches the last saved order. Given unsaved ordering changes, When the user attempts to navigate away, Then a prompt requires Save or Discard before leaving.
Parameterization with validation and safe defaults
Given a newly added step, When no parameters are set, Then safe defaults are auto-populated based on the selected marketplace preset or global defaults. Given a parameter field with defined ranges (e.g., margin 0–30%), When the user enters a value outside the allowed range, Then inline validation appears, the field is marked invalid, and Save is disabled until corrected. Given conflicting parameters across steps (e.g., fixed 2000x2000 size followed by DPI normalization that would downscale below minimum), When the conflict is detected, Then a specific error explains the conflict and offers a suggested resolution, and Save remains disabled until resolved or an explicit override is confirmed. Given units are required (px, %, dpi), When the user omits units where applicable, Then the system applies the default unit and shows a non-blocking info message. Given a marketplace preset is applied, When parameters are prefilled, Then all fields reflect the preset values and any deviations by the user are visibly indicated.
Conditional branching via marketplace presets
Given the user adds a condition node and selects Amazon and eBay presets, When branches are configured, Then the canvas displays two distinct labeled branches and each branch can have its own ordered steps. Given a batch of images tagged with marketplace metadata, When Preview Plan is run, Then each image is routed to the branch whose condition matches its tag, and unmatched images follow the default branch if configured. Given overlapping branch conditions, When the configuration is saved, Then the system enforces top-to-bottom priority, warns about overlaps, and requires an explicit priority order before Save is enabled. Given branch conditions are edited, When changes are saved, Then the updated routing is reflected in the execution plan and audit trail.
Incompatible or redundant step validation
Given two Color Profile Sync steps with the same target profile exist, When the user attempts to Save, Then Save is blocked with a redundancy error and a suggestion to remove one step. Given Background Enforcement is set to white and a later step replaces the background with the same white color, When validating, Then a redundancy warning is shown and Save requires either removing one step or acknowledging an explicit override. Given Size Normalization is fixed to 2000x2000 and a subsequent Crop would change dimensions, When validating, Then an incompatibility error is shown and Save is blocked unless the user enables an explicit "Allow dimension change" toggle on the Crop step. Given all validation errors are resolved, When the user clicks Save, Then the pipeline saves successfully with a validation pass summary.
Reusable templates with workspace-level RBAC
Given a user with Editor or higher role opens a pipeline, When they select Save as Template, Then a template is created in the workspace Preset Library and is visible to users per assigned visibility (Private, Team, Organization). Given a user with Viewer role, When browsing templates, Then they can apply a template to a new pipeline but cannot edit or delete it (actions disabled and permission tooltips shown). Given an Owner/Admin edits template permissions, When they assign role-based access, Then only users with Editor+ can modify, and only Owner/Admin can delete. Given a template is updated to a new version, When consumers load it, Then they see an update notification and can choose to adopt the latest version or keep their pinned version.
Pipeline versioning and one-click rollback
Given pipeline v1 exists, When the user makes changes and clicks Save, Then v2 is created with immutable audit metadata (author, timestamp, change summary) and a step/parameter diff is stored. Given the user selects Rollback to v1, When confirmed, Then v3 is created that restores v1 content and records the rollback action, and v3 becomes the active version. Given a batch is executed, When logs are generated, Then each log entry includes the pipeline ID and version used for traceability. Given two versions are selected in the Version History, When Compare is clicked, Then a side-by-side diff of steps, order, conditions, and parameters is displayed.
API parity, live preview, and batch runner integration
Given the public API is used to create or update a pipeline, When the payload includes steps, order, parameters, and conditions, Then server-side validation and error codes match UI validation (same rules, messages, and HTTP status mapping), and the resulting pipeline behaves identically to one created in the UI. Given a sample image ≤10MB, When Live Preview is run, Then a step-by-step preview with before/after diff renders within 5 seconds and reflects current unsaved parameters and order. Given Run Batch is initiated from the pipeline, When execution starts, Then the batch runner uses the selected export targets and logs step-by-step execution with links to the pipeline version and per-image outcomes. Given a batch is triggered via API with pipeline ID and export target IDs, When completed, Then outputs, logs, and status callbacks are identical in structure and content to a UI-triggered run.
Background Compliance Enforcement
"As a seller listing on Amazon and Etsy, I want the pipeline to automatically enforce each marketplace’s background requirements so that my images are accepted and look consistent."
Description

Automatically detect, remove, and replace backgrounds to comply with marketplace rules (e.g., pure white RGB 255/255/255 for Amazon) with configurable tolerance, hex color options, and soft shadow controls. Provide high-fidelity matting for hair/fur, edge smoothing, and halo suppression. Allow per-marketplace presets and per-step parameters, with confidence thresholds that route low-confidence images to manual review or alternative treatments. Log applied settings and outcomes for auditability and export re-use.

Acceptance Criteria
Amazon Pure-White Background Enforcement with Soft Shadow
Given marketplace preset "Amazon" with target background RGB 255/255/255, tolerance ±3, and soft shadow enabled (max opacity 8%, blur radius 12–24 px) And an input image with a noncompliant background When Background Compliance Enforcement runs in the Auto‑Fix Pipeline Then ≥ 99.9% of background pixels fall within the tolerance of the target color And the subject mask IoU against the gold mask is ≥ 0.92 And halo width > 1 px occurs along ≤ 1% of the subject perimeter And background mean luminance ≥ 252 and stddev ≤ 3 within the background region And soft shadow footprint ≤ 5% of canvas area and opacity never exceeds the configured max And output uses sRGB color profile with original pixel dimensions preserved
Per-Marketplace Presets and Parameter Overrides
Given the user selects the "Amazon" preset and overrides tolerance to ±5, target color to #FFFFFF, and soft shadow opacity to 6% When the user saves the preset to the project and runs Background Compliance on a batch Then the run uses the overridden parameters and records them in the run log And per-image backgrounds meet the selected tolerance and shadow limits And switching to the "eBay" preset updates target color and default tolerance as defined by that preset And per-step parameter overrides persist per project and can be reset to preset defaults And editing a preset does not alter artifacts or logs of previously completed runs
High-Fidelity Matting for Hair/Fur with Halo Suppression
Given images containing fine hair/fur edges and semi-transparent strands When the matting engine runs with edge smoothing and halo suppression enabled Then boundary F-score (Fb) on the reference test set is ≥ 0.85 And color spill ΔE00 along a 3 px edge band is ≤ 2.0 median and ≤ 3.0 at the 95th percentile And edge shrink/grow bias is ≤ 1 px on ≥ 95% of the subject perimeter And background contamination pixels (>5% darker than target) comprise ≤ 0.1% of the background area And semi-transparent strand alpha MAE versus ground truth is ≤ 0.10 within detected hair regions
Confidence Threshold Routing to Manual Review or Fallback
Given confidence threshold = 0.90 and routing = Manual Review And an input image yields segmentation confidence = 0.83 When Background Compliance evaluates the image Then no background replacement is committed And the image is placed into the Manual Review queue within 2 seconds with reason = "low_confidence" And a notification event is emitted with image_id and confidence Given confidence threshold = 0.90 and fallback = Neutral Gray #F7F7F7 configured And an input image yields confidence = 0.88 When evaluated Then fallback background is applied and outcome = "fallback_applied" is logged Given an image yields confidence = 0.93 When evaluated Then automatic background replacement is applied and outcome = "auto_applied" is logged
Audit Logging and Exportable Settings for Reuse
Given a completed Background Compliance run on a batch When the run finishes Then a per-image JSON log is created containing image_id, run_id, preset_id, target_color_hex, tolerance, soft_shadow params, matting_model_version, confidence, compliance_pass, outcome, timestamps, and operator_id And a batch-level CSV/JSON export is downloadable within 30 seconds of run completion with the same fields And re-importing the exported settings and re-running on the same inputs produces byte-identical outputs (checksum match) and identical logs And logs are retained ≥ 30 days and are filterable by preset_id and outcome
Subject Centering & Margin Correction
"As a seller, I want my product centered with consistent margins so that my catalog looks professional and thumbnails don’t crop key details."
Description

Detect the primary subject, compute a safe bounding box, and recompose the canvas to center or align to specified anchors while applying configurable margins (uniform or per-edge) and safe zones. Prevent cropping of critical regions, respect multiple-subject scenarios, and optionally apply content-aware canvas expansion or smart scaling within quality thresholds. Surface alignment options (center, top, grid-aligned), record numeric adjustments in logs, and expose presets per marketplace thumbnail heuristics.

Acceptance Criteria
Single-Subject Centering With Uniform Margins
Given an input image with a single dominant subject detected and configuration anchor=center with uniform margin=5% of the shortest canvas side When the Subject Centering & Margin Correction step runs Then the subject bounding box center is within 1% of the canvas center on both axes And the distance from the subject bounding box to each canvas edge equals 5% of the shortest side ±1 px And no part of the subject bounding box lies outside the canvas And if margins cannot be achieved without cropping and expansion/scaling are disabled, the image is returned unchanged with reason=margin_unmet_noop
Per-Edge Margins and Safe Zone Enforcement
Given configuration per-edge margins top=5%, right=12%, bottom=15%, left=8% and safe_zone=4% on all edges When the Subject Centering & Margin Correction step runs Then the distances from the subject bounding box to each corresponding edge equal the configured margins within ±1 px And all subject-to-edge distances are ≥ the configured safe_zone And no cropping of the subject occurs And if the configured margins cannot be met without cropping and expansion/scaling are disabled, the image is returned unchanged with reason=margin_unmet_noop
Multi-Subject Group Alignment (Top Anchor)
Given an image with three detected subjects and configuration multiple_subject_mode=group, anchor=top, top_margin=6%, horizontal_align=center When the Subject Centering & Margin Correction step runs Then the group bounding box top edge is positioned 6% ±1 px from the canvas top And the group bounding box horizontal center is within 1% of the canvas horizontal center And all individual subject bounding boxes remain fully within the canvas (no clipping) And pairwise distances between subject centroids change by ≤2% relative to their original distances (preserving layout)
Critical Region Crop Protection
Given crop_protection=enabled and detected critical regions (faces, logos, text) with bounding polygons When recomposition enforces anchors and margins Then each critical region is fully contained within the output canvas with no pixels removed (area difference from original ≤1 px and no polygon-edge intersection) And if enforcing margins would crop any critical region, the operation selects expansion or scaling according to configuration; if neither is permitted, the image is returned unchanged with reason=crop_protection_blocked
Content-Aware Expansion and Smart Scaling Quality Guard
Given expansion_enabled=true with max_expand=10% per edge, scaling_enabled=true with max_upscale=1.2x, min_ssim=0.97, and min_output_dpi=300 When required margins/anchors cannot be satisfied without cropping Then the algorithm first attempts content-aware expansion up to the lesser of max_expand and the needed pixels per edge And if expansion is insufficient, the algorithm attempts scaling (downscale preferred; otherwise upscale up to 1.2x) And the final output meets min_ssim ≥0.97 compared to the original and maintains output DPI ≥300; otherwise the operation is aborted and the original is returned with reason=quality_guard_blocked And the chosen method (expand/scale) achieves the configured margins within ±1 px
Grid-Aligned Placement and Numeric Adjustment Logging (Marketplace Preset)
Given preset=Amazon_Thumb_v1 with grid=3x3, anchor=grid(2,2), subject_coverage_target=85% ±2%, and min_edge_margin=5% When the Subject Centering & Margin Correction step runs Then the subject bounding box centroid is placed at the grid intersection (2,2) within 1% of canvas width/height And subject coverage (subject bounding box area / canvas area) is within 85% ±2% And each edge margin is ≥5% of the canvas shortest side And the system records before/after numeric adjustments: original and final bounding boxes (x,y,w,h in px), margins per edge (px and %), anchor/grid applied, scale factor, expansion pixels per edge, and coverage %, with a decision reason And the log entry is attached to the image job and retrievable via API within 1 second of step completion
Size, DPI & Color Profile Normalization
"As a seller exporting to multiple marketplaces, I want images standardized to the required size, DPI, and color profile so that I avoid rejections and my images render consistently."
Description

Normalize outputs to target dimensions and DPI using marketplace presets (e.g., 2000×2000 at 300 DPI), with long-edge/short-edge constraints, aspect-ratio handling, and quality-preserving upscaling limits. Convert and embed ICC profiles (default sRGB IEC 61966-2.1), ensure consistent gamma, and strip disallowed metadata while preserving essential EXIF when needed. Enforce format-specific rules (JPEG quality, PNG transparency) and provide per-marketplace export bundles. Validate results and flag assets that exceed safe upscaling thresholds.

Acceptance Criteria
Apply Marketplace Preset: Amazon 2000x2000 @300 DPI
Given a batch of product images with varying dimensions, DPI, and color profiles When the user runs the Auto‑Fix Pipeline with the "Amazon Standard (2000x2000, 300 DPI, sRGB, JPEG Q85)" preset Then each output image is exactly 2000x2000 pixels And the stored DPI metadata is 300x300 And the ICC profile embedded is "sRGB IEC 61966-2.1" And the file format is JPEG encoded at quality factor 85 (±1 verified via quantization tables) And the output passes preset validation without warnings
Respect Long-Edge/Short-Edge Constraints with Aspect-Ratio Handling
Given target size mode = "Long edge = 3000 px; short edge unconstrained; no padding" When the pipeline normalizes dimensions Then the long edge of each output equals exactly 3000 px And the short edge is ≤ 3000 px calculated by proportional scaling with integer rounding And no cropping or padding is introduced And if the input long edge is already ≥ 3000 px, no upscaling is applied
Color Space Conversion and ICC Embedding to sRGB IEC 61966-2.1
Given inputs include Adobe RGB (1998), ProPhoto RGB, Display P3, and untagged images When color normalization runs with default color profile = sRGB IEC 61966-2.1 Then all outputs are converted to and embed the ICC profile "sRGB IEC 61966-2.1" And untagged inputs are treated as sRGB for conversion And the output tone response curve matches sRGB (gamma ≈ 2.2) And the average ΔE00 for an in‑gamut test chart between source proofed to sRGB and output is ≤ 3.0
Enforce Format-Specific Rules: JPEG Quality and PNG Transparency
Given an export bundle containing both JPEG and PNG targets When the pipeline writes JPEG outputs Then JPEGs are encoded at quality 85 (±1), 8‑bit, include embedded sRGB ICC, and have no alpha channel And when the pipeline writes PNG outputs Then PNGs preserve transparency where present or required by preset, are 8‑bit RGB/RGBA as appropriate, and include embedded sRGB ICC (or equivalent gAMA/chrm with consistent gamma)
Metadata Policy: Strip Disallowed Tags, Preserve Essential EXIF
Given metadata policy = "Strip disallowed; preserve essential EXIF" When normalization completes Then GPS, MakerNotes, embedded thumbnails, and proprietary vendor tags are removed And essential EXIF fields (e.g., DateTimeOriginal, Copyright) are preserved when present And orientation is normalized in pixels and the EXIF Orientation tag is set to 1 (or removed) And verification with exiftool shows only the allowed whitelist remains
Safe Upscaling Thresholds and Asset Flagging
Given safe upscaling limit = 2.0× on the long edge When an image would require scaling beyond 2.0× to reach the target dimensions Then the output is upscaled to at most 2.0× on the long edge And any remaining gap to the target is resolved per preset (padding or leaving short edge below target) And the asset is flagged with code UPSCALE_LIMIT_EXCEEDED in the item log and batch report And the batch summary includes the count of flagged assets
Per-Marketplace Export Bundles Generation and Validation
Given the user selects export bundles for "Amazon US" and "Etsy" When the pipeline runs and export completes Then two separate ZIP archives are produced, one per marketplace, named with batch ID and marketplace And each archive contains images that meet that marketplace's target dimensions, DPI, color profile, format rules, and metadata policy And filenames preserve the original basename plus a marketplace suffix And each archive includes a validation report (CSV or JSON) listing pass/fail per asset with reasons for any failures
Bulk Processing Queue & Parallelization
"As a solo seller with hundreds of images, I want to run my pipeline in bulk with reliable progress and retries so that I can finish edits quickly without babysitting the process."
Description

Implement a resilient job orchestration layer to execute pipelines across large batches with configurable concurrency, chunking, retries with exponential backoff, idempotency keys, and resumable checkpoints. Provide pause/resume/cancel controls, per-job prioritization, throughput and error metrics, and autoscaling workers with cost guardrails. Support diverse inputs (uploads, URLs, S3/GCS buckets) and outputs (cloud storage, direct marketplace export), with real-time progress, ETA, and per-item status streaming via WebSocket and webhooks.

Acceptance Criteria
Configurable Concurrency and Chunking for 10k-Image Batch
Given a batch job J with 10,000 items, concurrency=32, and chunkSize=100 When J starts processing Then metrics.active_workers never exceeds 32 and never drops below 1 while queue.backlog > 0, except transient (<5s) restarts And dequeue operations fetch exactly 100 items per pull (final chunk <= 100) And all 10,000 items reach a terminal state (Succeeded or Failed) with succeeded_count + failed_count = 10,000 And no item is processed more than once (per-item processed_count = 1) And total job duration and per-item timings are recorded in metrics with p95 and p99 latencies
Exponential Backoff Retries with Idempotency Keys
Given a retry policy of maxRetries=5, baseDelay=2s, factor=2, jitter=20%, and per-item idempotencyKey And 50 items experience transient 502 errors on first 2 attempts then succeed When the job runs Then each affected item attempts at approximately 0s, ~2s, ~6s and succeeds with attempt_count = 3 And no duplicate outputs are written for any idempotencyKey (exactly one output per key) And for items with permanent errors (e.g., 404), processing stops after 1 + 5 attempts and status = Failed with last_error_code recorded And retries for a given idempotencyKey are mutually exclusive across workers (no concurrent attempts for the same key)
Resumable Checkpoints with Pause/Resume/Cancel Controls
Given a job K of 5,000 items with concurrency=16 When the user requests Pause after 2,000 items are Succeeded Then no new items begin processing within 5s and in-flight items are allowed to finish And a durable checkpoint persists counts: completed>=2,000, in_progress<=16, remaining=5,000 - completed - in_progress When the user requests Resume Then processing restarts from remaining items with zero reprocessing of completed items (no duplicate outputs) When the user requests Cancel Then no new items start and in-flight items are aborted within 30s And final tallies reflect Succeeded (completed before cancel), Aborted (stopped in-flight), and Canceled (not started), and job status = Canceled
Per-Job Priority Scheduling and Fairness
Given two queued jobs J1 (priority=High) and J2 (priority=Low) and total worker capacity=16 When both have backlog > 0 Then within 10s the scheduler allocates at least 12 workers to J1 and no more than 4 to J2 until J1 backlog reduces And upgrading J2 from Low to High rebalances within 15s so both share capacity fairly (8±2 each if equal backlog) And starvation protection ensures Low priority jobs receive at least 1 worker whenever backlog > 0 And scheduling decisions and allocations are emitted to metrics and logs with timestamps
Realtime Progress, ETA, and Per-Item Status Streaming
Given a client subscribes via WebSocket to job J and a webhook endpoint is configured When J runs Then progress updates stream at 1–5 Hz with end-to-end delivery latency <= 2s p99 And each per-item event includes item_id, status (Queued|Running|Succeeded|Failed|Aborted|Canceled), attempt_count, error_code (nullable), started_at, finished_at, and is schema-valid And per-item events are emitted in monotonic order for that item (no regressions in status) And ETA is published and updates at least every 10s with error <= ±20% once >10% of items are completed And webhooks deliver batched events with HMAC-SHA256 signatures; non-2xx responses are retried with exponential backoff up to 10 attempts and include idempotency headers
Autoscaling Workers with Cost Guardrails
Given autoscaling is enabled with minWorkers=2, maxWorkers=100, target_queue_latency=30s, and cost_cap_usd_per_day=50 When backlog grows such that projected latency > 30s Then the scaler increases workers toward the target without exceeding maxWorkers or projected daily spend > cost_cap And if adding workers would breach the cost cap, scaling is capped and a CostGuardrailHit alert is emitted and visible in the dashboard And when backlog drains and queue latency < 5s for 5 consecutive minutes, workers scale down toward minWorkers without interrupting in-flight items And all scaling decisions are recorded with rationale: backlog, latency, worker_count, spend_projection
Multi-Source Inputs and Multi-Destination Outputs
Given a batch with 50 local uploads, 50 HTTPS URLs (with up to 3 redirects), 50 S3 URIs (role-based auth), and 50 GCS URIs (service account) When the job runs Then all accessible inputs are fetched successfully; failures are categorized with specific error codes (e.g., DNS_ERROR, 403_FORBIDDEN, NOT_FOUND, TIMEOUT) And outputs are configured to S3 (bucket/prefix A), GCS (bucket/prefix B), and a direct marketplace export; succeeded items produce exactly one artifact per configured destination And partial destination failures do not block other destinations; per-item per-destination statuses are tracked and reported And overwrite=true results in idempotent writes (same object keys) and overwrite=false results in unique versioned keys; checksums are recorded in logs
Visual Diffs, Step Logs & One-Click Rollback
"As a meticulous seller, I want to see exactly what changed at each step and revert if needed so that I retain control while benefiting from automation."
Description

Provide a non-destructive review interface with before/after sliders, zoom, grid view, and per-step thumbnails. Capture a detailed step log including parameters, confidence scores, and pixel deltas, exportable as JSON for audit. Allow selecting any step to revert an image or an entire batch, maintaining version history for originals and intermediates with lifecycle policies to control storage costs. Integrate with the queue to access per-item logs and enable batch-level rollback actions safely and quickly.

Acceptance Criteria
Before/After Slider, Zoom, and Grid Review
- Given a processed image pair is loaded in Review, when the user drags the before/after slider, then the preview updates at ≥20 FPS with interaction latency ≤50 ms and the split remains aligned pixel-for-pixel. - Given the user sets zoom between 50% and 400%, when panning, then the image stays within bounds, the zoom/pan state persists per image, and no visual tearing occurs. - Given Grid View is toggled, when 2–100 images are loaded, then first paint occurs within ≤800 ms and additional thumbnails lazy-load without blocking UI interactions. - Given color-managed display, when viewing before/after, then the same color profile is applied to both panes and delta-E deviation ≤1 from the step output profile rendering.
Per-Step Thumbnail Timeline & Metadata Peek
- Given a pipeline with N steps, when the Review opens, then a timeline lists all steps in order with index, name, duration, and a thumbnail (≤512 px longest side) for each. - Given a step thumbnail, when hovered or focused, then a popover shows key parameters, confidence score (0–1), and time taken in ms. - Given a step is selected, when the user clicks it, then the main preview switches to that step’s output within ≤300 ms and the selection is highlighted in the timeline.
Detailed Step Log Capture (Parameters, Confidence, Pixel Deltas)
- Given the pipeline executes, when a step completes, then a log entry is persisted with: step_id, name, start_ts, end_ts (ms precision), parameters (actual values), confidence scores per detector, input_asset_id, output_asset_id, output_checksum, pixel_delta_count, pixel_delta_percent, and error (nullable). - Given large images up to 30 MP, when computing pixel deltas, then the computation finishes within ≤1 s per step at the 95th percentile. - Given an image is completed, when fetching its step log, then entries are returned in execution order with schema_version and total_processing_time_ms.
JSON Export of Step Log
- Given a completed image or batch, when the user clicks Export JSON, then a JSON file conforming to schema version v1 is generated with steps[], assets[], summaries, and checksums, and downloads within ≤3 s for up to 500 images. - Given the JSON export, when validated against the published JSON Schema, then it passes validation with zero errors. - Given potentially sensitive parameters, when exporting, then secret values are redacted according to an allowlist and flagged as "redacted": true.
One-Click Rollback to Selected Step (Single Image)
- Given an image with step history, when the user selects a prior step and clicks Rollback, then a new version is created pointing to that step’s output and all previous versions (including original) remain intact. - Given rollback executes, when it completes, then the UI shows updated version number, an audit log entry (actor, timestamp, from_version, to_version), and end-to-end time ≤2 s at the 95th percentile. - Given a rollback has occurred, when the pipeline is re-run, then processing resumes from the rolled-back step’s output without reprocessing earlier steps unless explicitly requested.
Batch-Level Rollback via Queue with Safety & Progress
- Given a batch is selected in Queue, when the user triggers rollback to step K, then a confirmation modal shows item count, target step, and effects; proceeding enqueues per-item rollback jobs idempotently. - Given rollback jobs run, when some items fail, then failures are retried up to 3 times with exponential backoff and are visible in per-item logs; successful items remain rolled back and are not retried. - Given the batch rollback is in progress, when viewing the Queue, then progress (% complete), ETA, and counts (success/fail/retry) update at least every 5 s. - Given a queue item row is expanded, when the user clicks View Log, then the per-item step log opens in a modal with a JSON view and an Export button.
Version History & Storage Lifecycle Policies
- Given versioning is enabled, when viewing an image, then a version history lists all versions (original, intermediates, finals) with timestamp, originating step name, and storage class, and any version can be previewed. - Given lifecycle policies are configured (e.g., intermediates retained 30 days), when a version exceeds its retention, then it transitions to the configured storage class or is deleted according to policy and the action is audit-logged. - Given lifecycle enforcement runs, when policies execute, then total storage usage is recalculated and reflected in Settings within ≤15 minutes of the run completing.

Smart Margins

AI subject detection sets compliant, composition‑aware margins per marketplace and aspect ratio. Smart Margins maximizes product fill while respecting safe-zone rules, preserving natural framing for craft/vintage items and boosting CTR on main images—no more guesswork crops.

Requirements

Marketplace Safe-Zone Rule Engine
"As a solo e-commerce seller listing across multiple marketplaces, I want PixelLift to automatically apply each marketplace’s margin and safe-zone rules so that my images are compliant without manual research or trial-and-error."
Description

Centralized, versioned rule definitions for marketplace-specific safe zones and aspect ratios (e.g., Amazon, Etsy, eBay, Walmart, Shopify), including minimum margins, edge-clearance, allowable background colors, logo/text restrictions, and regional variants. Automatically maps selected marketplace and listing category to the correct rule set, with fallbacks and deprecation handling. Exposes a rules API to the cropping pipeline so margin calculations remain compliant by design, and updates seamlessly as policies change without requiring code deployments. Reduces image rejections, ensures consistency across batches, and shortens onboarding for new marketplaces.

Acceptance Criteria
Retrieve Effective Rule Set by Marketplace, Category, and Region
Given a request to GET /rules with marketplace="amazon", category="apparel", region="US", aspectRatio="1:1" When the rules API is called Then the response is HTTP 200 with a JSON body containing fields: ruleId, version, marketplace, category, region, aspectRatios, minMarginPercent, edgeClearancePx, allowedBackgroundColors, textLogoAllowed, deprecated And the returned values match the active configuration for the specified marketplace/category/region And the response includes headers ETag and Cache-Control
Fallback Rules for Unmapped Categories and Deprecated Versions
Given marketplace="etsy", category="unknown-cat", region="US" When GET /rules is called Then the API returns HTTP 200 with fallback=true and a ruleId for the marketplace default And an event RULE_FALLBACK_USED is logged with marketplace, category, and region And no 5xx error occurs Given a request specifying version="deprecated-version" When GET /rules is called Then the API returns HTTP 200 with the current active version and metadata deprecatedPreviousVersion="deprecated-version" And a Deprecation header is present
Versioning, Activation, and Audit of Rule Updates without Deployments
Given a new rule set is published via the rules admin/config store When the new version is activated Then within 5 minutes the API begins serving the new version for applicable queries without any service restart or code deployment And the previous version remains retrievable by explicit version query for at least 90 days And an audit record captures publisher identity, timestamp, change diff, and rollout scope
Rules API Performance and Availability Under Load
Given a sustained load of 200 requests per second with realistic parameter mixes When the rules API is exercised for 15 minutes Then P95 latency <= 100 ms and P99 latency <= 250 ms And error rate (5xx) <= 0.5% And availability over a 30-day window >= 99.9%
Schema Backward Compatibility and Field Completeness
Given existing clients pinned to schemaVersion=1 When schemaVersion=2 adds optional fields Then requests without schemaVersion still succeed with backward-compatible responses containing required fields: ruleId, version, marketplace, category, region, aspectRatios, minMarginPercent, edgeClearancePx, allowedBackgroundColors, textLogoAllowed And no required field is removed or renamed between versions And a JSON Schema document is published and validates 100% of responses
Cropping Pipeline Integration: Rule Consumption and Compliance Guarantees
Given the cropping pipeline processes a batch of 500 images for marketplace="walmart", category="home-decor", region="US" When the pipeline calls the rules API and applies Smart Margins Then 100% of output images conform to minMarginPercent and edgeClearancePx from the rule set And output backgrounds are within allowedBackgroundColors And no text or logo overlays are present when textLogoAllowed=false And the pipeline logs the ruleId and version used for each image
Regional Variant Precedence and Defaulting
Given marketplace="amazon" has a region-specific rule for region="JP" and a default rule When GET /rules is called with region="JP" Then the JP rule is returned and deprecated=false And when GET /rules is called without region, the default rule is returned And when GET /rules is called with region="CA" and no CA-specific rule exists, the default is returned with fallback=true
AI Subject & Bounding Mask Detection
"As a seller, I want the system to accurately detect the true product boundaries so that margins are set around the subject rather than props or empty space."
Description

High-accuracy subject detection that generates a tight segmentation mask and bounding box around the primary product, robust to translucent materials, reflective surfaces, fine edges, and multi-item bundles. Identifies orientation, dominant facing direction, and occlusions; assigns confidence scores and fallbacks (e.g., center-weighting or manual subject pick) when uncertain. Handles transparent-background inputs and preserves delicate, craft/vintage silhouettes to avoid unnatural truncation. Outputs normalized geometry used by downstream margin optimizers for precise, composition-aware framing.

Acceptance Criteria
Single-Product Image: Mask and Bounding Box Accuracy
Given a test set of single-product photos with human-annotated masks and boxes When the detector processes each image Then for at least 95% of images, the segmentation mask IoU with ground truth is ≥ 0.88 And for at least 95% of images, the bounding box IoU with ground truth is ≥ 0.92 And the mask area is > 0 and the bounding box lies fully within image bounds for 100% of images
Translucent/Reflective/Fine-Edge Robustness
Given a test set containing translucent (glass), reflective (metal), and fine-edge (fringe/hair) products with expert masks When the detector processes each image Then the boundary F1 score at 2 px tolerance is ≥ 0.82 (dataset-level) And recall within semi-transparent regions (alpha > 0.2) is ≥ 0.85 And median edge halo/fringe thickness around the mask boundary is ≤ 1 px at 1600 px longest side
Multi-Item Bundles: Primary Cluster Detection
Given bundle product images with ground-truth primary-item cluster masks and prop annotations When the detector processes each image Then the union mask of detected primary items has IoU ≥ 0.85 with the ground-truth primary cluster And non-primary prop inclusion is ≤ 10% by pixel area (precision ≥ 0.90) And the bounding box tightly encloses all primary items with ≤ 2% extra area beyond the primary cluster (per image)
Orientation and Dominant Facing Direction Estimation
Given a labeled set with facing_direction ∈ {front,left,right,back,top,bottom} and yaw/pitch/roll angles for each product When the detector processes each image Then facing_direction top-1 accuracy is ≥ 90% And median absolute error for yaw and pitch is ≤ 15° (90th percentile ≤ 25°) And median absolute error for roll is ≤ 5° (90th percentile ≤ 10°) And outputs include per-attribute confidence scores in [0,1]
Uncertainty Handling: Confidence, Occlusions, and Fallbacks
Given any processed image When subject_confidence < 0.75 or occlusion_percent ≥ 0.15 Then fallback_used = true is set and a center-weighted fallback box is produced, centered and sized to 80% of the shorter image dimension, with aspect ratio matching the target marketplace ratio parameter And a manual_subject_pick flag is exposed for the UI and a detection overlay is stored for review And an event is logged with reason ∈ {low_confidence, occlusion} including numeric scores And occlusion_percent is reported with absolute error ≤ 7% against ground truth on the occlusion-labeled set
Transparent-Background Inputs Handling
Given PNG inputs with alpha channels and corresponding ground-truth derived from alpha where applicable When the detector processes each image Then non-transparent pixels (alpha > 0.01) are preserved in the final mask with IoU ≥ 0.95 in regions without soft edges And interior transparent holes ≥ 10 px diameter are preserved in ≥ 95% of cases And the tight bounding box derived from the mask has padding ≤ 2 px at 1600 px longest side
Normalized Geometry Output Contract for Margin Optimizer
Given any processed image When results are emitted Then bounding_box is returned as normalized [x_min,y_min,x_max,y_max] within [0,1] And segmentation_mask is returned with encoding (RLE or PNG) plus scale and origin mapping to original dimensions And orientation includes facing_direction enum and yaw/pitch/roll (float degrees) And occlusion_percent ∈ [0,1] and subject_confidence ∈ [0,1] are present And all fields validate against schema v1.x and are accepted by the downstream margin optimizer in 100% of integration test cases
Composition-Aware Margin Optimizer
"As a seller, I want the main subject to fill the frame attractively yet naturally so that my thumbnails stand out and drive higher CTR without looking cramped or cut off."
Description

Algorithm that computes optimal crop and padding from the detected mask and marketplace rules to maximize product fill while preserving natural headroom and breathing room. Balances rule-of-thirds anchoring, facing-direction bias, and protection of critical details (e.g., labels, handles) to avoid amputations. Supports tunable fill targets per category (e.g., apparel vs. home decor) and generates consistent framing across a set. Produces precise crop rectangles per target aspect ratio and exposes parameters for A/B variant generation, all while guaranteeing compliance with safe-zone constraints.

Acceptance Criteria
Safe-Zone Compliance Across Marketplaces
- Given a detected subject mask and marketplace safe-zone rules per target aspect ratio, when the optimizer generates a crop and padding, then 0 subject-mask pixels lie outside the marketplace-defined safe zone for that aspect ratio. - Then left/right/top/bottom margin percentages fall within the marketplace rule-defined min/max bounds. - If target fill conflicts with safe-zone bounds, then the optimizer reduces fill until both constraints are met; safe-zone compliance is never violated. - Then the output includes a compliance flag = true and a list of any rules that constrained fill (empty when none).
Category Fill Target Adherence with Tolerance
- Given a category configuration with target_fill_pct and tolerance_pct, when the optimizer computes the crop, then achieved_fill_pct ∈ [target_fill_pct − tolerance_pct, target_fill_pct + tolerance_pct]. - If safe-zone or critical-detail protection prevents meeting target within tolerance, then the result is marked reason="FILL_CONSTRAINT" and includes achieved_fill_pct and blocking_constraints; no subject pixels are cropped. - Then achieved_fill_pct is measured as (subject-mask pixels within frame) / (frame pixels) × 100 and logged to 1 decimal place per image.
Natural Headroom and Critical Detail Protection
- Given critical-detail submasks (e.g., labels, handles, text) from detection, when the optimizer crops, then the crop does not intersect any critical-detail submask (i.e., preserved_pct for each submask = 100%). - Then the minimum distance from any critical-detail pixel to the nearest crop edge ≥ configured min_critical_padding (in % of the shorter frame side or px), unless safe-zone requires more. - If a subject "top anchor" is provided, then headroom (top edge to top anchor) ∈ [min_headroom_pct, max_headroom_pct] of frame height; otherwise default centering tolerance ±2% is applied. - If constraints are mutually incompatible, then the optimizer selects the smallest fill reduction that restores all protections and records the applied headroom and padding values.
Facing-Direction Bias and Rule-of-Thirds Anchoring
- If facing_direction ∈ {left,right} is available, then leading space on the facing side − trailing space ∈ [bias_min_pct, bias_max_pct] of frame width; if unknown, horizontal centering error ≤ 2% of frame width. - Then the subject centroid aligns to a configured rule-of-thirds line with offset ≤ 3% of the corresponding frame dimension (vertical and/or horizontal per config). - Then vertical balance (top vs. bottom breathing room) respects configured vertical_bias_pct ± 2%. - All above constraints must remain safe-zone compliant; if conflicts arise, thirds alignment may relax before bias, and bias may relax before fill.
Set Consistency Across Batch
- Given a batch of N images for the same product and category with identical config, when optimized, then the standard deviation of achieved_fill_pct across the set ≤ 2.0 percentage points. - Then the median absolute deviation of the subject centroid (normalized to frame) across the set ≤ 2% of frame width (x) and ≤ 2% of frame height (y). - Then per-side margin variance (std dev of left/right/top/bottom margins as % of frame) ≤ 2 percentage points. - Any image constrained by safe-zone or critical-detail rules is excluded from variance metrics but reported separately with reason codes.
Multi-Aspect Outputs and Precise Crop Rectangles
- Given a list of target aspect ratios, when optimized, then for each aspect ratio the system outputs a crop rectangle within source bounds with integer x,y,width,height and normalized [0,1] values with ≥ 3 decimal places. - Then abs((width/height) − target_aspect) ≤ 1 pixel-equivalent tolerance after rounding; any required padding is applied per config (color/alpha) and recorded. - Then the sum of crop+padding exactly matches the target aspect ratio within 0.1% relative error. - Each output includes a schema-valid payload containing: aspect_ratio, crop_px, crop_norm, achieved_fill_pct, margins_pct per side, compliance_flags, and reason codes (if any).
A/B Variant Generation and Distinctness
- Given a requested set of K variants with specified parameter deltas (e.g., fill_target ±Δ, thirds_anchor options, bias adjustments), when generated, then exactly K outputs are produced per image×aspect ratio with their parameter sets echoed in metadata. - Then each variant complies with safe-zone and critical-detail protections (0 violations) and meets category tolerance rules relative to its own target parameters. - Then the IoU between any two variants’ crop rectangles for the same image×aspect ratio ≤ 0.95 to ensure distinctness. - API latency for generating up to K=3 variants for a 3000×3000 source image ≤ 1.0s p95 on reference hardware/config; results include timing metrics.
Multi-Aspect Auto-Cropping & Preview
"As a seller, I want to preview margins across all required aspect ratios and devices so that I can be confident my images will render well everywhere before exporting."
Description

Automated generation of compliant crops for multiple required aspect ratios from a single source image (e.g., 1:1, 5:4, 4:5, 16:9), maintaining visual consistency and subject anchoring. Interactive previews simulate marketplace thumbnails on desktop and mobile, with optional background color simulation and safe-zone overlays. Detects potential cutoffs for text or fine details and suggests alternate crops. Enables one-click export of all aspect variants per marketplace profile.

Acceptance Criteria
Compliant multi-aspect crops generation
Given a single source image and a selected marketplace profile that specifies required aspect ratios [1:1, 5:4, 4:5, 16:9] and safe‑zone rules, When the user clicks Auto‑Crop, Then the system generates one crop per required aspect ratio within 2.0 seconds per image, And each crop passes the profile’s safe‑zone validator with 0 violations, And the primary subject’s bounding box fills 70–95% of the shortest frame dimension as defined by the profile, And no upscaling beyond the source pixel dimensions occurs; if target output exceeds source, the system displays a resolution warning.
Interactive desktop/mobile thumbnail preview with safe‑zone and background simulation
Given at least one generated crop variant, When the user toggles between Desktop and Mobile preview modes, Then previews render at marketplace‑accurate dimensions (±1 px) and aspect ratios, And the mode toggle responds within 100 ms on a standard test device, And the user can toggle the safe‑zone overlay on/off and the overlay aligns to profile regions within ±1 px, And background simulation allows switching between at least 3 presets (white #FFFFFF, light gray #F5F5F5, dark #0A0A0A) and the marketplace default without altering the underlying image data.
Cutoff risk detection and alternative crop suggestions
Given a generated crop variant, When any detected text, logo, or high‑frequency edge segment lies within 12 px of a crop boundary, Then the system flags a Cutoff Risk for that variant and highlights the at‑risk region, And it generates at least one alternative crop per affected aspect that moves the at‑risk region ≥12 px inside the frame while keeping the subject anchor shift ≤3% of frame width/height, And when the user clicks Apply Suggestion, the variant is replaced and the risk flag is cleared.
One‑click export of all aspect variants per marketplace profile
Given a marketplace profile with N required aspect ratios and at least one image with generated variants, When the user clicks Export All, Then exactly N files per image are exported with filenames in the pattern {SKU}_{aspect}_{marketplace}.{ext}, And files are encoded sRGB JPEG at profile‑specified max dimensions with quality 85% unless overridden by the profile, And export completes within 5 seconds for a set of 4 aspects per image on a 3000×3000 px source image on a standard test machine, And a progress indicator shows per‑image and overall completion, and any failures are listed with actionable error codes.
Subject anchoring consistency across aspect variants
Given the detected subject anchor point and orientation vector, When generating all aspect crops for the selected profile, Then the subject anchor remains within ±3% of its relative position across variants (normalized to frame width/height), And horizon/vertical alignment deviates by ≤2° between variants when a dominant axis is detected, And visual jitter between adjacent variants measured by anchor displacement is ≤15 px on a 1080 px reference frame.
Marketplace profile switch re‑crops and updates previews
Given generated variants for Marketplace A, When the user switches the marketplace profile to Marketplace B with different safe‑zones and aspect requirements, Then new crops for Marketplace B are generated within 2.5 seconds per image, And all previews update to reflect Marketplace B’s thumbnail specs, overlays, and background defaults, And any variants that cannot meet Marketplace B’s minimum resolution are flagged with a warning before export.
Batch Overrides & Presets
"As a power user, I want to apply Smart Margins in bulk but still override edge cases so that I keep speed without sacrificing quality."
Description

Bulk application of Smart Margins with per-marketplace, per-category presets and per-image override controls. Provides quick nudges, anchor locks, and padding adjustments without breaking compliance, plus the ability to exclude outliers from batch processing. Saves reusable presets at account and team levels, supports keyboard shortcuts, and records an audit trail of manual adjustments for repeatability. Preserves throughput while allowing nuanced control for edge cases.

Acceptance Criteria
Batch apply marketplace/category Smart Margins preset to selected images
Given a selection of at least 100 images tagged with a marketplace and category and a matching Smart Margins preset exists When the user clicks Apply to Batch Then the system applies the preset to all selected images and generates outputs for each aspect ratio defined in the preset And each processed image's compliance status equals "Compliant" And a progress indicator displays real-time counts for processed, pending, and failed And upon completion a summary shows total processed, failed with error reasons, and average processing rate And, compared to a baseline run without overrides on the same dataset, throughput is >= 95%
Per-image override enforces compliance
Given an image with a selected marketplace and category and a preset applied When the user performs manual margin adjustments (drag, nudge, anchor lock, padding) Then adjustments are constrained to remain within marketplace safe-zone rules And if an attempted adjustment would violate a rule, the UI prevents the change and shows an inline message And if Auto-correct is enabled, the system snaps the margin to the nearest compliant value And clicking Save persists the override and sets image.override=true while keeping compliance status "Compliant" And clicking Reset reverts the image to the preset values
Exclude outliers from batch application
Given a batch selection with one or more images marked Excluded When the user runs Apply to Batch Then excluded images are not processed and do not change And the completion summary reports counts for processed, failed, and excluded separately And excluded state persists until manually cleared And toggling Include on an excluded image immediately returns it to the next batch run
Quick nudges, anchor locks, and padding adjustments
Given an image in override mode with Smart Margins preview visible When the user presses Arrow keys Then the margin nudges by 2 px per keypress (or 10 px with Shift+Arrow) And when Anchor Lock is enabled on an edge or center, resizing preserves that anchor's position And padding can be adjusted in 1% increments (or 2 px when unit=px) within a 0–20% range And the preview updates within 100 ms of input and remains compliant
Keyboard shortcuts for overrides and batch controls
Given an image selected in override mode and no text input is focused When the user presses supported shortcuts Then Arrow nudges margins by 2 px and Shift+Arrow by 10 px And L cycles anchor lock points And P focuses the padding input And E toggles Exclude for the current image And Cmd/Ctrl+S saves overrides And Esc cancels unsaved changes And Cmd/Ctrl+/ shows the shortcuts help overlay And when a text input is focused, these shortcuts do not trigger except Cmd/Ctrl+S
Save and share reusable presets at account and team levels
Given a user with Admin or Editor role defines Smart Margins settings for a marketplace and category When they choose Save Preset and select scope Account or Team Then the preset is saved with key {marketplace, category, aspect ratios} and an auto-incremented version number And Account-scope presets are visible to all users in the account; Team-scope presets are visible only to members of that team And users with Viewer role can apply but cannot edit or delete presets And setting a preset as Default for a marketplace/category makes it preselected in new batch runs And applying a newer preset version does not retroactively alter images processed with older versions
Audit trail of manual adjustments and replayability
Given a user makes and saves manual Smart Margins adjustments on an image When the save completes Then an audit record is created with image_id, user_id, timestamp, preset_version, actions [{type, before, after}], compliance_status_after, and batch_id (if any) And the audit history is viewable per image and exportable as CSV and JSON And selecting Replay on a history entry reapplies the recorded adjustments to a similar image and produces identical margins within ±1 px And repeated replays are idempotent and do not duplicate audit entries
Compliance Validator & Export Annotations
"As a seller, I want clear validation and autofixes before export so that my images get accepted on the first try."
Description

Final validation step that checks all crops against active rule sets and flags violations with human-readable reasons (e.g., subject within restricted zone, margin below minimum). Offers one-click autofix where possible, and blocks export only when necessary. Embeds compliance metadata (rule set ID, version, parameters) into export manifests and optionally overlays guides for internal QA. Integrates with PixelLift’s bulk export pipeline to ensure only compliant, ready-to-upload images are delivered.

Acceptance Criteria
Batch Validation Across Multiple Marketplaces
Given a batch of up to 500 images with per-image targets (marketplace, aspect ratio) When validation is triggered Then each image is evaluated against the correct active rule set for its marketplace and aspect ratio And the validator outputs per-image results including: result (Pass|Fail), rule_set_id, rule_set_version, evaluated_rules_count, violations[] And 95th percentile validation latency is <= 500 ms per image for batches up to 500 images And processing is isolated per image so no image uses another image's targets
Human-Readable Violation Reporting & Autofix
Given an image that violates minimum margin and safe-zone rules When validation completes Then each violation includes a human-readable message with rule name, observed value, required value, and suggested fix And each violation includes a stable machine-readable code Given a violation supports autofix When the user clicks "Autofix" Then the system applies the fix, revalidates the image, and updates the result within 2 seconds And if all violations are resolved the image result changes to Pass and an autofixes_applied list is recorded And if violations remain they are listed with reasons why no autofix is available And autofix does not upscale beyond 110% nor crop more than 1% of the subject bounding box
Selective Export Blocking and Batch Continuation
Given a batch contains both Pass and Fail images When export is initiated Then only images with violations marked blocking are withheld from export And Pass images are exported without delay And non-blocking violations (warnings) do not block export and are included in the manifest And the export summary shows counts for total, exported, blocked, and autofixed images And the export operation completes successfully with per-image statuses available via UI and API
Manifest Compliance Metadata Embedding
Given images are exported When the export manifest is generated Then each exported image record contains compliance metadata: rule_set_id, rule_set_version, validated_at (UTC ISO 8601), result (Pass|Fail|Warning), parameters (key:value) used in validation, and violations[] (code, message, severity, observed, required) And the manifest conforms to schema version "compliance.v1" and passes JSON Schema validation And a SHA-256 checksum of each image binary is included And revalidating the same image with the same rule set version yields identical compliance fields
QA Overlay Guides Generation
Given the "Include QA guides" option is enabled When export runs Then a separate overlay asset is generated per image showing safe zones, margins, and subject outline And overlays are 1:1 pixel aligned, non-destructive to the production image, and stored as distinct files And overlays are named predictably (e.g., <image_basename>_qa.png) and referenced in the manifest under compliance.overlay_url And overlay generation adds <= 100 ms per image on average for batches up to 500 images Given the option is disabled Then no overlay assets are produced and no overlay URLs appear in the manifest
Rule Set Versioning and Snapshot Consistency
Given rule sets can change over time When a batch validation starts Then the validator uses a consistent snapshot of rule_set_version per (marketplace, aspect_ratio) for the duration of the batch And the snapshot identifier is recorded with each image's results and in the export manifest And subsequent rule changes do not alter stored results for that batch And re-running validation with the same snapshot produces identical Pass/Fail outcomes And if a referenced rule set is deprecated before export, the recorded snapshot is still used for that batch while new runs default to the latest active version
Margin Variant Generator for A/B Testing
"As a seller focused on improving CTR, I want multiple compliant margin variants so that I can A/B test thumbnails and choose the framing that performs best."
Description

Controlled creation of multiple compliant margin variants by adjusting fill percentage, vertical bias (top/bottom), and centering within allowable tolerances. Ensures all variants pass safe-zone checks, labels them consistently (A/B/C), and integrates with export naming conventions and batch randomization. Stores variant parameters for later performance analysis and supports automatic best-variant selection when paired with external CTR data.

Acceptance Criteria
Batch Generation of Margin Variants per Marketplace Template
- User can specify 2–5 variants per image (default 3) for a selected marketplace template and aspect ratio. - Fill percentage per variant adjustable from 60% to 95% of safe-zone bounding area in 1% increments; default 90%. - Vertical bias adjustable from -20% (top) to +20% (bottom) of image height in 1% increments; default 0%. - Horizontal centering offset adjustable from -5% (left) to +5% (right) of image width in 1% increments; default 0%. - Generated variants maintain the marketplace’s required aspect ratio and background rules. - For identical inputs, the generator produces identical outputs (deterministic). - Batch operation processes all selected images and produces the requested number of variants per image without manual intervention.
Safe-Zone Compliance Validation for All Variants
- 100% of generated variants pass the selected marketplace safe-zone and margin rules. - If an intended variant would violate rules, the system auto-adjusts within these limits: fill ±3%, vertical bias ±5%, centering ±2% to achieve compliance. - If compliance cannot be achieved within limits, the variant is skipped, the batch continues, and the item is flagged as Non-compliant with a reason code. - A per-image compliance report lists each variant’s status (Pass/Adjusted/Skipped) and any auto-adjustment deltas applied.
Variant Labeling and Export Naming Integration
- Variants are labeled sequentially A, B, C, D, E per source image; labels reset per image. - Export filenames include base name, variant label, marketplace code, aspect ratio, and parameter shortcodes for fill (f), bias (b), center (c), separated by double underscores. - Each exported filename matches: ^(.+?)__([A-E])__([A-Z0-9]{2,8})__(\d+x\d+)__f(6\d|7\d|8\d|9[0-5])_b[+-](\d|\d\d|20)_c[+-](\d|\d\d)\.(jpg|jpeg|png)$ - Export manifest includes a mapping of source base name to variant label and filename.
Batch Randomized Assignment of Variants
- When randomization is enabled, the system assigns variants across the batch so that counts per variant differ by no more than 1 (balanced allocation). - An optional numeric seed yields reproducible assignments; identical input set + seed => identical mapping. - The randomization mapping (imageId → variantLabel) is written to the export manifest.
Variant Parameter Persistence and Analytics Export
- For each exported variant, the system stores: imageId, variantLabel, marketplaceCode, aspectRatio, fillPct, verticalBiasPct, centerOffsetPct, safeZoneStatus, autoAdjustments, generatorVersion, createdAt. - Parameters are retrievable via API and downloadable as CSV, one row per exported file, with consistent column headers and data types. - All percentage parameters are stored as integer percentages (e.g., 90, -5, +3) with sign for directional values. - Retrieval returns exactly the parameters that were used to generate the exported asset (no recomputation).
Automatic Best-Variant Selection via External CTR Data
- System accepts CTR data import via CSV or API with columns: imageId, variantLabel, impressions, clicks; validations: impressions >= clicks, non-negative integers. - Best-variant selection runs when every compared variant for an image has at least 200 impressions. - Selected winner is the variant with the highest CTR (clicks/impressions); ties within 0.5 percentage points result in No winner. - Upon selection, the winning variant is marked as default for future exports of that image and the decision is logged with timestamp and dataset reference. - The job produces a report listing imageId, evaluated variants, impressions, clicks, CTRs, winner/decision status.

Text Guard

OCR and watermark detection catch disallowed text, badges, borders, or promo stickers before export. Text Guard can auto‑remove overlays via content‑aware cleanup, or require approval when packaging text must remain—cutting instant rejection risks on strict marketplaces.

Requirements

Multilingual OCR with Layout Awareness
"As a marketplace seller, I want PixelLift to accurately read any text present on my product images so that disallowed text can be caught and handled automatically before I export."
Description

Implement an OCR subsystem that reliably detects and extracts text across multiple languages, fonts, and orientations (rotated, curved, or perspective-warped) within product images. The engine must return line- and word-level bounding boxes, reading order, and confidence scores, and be resilient to low contrast, translucent overlays, and complex backgrounds. Tight integration with PixelLift’s batch pipeline is required to process large image sets in parallel, emitting a structured JSON payload per image for downstream policy checks. The module should expose a service interface usable by both the web app and export API, cache repeated runs for identical inputs, and support GPU acceleration where available. This capability enables early identification of disallowed text elements and reduces marketplace rejections by ensuring text content is accurately discoverable before export.

Acceptance Criteria
Multilingual Text Detection Coverage
Given the v1.0 multilingual OCR benchmark covering 20 languages with varied fonts and scripts When each image is processed via the OCR service with default settings Then the per-language word-level F1 is ≥ 0.92 and the macro-average F1 across all languages is ≥ 0.94 And at least 99% of detected lines include a language code; language identification accuracy is ≥ 0.97 And text rotated up to ±180°, curved baselines (radius ≥ 30 px), and perspective skew (tilt ≤ 35°) show ≤ 1.5 F1 point degradation versus upright text
Layout-Aware Output with Reading Order and Confidence
Given an image containing multiple text blocks with mixed reading directions (LTR and RTL) When processed by the OCR service Then each line and word returns a polygon bounding box in pixel coordinates aligned to source dimensions And IoU with ground-truth polygons is ≥ 0.80 for lines and ≥ 0.70 for words at p90 And reading order matches ground truth with Kendall’s tau ≥ 0.90 per block, respecting RTL scripts And each token includes a confidence score in [0,1] with Expected Calibration Error ≤ 0.10 on the benchmark And the per-image JSON validates against schema ocr_payload.v1 (image_id, width, height, lines[], words[], reading_order[], confidences)
Robustness to Low Contrast, Translucent Overlays, and Complex Backgrounds
Given a robustness test set with degradations: contrast ≤ 0.6, translucent overlays at 20–60% opacity, and background clutter score ≥ 0.7 When images are processed Then macro F1 decreases by ≤ 5.0 points relative to the clean baseline And recall on text under translucent overlays is ≥ 0.90 And false positives on background texture are ≤ 1 per megapixel at p95 And correctly recognized words have confidence ≥ 0.60 at p50
Batch Pipeline Parallel Processing and Throughput
Given a batch of 10,000 images (mean 4 MP, JPEG/PNG mix) submitted via the PixelLift batch pipeline When processed on a node with 1× T4 GPU and 8 vCPU with default concurrency Then sustained throughput is ≥ 600 images/min and p95 per-image latency ≤ 2.5 s And failures are isolated to the affected images; retryable errors are retried up to 2 times and a final status is recorded per image And results emit one JSON payload per image with the original image_id; output ordering may differ from input without loss And the job completes without OOM; GPU and CPU utilization remain ≤ 85% at p95
Service Interface Usable by Web App and Export API
Given authenticated clients using PixelLift service tokens When POST /v1/ocr is called with multipart image bytes or a signed URL and optional parameters (languages, detect_orientation, gpu_hint, cache=true) Then the response is HTTP 200 with JSON matching ocr_payload.v1 within 2.0 s p95 on GPU and 6.0 s p95 on CPU for 4 MP images And requests are idempotent when Idempotency-Key is provided; duplicates return the same result reference And errors return typed codes (400, 413, 415, 429, 500) with machine-readable details; timeouts ≤ 30 s cancel GPU work gracefully And CORS is enabled for the web app origin; rate limits ≥ 50 RPS per tenant with fair queuing
Deterministic Content-Addressed Caching
Given two OCR requests that resolve to identical normalized image content, OCR parameters, and model version When the second request is processed within the cache TTL Then a cache hit returns the identical payload and checksum in ≤ 150 ms at p95 And cache hit rate is ≥ 80% on a replay of the last 24 h workload And cache keys are SHA-256 over normalized bytes plus parameter hash; cache invalidates automatically on model or schema version change
GPU Acceleration with Reliable CPU Fallback
Given a host with a supported CUDA-capable GPU When the OCR service initializes and receives requests Then GPU is used for inference; if GPU is unavailable or fails mid-run, the service transparently falls back to CPU without request failure And GPU p50 latency is at least 3× faster than CPU for 4 MP images; accuracy difference (word-level F1) between GPU and CPU is ≤ 0.5 points And device selection, CUDA version, and fallback events are logged with correlation IDs and exported as metrics
Watermark, Badge, and Border Detection
"As a seller, I want automatic detection of watermarks, badges, and borders so that my images don’t get rejected for visual overlays I may have missed."
Description

Build a computer vision detector that identifies common non-compliant overlays such as watermarks, promo badges (e.g., starbursts, ribbons), stickers, and image borders/frames. The detector must output semantic classes, bounding polygons, and per-object confidence, and handle partially transparent or low-opacity marks. Combine learned models with geometric/colour heuristics to reduce false positives across diverse catalog imagery. Ensure outputs are normalized to the same schema as OCR findings for unified policy evaluation. Provide batch inference support, model versioning, and configurable detection thresholds per workspace. This module directly powers Text Guard’s ability to find non-text overlays that trigger marketplace policy violations.

Acceptance Criteria
Structured Output for Promo Badges and Stickers
Given an image containing one or more promo badges or stickers (e.g., starburst, ribbon) sized ≥128 px on the shorter side and labeled ground truth polygons When the detector runs at the workspace default threshold Then at least one detection is returned per ground-truth object with class in {"promo_badge","sticker"} And each detection includes a bounding polygon with ≥4 vertices using normalized image coordinates [0,1] And each detection includes a confidence score in [0,1] that is ≥ the applied threshold And the IoU between the detection’s bounding box (from polygon) and ground truth bbox is ≥ 0.50 And the output object conforms to the unified overlay schema used by OCR findings (fields: id, class, polygon, confidence, source, model_version, image_id, workspace_id)
Semi-Transparent Watermark Detection
Given a labeled validation set with semi-transparent watermark overlays at 15%–40% opacity covering 5%–50% of image area When the detector runs at the workspace default threshold Then watermark recall (IoU ≥ 0.50) is ≥ 0.85 across the set And false positive rate on a clean (no-overlay) control set is ≤ 0.05 And each detected watermark includes a polygon and confidence in the unified schema
Border and Frame Detection with Low False Positives
Given images with uniform or patterned borders/frames between 2% and 15% of the image width on any side and corresponding ground truth When the detector runs at the workspace default threshold Then class "border" or "frame" is returned with polygons correctly outlining the border/frame And recall (IoU ≥ 0.50 using polygon-derived bbox) is ≥ 0.95 on the bordered set And on a control set of product images without borders, the false positive rate for border/frame is ≤ 0.01
Model+Heuristic Fusion Reduces False Positives
Given a 2,000-image, category-diverse validation set with ground truth overlays When comparing the fused pipeline (learned model + geometric/color heuristics) to the raw model baseline at matched recall (±1%) Then the fused pipeline achieves ≥ 30% reduction in false positives relative to baseline And relative recall loss versus baseline is ≤ 5% when operating at the workspace default threshold And no class (watermark, promo_badge/sticker, border/frame) drops below 0.75 recall individually
Per-Workspace Detection Thresholds
Given workspace W with per-class thresholds set to { watermark: 0.60, promo_badge: 0.70, border: 0.80 } When running inference for W Then only detections with confidence ≥ the configured per-class threshold are returned for each class And when thresholds are updated via configuration API, subsequent inferences reflect changes within 60 seconds And removing a per-class override causes the global default threshold to be used for that class And threshold configuration persists across service restarts
Batch Inference API and Result Ordering
Given a batch request of 1–1,000 images (mixed sizes up to 4096 px on the longest edge) When invoking the batch inference API Then the service returns one result envelope per input preserving input order And each envelope contains either detections (per the unified schema) or a per-item error code and message without aborting the batch And the batch request succeeds when ≥ 95% of items complete without error And aggregated batch metrics (processed_count, success_count, error_count, duration_ms) are included in the response
Model Versioning and Workspace Pinning
Given model versions are managed with semantic versioning (e.g., 1.4.0) When inference runs Then each response includes model_version and a unique inference_id And a workspace can pin to a specific model_version; pinned workspaces do not change behavior on new releases until updated And unpinned workspaces automatically use the latest compatible model at the next inference And given identical inputs and a fixed model_version, outputs are reproducible with polygon coordinates equal within 1e-4 and confidence values equal within 0.001
Content-Aware Overlay Removal
"As a time-pressed seller, I want PixelLift to automatically remove disallowed overlays while keeping product quality intact so that I can publish compliant images without manual editing."
Description

Provide an automated cleanup pipeline that removes detected text and overlay artifacts using edge-aware inpainting and texture synthesis while preserving product contours, shadows, and reflections. The system should accept masks from OCR/overlay detectors, allow mask refinement (expand/feather), and generate a non-destructive cleaned variant with side-by-side preview. Include quality safeguards: confidence-weighted inpainting strength, background consistency checks, and rollback on quality regressions. Support batch application with per-image overrides, and expose outputs via versioned assets so users can choose original, cleaned, or approved variants for export. This capability shortens manual retouching cycles and increases pass rates on strict marketplaces.

Acceptance Criteria
Mask Intake and Refinement Controls
Given an image with detector-provided overlay masks and a product segmentation mask When the user adjusts Expand (0–30 px) and Feather (0–20 px) in Mask Refinement and saves Then the refined mask updates in the preview within <=300 ms And the refined mask equals morphological dilation by Expand and Gaussian feather by Feather And overlap of the refined mask into the locked product segmentation is <=1 px boundary tolerance And the saved parameters persist to the job configuration and API and are used in processing And an audit entry records user, values, and timestamp
Edge-Aware Inpainting Preserves Product Geometry
Given inpainting is executed with edge-aware mode on a product image with overlay mask M and product mask P When the cleaned variant is generated Then the product contour IoU between P (original) and P' (post-process) is >= 0.97 And the mean color difference (ΔE00) within P between original and cleaned is <= 1.5 And gradient magnitude change within annotated shadow/reflection regions is <= 10% And no new halos >3 px width are detected at the product-background boundary And output dimensions and DPI match the original
Confidence-Weighted Inpainting Strength
Given a per-pixel overlay confidence map C in [0,1] accompanies mask M When inpainting runs Then per-pixel inpaint strength S is applied as S = 0.2 + 0.8*C And process metadata stores Smin, Smax, seed, and a histogram of C And regions with mean C < 0.3 use blending alpha <= 0.44 And regions with mean C >= 0.7 use full inpainting (S >= 0.76) And results are deterministic for a fixed seed
Background Consistency Checks and Automatic Rollback
Given a cleaned variant is produced When background consistency checks run on the background region B (image minus product mask) Then SSIM(B, original background) is >= 0.92 And tiling/repetition artifact score is <= 0.2 And noise variance change in B is within ±15% of the original And if any check fails, the system rolls back the export candidate to the original, marks status "Rolled Back: Background", and records reason codes And the user is notified and can re-run with adjusted parameters
Non-Destructive Variant Creation and Side-by-Side Preview
Given processing completes for an image When assets are saved Then the original remains immutable as version v1 And the cleaned variant is saved as a new version v2 with a unique immutable ID and metadata And the side-by-side preview loads v1 and v2 in <=1000 ms with synchronized zoom/pan and a pixel-diff toggle And clicking Revert creates v3 that equals v1 without overwriting prior versions And clicking Approve tags the selected variant as "approved" without altering other versions
Batch Processing with Per-Image Overrides
Given a batch job of N images (1 <= N <= 1000) with defaults and per-image overrides for subset K When the job runs Then images in K use their overrides and others use defaults And p95 per-image processing time for 3000x3000 px images is <= 30 s And failed items are retried up to 2 times and on final failure record reason codes And a per-image summary includes variant IDs, timings, rollback status, and applied settings And the batch can be paused and resumed without data loss
Versioned Asset Selection for Export and Approval Gate
Given a set of versioned assets {original, cleaned, approved?} for each image When the user configures an export Then they can select per-image variant from {original, cleaned, approved} And the default is approved if present, else cleaned if quality checks passed, else original And the export manifest includes file names, variant type, and version IDs And strict marketplace profiles block export of non-approved variants unless the user explicitly overrides with confirmation And items flagged "Packaging Text – Approval Required" are excluded from auto-clean export until approved
Marketplace Policy Profiles & Pre-Export Gates
"As a seller targeting multiple marketplaces, I want policy-aware checks before export so that my images meet each marketplace’s rules without guesswork."
Description

Define configurable policy profiles for major marketplaces (e.g., Amazon, eBay, Etsy) capturing rules for disallowed text, badges, borders, and exceptions. Implement a pre-export validation gate that evaluates OCR/overlay findings against the selected profile, returning pass/fail status with machine-readable reasons and human-readable guidance. Profiles must be versioned, support workspace-level defaults, and allow quick updates as marketplace rules change. Integrate gating with the export flow, bulk exporter, and API webhooks to block non-compliant batches or route them to cleanup/review. Provide rule simulations (dry runs) to preview impacts before enforcement. This ensures consistent compliance and reduces downstream rejection risk.

Acceptance Criteria
Create and Version Marketplace Policy Profiles
Given I am a workspace admin, When I create a new profile for a marketplace (e.g., Amazon), Then I can define rules for disallowed text, badges, borders, watermarks, and named exceptions. Given I save the profile, When it is created, Then it is assigned a semantic version (e.g., 1.0.0) with an immutable rule payload and stored changelog notes. Given an existing profile v1.0.0, When I publish edits, Then a new version (e.g., v1.1.0) is created without mutating prior versions and becomes selectable for enforcement. Given multiple versions exist, When I set one as the workspace default for that marketplace, Then new exports targeting that marketplace auto-bind to that version. Given a new version is published, When new exports start after publication, Then they bind to the new default while in-flight jobs continue evaluating against their originally bound version. Rule validation: Invalid schemas or conflicting rules are rejected with clear error messages pointing to offending fields.
Pre-Export Gate Evaluates Images Against Selected Profile
Given OCR/overlay analysis results exist for a set of assets and a profile version is selected, When export is initiated, Then the pre-export gate evaluates each asset against all applicable rules in the profile. Then each asset receives an outcome: Pass, Fail, or Needs Review (for exception-eligible detections). Then for Fail or Needs Review outcomes, machine-readable reasons are returned with fields: rule_id, code, severity, bbox (if applicable), and human-readable guidance text per reason. Then batch status is Pass only if all assets Pass; otherwise export is blocked or routed according to the profile’s configured action. Then each decision is recorded with timestamp, evaluator version, profile_id, profile_version, and a stable evaluation hash for auditability.
Bulk Exporter Blocks or Routes Non-Compliant Batches
Given a batch contains one or more non-compliant items, When the Bulk Exporter is used, Then the exporter blocks the batch by default and displays a summary of Pass/Fail/Needs Review counts. Then users can download per-item findings as CSV and JSON including asset_id, outcome, reasons[], and guidance. Given the profile action for non-compliant is Auto-cleanup, When removable overlays are detected, Then cleanup is executed, results are re-evaluated once, and only assets that Pass proceed; others are blocked or sent to review per configuration. Given the profile action is Route to Review, When items are Needs Review, Then those items are moved to the review queue with links to annotated findings, and they are excluded from the export payload. Given all items Pass (initially or after cleanup), Then the batch proceeds to destination and a compliance report is attached to the job record.
API Webhook Delivers Gate Results
Given a webhook endpoint is configured for the workspace, When a gate decision completes for an export job, Then PixelLift POSTs a signed JSON payload including job_id, workspace_id, profile_id, profile_version, outcomes[], and summary counts. Then the payload includes an event_id and signature header verifiable with the workspace secret; deliveries are idempotent based on event_id. When the receiver returns a non-2xx response (except 410/422), Then delivery is retried with exponential backoff up to 3 attempts and logged; on 2xx, the job is marked webhook_delivered in the audit log.
Dry-Run Simulation of Policy Profile
Given a set of assets and a selected profile/version, When I run a dry run, Then no exports, cleanups, or state changes occur. Then the system returns a simulation report with per-asset predicted outcome, reasons, and guidance, plus aggregate counts and estimated pass rate. Given two profile versions are selected for comparison, When I run the simulation, Then the report includes deltas by outcome and lists assets whose outcomes would change. Then the simulation report can be downloaded (CSV/JSON) and shared as a link accessible to workspace members.
Workspace-Level Default Profiles and Overrides
Given multiple marketplaces are supported, When an admin sets a workspace default profile version per marketplace, Then new exports targeting each marketplace auto-select that default unless overridden. Given a user with permission starts an export, When they override the profile/version for that job, Then the override is recorded and used by the gate for that job only. Given no default exists for a marketplace, When an export is initiated, Then the system requires the user to select a profile before proceeding. Then all default changes and per-job overrides are captured in the audit log with actor, timestamp, and before/after values.
Exception Handling and Approval Workflow
Given a finding matches a rule flagged as exception-eligible (e.g., required packaging text), When detected, Then the asset outcome is Needs Review and it is routed to the review queue with snapshots and bounding boxes highlighting the finding. Given an approver reviews an item, When they approve with justification and an optional expiry, Then the item passes for the current export and an exception record is saved and honored for future exports until expiry. Given an approver rejects an item, Then the item remains blocked and, if Auto-cleanup is enabled, the system attempts cleanup and re-evaluates once. Then exception decisions are immutable and auditable; revocations create a new decision record without altering prior approvals.
Whitelist and Manual Approval Workflow
"As a brand owner, I want a simple way to approve necessary packaging text so that compliance checks don’t strip legally required information."
Description

Introduce a review workflow for cases where text must remain (e.g., on-product packaging, regulatory markings). Allow users to whitelist text by phrase, region, or class, and request manual approval when automated rules are inconclusive. Provide an in-app reviewer UI with detected regions overlay, quick keep/remove actions, comment threads, and audit logs of decisions. Support role-based permissions, bulk approvals, and SLA reminders/notifications for pending reviews. Capture approved exceptions in workspace policies to reduce repeat reviews. This balances automation with human oversight for edge cases and preserves necessary on-product information.

Acceptance Criteria
Whitelisting by Phrase, Region, and Class
- Given a workspace policy with whitelisted phrases (case-insensitive exact phrase match with punctuation normalization), when OCR detects “gluten-free” or “CE” on-pack, then those regions are marked Allowed, excluded from auto-removal, and not flagged as violations. - Given a saved polygon or rectangular region whitelist for a SKU/template, when an image matching that SKU/template is processed, then any text fully within that region bypasses removal and violation flags. - Given a whitelisted class "regulatory_markings" with a minimum confidence of 0.85, when a detected region is classified as regulatory_markings with confidence ≥ 0.85, then it is kept and not flagged; when confidence < 0.85, then normal rules apply. - Given non-whitelisted phrases/regions/classes, when detected, then standard Text Guard removal/flagging logic is applied. - Given a reviewer with permissions updates a whitelist entry (phrase/region/class) at workspace or project scope, when the next image is processed, then the updated rule is applied immediately and the change is recorded in the audit log.
Manual Review Trigger on Inconclusive or Conflicting Rules
- Given a detection with confidence between 0.40 and 0.85 (inclusive of lower bound, exclusive of upper), when export is requested, then the image is routed to the Manual Review queue and auto-removal is deferred pending a decision. - Given conflicting outcomes (e.g., phrase not whitelisted but region partially overlaps a whitelist polygon), when conflict is detected, then the system flags the item as Needs Review with conflict details. - Given a user manually requests review for an image, when the request is submitted, then the image appears in the Manual Review queue with the requester and timestamp recorded. - Given SLA is configured for reviews (e.g., 24h), when an item enters Needs Review, then its due time is set to now + SLA and displayed in the queue. - Given a reviewer approves or rejects, when a decision is recorded, then the item exits the queue and the export pipeline resumes with the chosen outcome.
Reviewer UI: Overlays, Quick Actions, and Cleanup Preview
- Given an image in review, when opened in the reviewer UI, then all detected text regions render overlays with class labels and confidence values within 1 second for images ≤ 12MP. - Given a region is selected, when the reviewer clicks Keep or Remove, then the preview updates to show before/after with content-aware cleanup (for Remove) within 1.5 seconds, and the region state is visually updated. - Given multiple regions, when the reviewer uses Keep All or Remove All, then the corresponding actions apply to all visible regions and the composite preview is updated. - Given an action was applied, when the reviewer clicks Undo/Redo, then the prior state is restored/applied without data loss and logged. - Given the reviewer saves decisions, when Save is clicked, then the computed edited image is persisted and the decision states are attached to the item.
Comments and Audit Trail of Review Decisions
- Given an image under review, when a reviewer or contributor adds a comment at image or region level, then the comment captures author, timestamp, and context link, and becomes visible to permitted users immediately. - Given any decision (Keep/Remove/Approve/Reject/Whitelist change), when it is performed, then the audit log records actor, role, timestamp, action, affected regions, before/after hashes, and rationale (if provided). - Given a batch review concludes, when an export of audit data is requested, then a CSV and JSON are downloadable containing all decisions and comments within the selected date or batch range. - Given a comment is edited or deleted, when the action occurs, then the previous version remains viewable in the audit history with who and when changes occurred.
Role-Based Permissions for Review and Policy Management
- Given a user without Reviewer permission, when they attempt to approve/reject or Keep/Remove, then the action is blocked with a 403 error and a UI message indicating insufficient permissions. - Given a user with Reviewer permission, when they approve/reject items, then the actions succeed and are recorded with their role at time of action. - Given a user without Admin permission, when they attempt to create/edit/delete workspace whitelist rules or policy settings, then the action is blocked and logged as denied. - Given an Admin, when they modify whitelist rules (phrase/region/class) or enable auto-learn, then changes take effect immediately across the workspace and are audit-logged with diff of prior vs new values. - Given API access tokens with scoped roles, when API endpoints for approvals or policy changes are called, then enforcement matches the UI (same allow/deny outcomes).
Bulk Approvals, Queue Filters, and SLA Reminders/Notifications
- Given items in the Manual Review queue, when a reviewer applies a bulk action (Approve, Keep All, Remove All) to a selection of up to 500 items, then the action completes and persists within 10 seconds with a success/failure summary per item. - Given the queue has many items, when filters are applied (status, SKU, confidence range, class, age, assignee), then the list updates within 1 second and results match the filter predicates. - Given SLA is 24h, when an item reaches 80% of SLA time without decision, then a reminder notification is sent to the assigned reviewer (and fallback group if unassigned); when the SLA is breached, then an escalation notification is sent to the configured channel. - Given user notification preferences, when reminders/escalations are sent, then channels (email/in-app) respect the user’s opt-in settings and include deep links to the items.
Workspace Policy Auto-Capture from Approved Exceptions
- Given a reviewer approves a Keep decision for a non-whitelisted phrase/region/class, when they click Save, then the system prompts to add this as a policy exception with pre-filled scope (workspace/project/SKU) and metadata; on confirm, the whitelist entry is created and linked to the case. - Given auto-learn is enabled, when the same unwhitelisted phrase/class/region pattern is approved ≥ 3 times within 7 days, then the system auto-creates a whitelist entry and notifies Admins and Reviewers with the summary and opt-out. - Given a whitelist entry was created from an exception, when subsequent matching images are processed, then they bypass manual review for that pattern and are not flagged, reducing repeat review occurrences for that pattern by ≥ 90% over the next 30 days. - Given a policy exception is rolled back by an Admin, when rollback is confirmed, then future processing reverts to standard detection rules; past outputs remain unchanged unless reprocessed.
Configurable Confidence Thresholds & Auto-Actions
"As an operations manager, I want to configure detection thresholds and default actions so that the system reflects our risk tolerance and minimizes unnecessary manual work."
Description

Offer workspace- and marketplace-specific settings to tune detection confidence thresholds and define auto-actions: auto-remove overlays above threshold, auto-pass below threshold, or route to manual review for ambiguous cases. Include presets (strict, balanced, lenient), per-class tuning (e.g., text vs. badge vs. border), and safe-mode options that prefer review over destructive edits. Provide logs and metrics showing how threshold changes affect false positives/negatives. Expose these controls via UI and API for bulk operations. This flexibility aligns Text Guard behavior with varying seller risk tolerance and catalog characteristics.

Acceptance Criteria
Workspace and Marketplace Threshold Configuration
Given a workspace admin is in the Text Guard settings UI, When they select a marketplace scope, Then they can set detection confidence thresholds (0.00–1.00, 0.01 increments) and default auto-actions for that scope. Given the admin saves settings, When the Save action succeeds, Then the new settings persist and are applied to all new jobs started within 60 seconds and remain after page reload. Given an image job for marketplace M, When marketplace-specific settings exist, Then they override workspace defaults; otherwise workspace defaults apply; otherwise system defaults apply. Given an invalid threshold value is submitted via UI or API, When it is outside 0.00–1.00 or missing, Then the request is rejected with a validation error (HTTP 400 for API) and no changes are persisted.
Per-Class Tuning for Text, Badge, and Border
Given the settings UI or API, When configuring class-specific values, Then thresholds and auto-actions can be set independently for text, badge, and border classes within the valid range. Given a detection contains multiple classes, When per-class actions differ, Then only regions for classes meeting their action criteria are modified; other regions are left unchanged. Given no class-specific values are defined, When processing runs, Then class behavior inherits from the scope’s default thresholds and actions. Given class-specific settings are changed, When saved, Then the effective settings snapshot recorded for subsequent jobs includes the per-class overrides.
Preset Profiles: Strict, Balanced, Lenient
Given preset profiles (Strict, Balanced, Lenient) are available, When a user selects a preset for a scope, Then preset threshold/action values by class are populated and previewed before save. Given a preset is applied and saved, When the user returns later, Then the UI indicates whether current values match the preset or show "Preset + modifications" if any field deviates. Given the API endpoint GET /text-guard/presets is called, When successful, Then it returns the preset values by marketplace and class. Given the user chooses Revert to preset, When they confirm, Then all modified fields in the scope reset to the selected preset values in a single transaction.
Safe Mode Prefers Review Over Destructive Edits
Given Safe Mode is enabled for a scope, When an auto-remove or other destructive action would be triggered, Then the item is routed to manual review instead and no destructive edit is applied. Given Safe Mode is disabled for a scope, When a detection meets the remove threshold, Then auto-remove proceeds as configured. Given detected packaging/mandatory text, When Safe Mode is enabled, Then the review entry is flagged "Packaging text" and requires explicit human approval before export. Given Safe Mode is toggled via UI or API, When the change is saved, Then the event is recorded in the audit log with actor, scope, timestamp, and old/new values.
Auto-Action Routing Based on Confidence Bands
Given per-class pass_threshold and remove_threshold are configured such that pass_threshold < remove_threshold, When a detection score S is produced, Then if S < pass_threshold the item auto-passes with no edit; if pass_threshold ≤ S < remove_threshold the item is routed to manual review; if S ≥ remove_threshold the item auto-removes with content-aware cleanup. Given a batch of up to 1,000 images is submitted, When processing starts, Then 95% of pass/remove/review decisions are assigned within 120 seconds of job start. Given an auto-remove action is applied, When the job completes, Then a diff preview and action details (class, score, thresholds used) are attached to the job results.
Audit Logs and Metrics on Threshold Impact
Given any settings change (thresholds, actions, presets, Safe Mode) is saved via UI or API, When the operation succeeds, Then an immutable audit log records actor, scope, before/after values, correlation ID, and ISO 8601 timestamp. Given reviewed items exist in the selected date range, When viewing the Metrics dashboard, Then the system displays per scope and class: counts of detections, auto-passes, auto-removals, reviews, reviewer outcomes, and estimated FP/FN rates derived from review outcomes. Given the Metrics API is requested with date range and scope filters, When successful, Then it returns JSON and CSV exports containing the same metrics fields. Given thresholds were changed between two periods, When the Compare view is used, Then the dashboard shows deltas in FP/FN rates and decision distribution attributable to the settings change.
UI and API for Bulk Settings and Operations
Given the Settings UI supports multi-select, When a user selects multiple marketplaces within a workspace, Then they can apply a preset or per-class thresholds/actions to all selected marketplaces in one Save, with a pre-save summary of changes. Given the Bulk Settings API is called with a list of scopes and an idempotency key, When processed, Then changes are applied atomically per scope, a per-scope success/failure report is returned, and duplicate requests with the same key are not applied twice. Given a processing job is submitted for a marketplace, When it is accepted, Then the effective settings snapshot (including per-class values and Safe Mode) is captured and its snapshot ID is included in job results. Given a user without admin permissions attempts to change settings via UI or API, When the request is made, Then it is rejected with 403 Forbidden and no changes are made.
High-Throughput Batch Processing and SLA Monitoring
"As a high-volume seller, I want fast, reliable batch processing with clear progress and performance guarantees so that my catalog updates aren’t delayed by image compliance checks."
Description

Ensure Text Guard scales to PixelLift’s batch use cases by implementing concurrent processing, resilient job queuing, and autoscaling for OCR, detection, and cleanup stages. Provide progress tracking, partial successes, retries with exponential backoff, and idempotent re-runs. Define performance SLAs (e.g., P95 processing time per image) with telemetry dashboards, alerts, and per-batch compliance reporting. Optimize model loading and caching to reduce cold-start latency. This guarantees timely turnaround on large catalogs while maintaining reliability and visibility for users and support teams.

Acceptance Criteria
Batch P95/P99 SLA Compliance and Throughput
Given a submitted batch of 10,000 images (<=12MP, <=5MB each) with mixed OCR/detection/cleanup paths and autoscaling enabled When processing occurs under normal production load (median system utilization) Then the P95 end-to-end processing time per image (enqueue to completion) is <= 5 seconds and P99 is <= 10 seconds And sustained throughput is >= 1,200 images per minute for at least 10 minutes And the per-batch SLA report marks the batch as Compliant and includes P50/P95/P99 and timestamped measurement window
Partial Success and Failure Reporting
Given a batch containing images that succeed, require approval, and fail unrecoverably When the batch completes Then each image has a terminal status in {Succeeded, Succeeded with Cleanup, Requires Approval, Failed} And batch summary totals and percentages per status are returned via API and rendered in UI And a downloadable CSV and JSON include per-image fields {status, duration_ms, stage, retry_count, error_code} And the batch overall status is "Completed with Partial Success" when it contains at least one success and at least one non-success
Exponential Backoff Retries with Limits
Given a transient error (e.g., timeout, 5xx storage error, worker interruption) in any stage When a job fails Then it is retried up to 3 times with exponential backoff starting at 2s, doubling each attempt, with +/-20% jitter And cumulative retry delay per image does not exceed 60s And non-retryable errors (e.g., unsupported format, corrupted file) are not retried And final image metadata records retry_count and last_error_cause
Idempotent Re-run of Failed Images
Given a completed batch and a user triggers a re-run with rerun=failed_only using the same input URIs and parameters When the re-run request is submitted with a unique rerun_token Then only images previously in {Failed, Requires Approval} are re-queued And duplicate submissions with the same rerun_token return the existing re-run without enqueuing duplicate work And new outputs atomically overwrite prior failed outputs while preserving successful outputs And telemetry correlates original and rerun attempts via shared batch_id and correlation_id fields
Queue Durability and Autoscaling Under Burst
Given a burst of 100,000 images across 50 concurrent batches When the system processes the burst Then the job queue persists tasks durably with at-least-once delivery and maintains in-order execution of per-image stages And workers auto-scale within 2 minutes to sustain >= 1,500 images/min throughput And the dead-letter rate stays <= 0.5% and all dead-lettered items have machine-readable error codes And on worker crash, in-flight jobs are reassigned within 30s without batch abortion
Model Loading and Caching Cold-start Reduction
Given a cold worker receiving its first job When OCR/detection/cleanup models are initialized Then total cold-start warm-up time per worker is <= 15 seconds before accepting work And an LRU cache holds at least the 2 most-used model variants with a >= 90% hit rate during steady state And scale-out pre-warms models before jobs are pulled And average stage start latency after warm-up is <= 200ms per image
Progress APIs, Webhooks, Dashboards, and Alerts
Given a batch is submitted When processing proceeds Then the API exposes a progress endpoint returning counts per status and percent_complete updated at least every 5 seconds And webhooks are sent for {batch_started, batch_progress, batch_completed} with HMAC-SHA256 signatures and up to 5 delivery retries with exponential backoff And the UI receives live updates via SSE or WebSocket with <2s median latency from backend event to render And dashboards display P50/P95/P99 per stage and end-to-end, error rates by type, queue depth, worker utilization, cache hit rate, and webhook success rate And alerts fire within 2 minutes when P95 per-image time exceeds SLA by >=20% for 5 consecutive minutes or when error rate > 2% And a per-batch downloadable SLA report includes percentile metrics, compliance verdict, and any alert windows

Compliance Gate

A hard stop that blocks export until all RuleLock checks pass. Displays a clear fail list with guided fixes and a bulk “Fix All” option. Supports documented exceptions with role‑based overrides and audit trails—keeping teams fast while maintaining zero‑reject standards.

Requirements

Export Compliance Gate & Rule Evaluation
"As a seller exporting a batch, I want exports to be blocked until all images pass required rules so that my listings aren’t rejected and I avoid rework."
Description

Implements a hard-stop gate in the export pipeline that evaluates all images against configured RuleLock checks before any file can be exported. Supports batch and streaming exports, fail-fast or full-run modes, and produces deterministic, machine-readable results per asset with rule IDs, severities, and remediation hints. Ensures idempotent re-evaluation, tracks which rule-set version was applied, and stores evaluation outcomes for later audit. Designed for high throughput with defined SLAs (e.g., per-image latency budget), graceful degradation, and retry logic. Integrates with PixelLift’s job queue, export profiles, and notification system to block, release, or re-queue batches based on compliance outcomes.

Acceptance Criteria
Fail-Fast Gate Blocks Non-Compliant Batch Export
Given a batch export job in fail-fast mode with at least one non-compliant asset When RuleLock evaluation begins Then evaluation stops at the first failure per asset and the job is halted before any file is exported And zero files are written to the export destination And the job status is set to "Blocked-Compliance" within 5 seconds of the first failure detection And a notification is sent to the job owner within 10 seconds containing job_id, export_profile_id, failing_asset_ids, and first_failed_rule_id
Full-Run Mode Produces Complete Rule Evaluation Report
Given a batch or streaming export job in full-run mode When RuleLock evaluation completes Then a machine-readable JSON report (schema v1.0) is produced per job with fields: job_id, rule_set_version, assets[].asset_id, assets[].rules[].{rule_id,severity,status,hint} And schema validation passes with no errors And export is blocked if any asset has a failing rule And the report is persisted and retrievable by job_id via API within 60 seconds of job completion
Deterministic, Idempotent Re-evaluation with Rule-Set Version Tracking
Given the same asset content hash and the same rule_set_version When the evaluation is re-run within 24 hours using the same idempotency key Then the returned results (including statuses, severities, hints, and ordering) are byte-for-byte identical to the prior result And no duplicate evaluation work is performed (idempotent response) And when the rule_set_version differs Then the new evaluation record includes the new version and prior records remain immutable and queryable
Streaming Exports Are Hard-Stopped Until Compliance Passes
Given a streaming export pipeline When an asset fails any RuleLock check Then that asset is not emitted downstream and no side-effects (writes) occur for that asset And stream metrics record a blocked event with asset_id and rule_id And when all assets in the window pass Then compliant assets are emitted and non-compliant assets remain blocked with reasons logged
High Throughput SLA, Graceful Degradation, and Retry Logic
Given sustained load of 10,000 assets/hour per tenant with up to 5 concurrent export jobs When RuleLock evaluations run Then P95 per-asset evaluation latency <= 200 ms and P99 <= 400 ms And system maintains ≥ 99.9% successful evaluations without manual intervention over a 1-hour window And when a transient error (timeout, 5xx) occurs Then the system retries up to 3 times with exponential backoff (0.5s, 1s, 2s) and jitter And when retries are exhausted Then the asset is marked "Evaluation-Error", the job status is "Blocked-Compliance", and a remediation message is attached And when a dependent non-critical enrichment service is unavailable Then checks flagged requires_enrichment=false proceed; checks requiring enrichment return "Inconclusive" and block export with a clear message, without crashing the job
Integration with Job Queue, Export Profiles, and Notifications
Given an export job enqueued with export_profile_id X When compliance evaluation finishes Then on all assets passing, the job transitions to "Ready-To-Export" and begins export automatically And on any failure, the job transitions to "Blocked-Compliance" with a link to the fail list and remediation hints And the notification system publishes to configured channels (email, webhook) a structured payload with job_id, export_profile_id, summary counts, and first 50 failing asset_ids And a re-queue action triggers re-evaluation only for assets marked changed since last evaluation
Audit Persistence and Queryability of Evaluation Outcomes
Given completed evaluations When outcomes are written to storage Then each record contains evaluation_id, job_id, asset_id, rule_set_version, evaluated_at (UTC), evaluator_version, result_checksum, and full rule results And records are retained for at least 13 months and are immutable And the API can return evaluation outcomes filtered by job_id or asset_id with P95 response time ≤ 500 ms for result sets up to 10,000 records And access is secured by role, and all reads/writes produce audit trail entries with actor_id and timestamp
Compliance Results Panel (Fail List UI)
"As a user reviewing a batch, I want a clear list of failures with explanations and quick actions so that I can resolve issues fast."
Description

Provides a clear, actionable UI that lists all failed checks by image and rule, including severity, human-friendly messages, and links to guidance. Supports grouping by rule, filtering, search, pagination, and sort by severity or SKU. Displays thumbnails, key metadata (dimensions, file size, color profile), and pass indicators for quick triage. Offers copy-to-clipboard error details and is fully localizable and accessible (keyboard navigation, screen-reader labels, contrast). Embeds deep links to one-click fixes and exception requests.

Acceptance Criteria
Failed Checks List Rendering
Given a processed batch containing at least one image with one or more failed RuleLock checks When the user opens the Compliance Results Panel for that batch Then the panel lists each failed check as a row containing: image thumbnail (min 64x64, preserved aspect), SKU, rule name and ID, severity badge (Critical/Major/Minor), human-readable message, and a guidance link labeled "View guidance" And each image card or header shows pass indicators summarizing check counts for that image (e.g., "3 pass • 1 fail") And severity badges are color-coded and include text labels (not color-only) And clicking the guidance link opens the rule’s guidance in a new tab
Grouping, Sorting, and Pagination Controls
Given the panel has failed checks from multiple images and rules When the user toggles Group by Rule ON Then items are grouped under rule headers that display rule name, rule ID, and the number of affected images And toggling Group by Rule OFF returns to a per-image list When the user sorts by Severity (desc) Then rows are ordered Critical, then Major, then Minor; ties are secondarily sorted by SKU ascending When the user sorts by SKU (asc) Then rows are ordered alphanumeric by SKU; ties are secondarily sorted by Severity (desc) Given more than 50 failed checks When the panel loads Then pagination controls appear with a default page size of 50, show total results, and allow page size changes (25/50/100) And changing page or page size updates the list without full page reload And the first page renders within 1 second for up to 2,000 failed checks on a standard test device and network profile
Filtering and Search
Given the panel displays failed checks When the user filters by Severity, Rule, and/or SKU Then only rows matching all selected filters are shown and the visible count updates accordingly When the user enters a search query Then matching occurs against SKU, rule name/ID, and message text using case-insensitive substring matching, and matches are highlighted When no results match filters/search Then an empty state appears with a "Clear filters" action And selecting "Clear filters" resets all filters and search and restores the full list
Thumbnails and Metadata Display
Given images with known metadata (dimensions, file size, color profile) When their failed checks are listed Then each image entry displays a non-distorted thumbnail (min 64x64), pixel dimensions (e.g., 1500×1500 px), file size (e.g., 320 KB/1.2 MB), and color profile (e.g., sRGB, CMYK) And hovering or focusing the thumbnail reveals a larger preview (min 256 px on the long edge) And if a thumbnail cannot load, a placeholder with the image filename is shown instead
Copy-to-Clipboard Error Details
Given a failed check row is visible When the user clicks "Copy details" Then the clipboard receives a plain-text block containing: SKU, image filename, rule ID, rule name, severity, message, and guidance URL on separate labeled lines And a success toast appears within 200 ms and auto-dismisses after 3 seconds And if the Clipboard API is unavailable or denied, a fallback dialog opens with preselected text and instructions to press Ctrl/Cmd+C And the copied text reflects the current locale for labels and number/date formats
Localization and Accessibility Compliance
Given the application locale is switched between English and Spanish When viewing the Compliance Results Panel Then all static UI text (headers, labels, buttons, toasts, empty states, severity names) appears in the selected language without truncation or overflow And all controls and rows have accessible names/roles/states for screen readers, including severity announced as text And keyboard-only users can reach all interactive elements with a visible focus order that follows the visual layout; Enter/Space activates the focused control; Escape closes dialogs and returns focus to the trigger And text, icons, and badges meet WCAG 2.1 AA contrast (>= 4.5:1 for text, >= 3:1 for large text/icons) And no keyboard traps are present and there are no color-only indicators
Deep Links to One-Click Fixes and Exception Requests
Given a failed check that supports a one-click fix When the user clicks "Fix now" Then the fix executes in context for the correct image and rule, the row status updates to "Resolved", and the item moves out of the fail list or shows a resolved badge And an Undo option is available for 30 seconds to revert the fix Given a failed check that allows exceptions When the user selects "Request exception" Then a prefilled exception form opens with SKU, rule ID, rule name, failure details, and evidence link; upon submit, an audit-trail entry is created and the row shows "Pending exception" And users without override permission do not see exception controls and receive an authorization error if they navigate directly via URL
Guided Fixes & Bulk Fix All
"As a seller, I want recommended fixes I can apply individually or to all failed items so that I can achieve compliance in seconds without manual editing."
Description

Delivers one-click, rule-aware fixes for common violations (background removal, canvas sizing, padding, aspect ratio, color profile conversion, max file size compression, watermark removal) with instant previews and the option to apply per-item or to selected/all failures. Includes a dry-run impact estimator, conflict resolution (e.g., competing crop vs. padding rules), and transactional execution with rollback/undo. Respects per-marketplace constraints and preserves visual quality via configurable thresholds. Logs every applied change for audit and supports progress tracking and rate limiting for large batches.

Acceptance Criteria
Instant One-Click Fix with Preview (Single Violation)
Given an image fails the Background Removal rule When the user clicks One‑Click Fix on that item Then the background is removed per the target marketplace specs and an updated preview is displayed And the item’s Background Removal rule status updates from Fail to Pass without altering other rule statuses And an audit entry is recorded with ruleId, parameters, actorId, timestamp, beforeHash, afterHash And the user can Undo to restore the original image in one action And visual quality metrics meet or exceed the configured thresholds
Bulk Fix All on Selected Failures with Progress and Rate Limiting
Given the user selects multiple items with one or more rule failures and clicks Fix All When batch processing begins Then rule‑aware fixes are applied to all detected failures on the selected items respecting each item’s marketplace constraints And a progress indicator shows completed/total items and latest operation status And processing adheres to the configured rate‑limiting policy for external calls And items that cannot be fixed are marked Failed with error reasons and are excluded from export And a completion summary lists counts of Passed, Failed, and Skipped items
Dry-Run Impact Estimator for Selected Items
Given the user triggers a Dry‑Run on selected failed items When the estimator runs Then no source files are modified And the UI displays predicted actions per item and rule (e.g., crop/pad dimensions, color profile change, expected compressed size, watermark removal) And potential rule conflicts are flagged with the resolution that would be applied And the user sees the estimated percentage of items that would pass RuleLock after fixes
Deterministic Conflict Resolution Between Competing Rules
Given a detected conflict exists between Crop and Padding rules for an item When the user runs Fix or Fix All Then the engine resolves the conflict using the configured rule priority order And the chosen resolution preserves minimum visual quality thresholds And the resolution decision and rationale are displayed in the UI for that item And the resulting image passes all applicable rules for the target marketplace, or the item is marked Unresolved with reason if no valid solution exists And the conflict and outcome are captured in the audit log
Transactional Execution and Rollback/Undo per Item and Batch
Given a batch fix is executing across multiple items When a step fails for any item Then all changes for that item are rolled back and its pre‑fix state is preserved And upon batch completion the user can Undo the entire batch with a single action, restoring all affected items to their prior state And audit entries include a batch correlationId and per‑item outcomes And partial success across items is allowed, but each item’s operations execute atomically
Marketplace Constraints and Quality Threshold Compliance
Given fixes are applied to items targeting specific marketplaces When validation runs post‑fix Then resulting images conform to the destination’s aspect ratio, canvas size, padding, color profile, and max file size requirements And any item not meeting specs after fixes is flagged Fail with explicit reasons and remains blocked by Compliance Gate And compression/resizing does not proceed if configured visual quality thresholds would be violated
Comprehensive Audit Logging and Traceability
Given any fix (single or bulk) is executed When the operation completes Then the system logs per item: ruleId(s), parameters used, actorId, timestamps, before/after checksums, quality metrics, marketplace context, and outcome (Pass/Fail/Skipped) And logs are immutable and queryable by time range and batch correlationId And documented exceptions or overrides include approver role and justification in the audit trail
Role-based Overrides & Exception Workflow
"As a team lead, I want to approve well-documented exceptions with controlled scope so that the team can move fast without compromising policy or auditability."
Description

Enables documented exceptions with role-based permissions and optional dual approval. Users can submit exception requests with scope (image/SKU/batch/marketplace), duration/expiry, reason codes, and attachments. Approvers receive notifications, can approve/deny with comments, and exceptions are enforced at evaluation time while respecting non-overridable critical rules. Every action is captured in an immutable audit log with actor, timestamp, and diff. UI surfaces override badges and countdowns to expiry and provides centralized management with least-privilege access controls.

Acceptance Criteria
Submit Exception with Scope, Duration, Reason, and Attachments
Given a user with Exception Request permission and a failed RuleLock check is present And tenant configuration defines allowed scopes (image, SKU, batch, marketplace), reason codes, and maximum duration When the user opens Request Exception and selects a valid scope and identifiers And selects a reason code from the configured list And sets an expiry within the configured maximum duration And optionally uploads up to 5 attachments of types [png, jpg, jpeg, pdf] with each file <= 10MB And submits the request Then the system creates a Pending exception record with a unique ID and captures all submitted fields And the UI shows a Pending Exception badge on all affected items And an audit log entry is written with actor, timestamp (UTC), and field-level diff And approvers for the selected scope receive notifications
Approve/Deny with Optional Dual Approval and Comments
Given an exception in Pending state and approver(s) with Approve Exceptions permission And the rule or policy indicates whether dual approval is required When approver A opens the request, reviews details and attachments, and either approves or denies with a comment (comment required on Deny) Then if the rule requires single approval, the exception transitions to Active on Approve or Denied on Deny And if dual approval is required, the status becomes Awaiting Second Approval after the first Approve And the system prevents the requester and approver A from serving as the second approver And on the second Approve the exception transitions to Active; on Deny it transitions to Denied And all transitions generate notifications to the requester and audit log entries with actor, timestamp, and diff
Enforcement at Evaluation Respects Non-overridable Critical Rules
Given an Active exception with defined scope and unexpired expiry When the Compliance Gate evaluates an export Then only the specified failed checks within the exception’s scope are suppressed And any rule marked Non-overridable continues to block export regardless of the exception And exceptions outside their scope or past expiry are ignored And the evaluation report indicates which exceptions were applied and which rules remained blocking And export proceeds only if all remaining checks pass
Prevent Exception Creation on Non-overridable Rules
Given a rule is marked Non-overridable When a user attempts to request an exception for that rule via UI or API Then the request action is disabled in the UI and the API returns 403 Forbidden with an explanatory error code And no exception record is created And an audit log entry records the attempted action with actor and timestamp
UI Badges and Expiry Countdown
Given items covered by Pending or Active exceptions When viewing the Compliance Gate results or item detail pages Then a badge is displayed per affected item showing status (Pending/Active), reason code, and expiry countdown (for Active) And a tooltip or detail panel shows requester, approver(s), scope, and expiry timestamp And countdown updates without page reload at 1-minute granularity And badges are removed immediately upon expiry or revocation
Centralized Exception Management with Least-Privilege Controls
Given users with varying roles (Requester, Approver, Admin, Viewer) When users access the Exceptions Management page or API Then access control enforces least-privilege: Requesters see and withdraw their own requests; Approvers see requests in their domain and can approve/deny; Admins can revoke any Active exception; Viewers have read-only access And list views support filtering by status, rule, scope type, marketplace, requester, approver, and expiry window And bulk actions are limited to allowed roles (e.g., Admin bulk revoke) And CSV export respects the same access filters And every action writes an audit log entry
Audit Log Immutability and Accurate Diffs
Given any exception lifecycle action (create, approve, deny, second approve, revoke, expire, renew) When retrieving the audit trail for the exception via UI or API Then each action has an immutable entry with actor ID, role, timestamp (UTC), action type, and field-level before/after values And entries are append-only; attempts to edit or delete an entry are blocked and logged And the chronological trail reconstructs the current exception state when replayed
Marketplace Rule Templates & Versioning
"As a seller, I want marketplace-specific rule profiles that stay current and are easy to apply so that my exports always meet each channel’s standards."
Description

Offers prebuilt, up-to-date rule templates for major marketplaces (Amazon, eBay, Etsy, Walmart, Shopify) with the ability to customize, clone, and compose rules per export profile. Maintains semantic versioning, change logs, effective dates, and migration helpers to re-evaluate existing batches when templates update. Supports import/export of templates as JSON, validation on save, and safe draft/publish workflows. Notifies users of breaking changes and maps template versions to evaluations for traceability.

Acceptance Criteria
System Template Catalog Availability
Given I am an authenticated workspace admin When I open the Marketplace Rule Templates catalog Then I see prebuilt system templates for Amazon, eBay, Etsy, Walmart, and Shopify And each system template displays its semantic version, publish date, and marketplace And system templates are read-only with Clone action enabled and Delete/Edit disabled And searching or filtering by marketplace returns the expected system templates
Cloning, Customization, and Profile Composition
Given a system template is visible When I clone it Then a new draft custom template is created in my workspace with a new ID and inherited rules When I edit rules and save Then validation runs and shows errors inline; save is blocked until all errors are resolved When I publish the custom template Then it receives version 1.0.0 and is available to add to Export Profiles When I compose multiple templates into a profile with defined precedence (Profile Overrides > Custom Templates > System Templates) Then conflicts are flagged before save and I must resolve or set precedence to publish the profile
Semantic Versioning and Change Log Enforcement
Given I have a draft of an existing custom template at version 1.2.3 When I make backward-compatible rule additions Then the minimum allowed next version is 1.3.0 When I modify a rule in a way that can increase rejections Then the minimum allowed next version is 2.0.0 and I must provide breaking-change notes When I change non-functional metadata only Then the minimum allowed next version is 1.2.4 And publishing is blocked if the chosen version violates these rules And each publish requires a change log entry that is persisted and visible in the template history
Draft/Publish Workflow and Validation Gates
Given I am editing a template When I click Save Draft Then no release version is assigned and the template cannot be referenced by evaluations When I click Validate Then all rule syntax and constraint checks run and any failures are listed with locations When I click Publish on a valid draft Then the template receives a release version and becomes selectable in profiles And publish is blocked if validation fails, showing the fail list
Effective Dates and Automatic Re-evaluation
Given a template v1.0.0 is in use by active profiles When I publish v2.0.0 with an effective date in the future Then evaluations scheduled before the effective date use v1.0.0 and those after use v2.0.0 When the effective date is reached Then the system automatically applies v2.0.0 to new evaluations And a migration helper lists impacted profiles and historical batches with an option to re-evaluate using v2.0.0 And each re-run stores a new evaluation record linked to v2.0.0 without altering prior results
Template JSON Import/Export and Schema Validation
Given I select a template When I export as JSON Then the file validates against the public schema and includes id, name, marketplace, version, rules, effectiveDate, and changeLog Given I import a JSON file When the JSON conforms to the schema Then a draft template is created; it is not auto-published; and name conflicts are detected and must be resolved When the JSON is invalid Then the import is rejected with precise field-level errors
Breaking Change Notifications and Traceability
Given profiles use a template When a MAJOR version is published Then owners and watchers of affected profiles receive in-app and email notifications within 60 seconds including change summary, impacted profiles, and migration helper link And the Evaluation Details view and API expose the template name and version used for each evaluation with a link to the change log And historical evaluations remain immutable and continue to reference their original template versions
Compliance Audit Logs & Reporting
"As a compliance manager, I want auditable records and insights into compliance health so that we can prove adherence and prioritize systemic fixes."
Description

Captures an end-to-end, immutable audit trail of evaluations, fixes, overrides, and export decisions, including who, what, when, and before/after artifacts. Provides filters by date range, user, rule, marketplace, and batch, with CSV/JSON export and secure API access. Delivers dashboards for pass/fail rates, top violations, average time-to-fix, override frequency, and trends over time. Emits webhooks/events for external SIEMs and enforces retention policies with PII minimization and access controls.

Acceptance Criteria
Immutable Audit Trail for Compliance Events
Given a product image batch passes through Compliance Gate When evaluations run, a user applies a fix, an override is requested or granted, or an export is attempted Then an audit entry is appended for each event capturing: event_type, event_id, timestamp (UTC ISO 8601), actor_id, actor_role, batch_id, item_id, rule_id (if applicable), marketplace_id (if applicable), decision/outcome, reason/notes (if provided), before_artifact_ref, after_artifact_ref, before_hash, after_hash Given an existing audit entry When an update or delete is attempted via UI or API Then the system rejects the change with 409 and records a new correction entry referencing the original event_id without altering the original Given the audit log store When a tamper-integrity check runs Then each entry’s content hash and chain signature validate, and any mismatch is flagged as a critical alert Given a viewer without artifact permissions When viewing an audit entry with artifact refs Then only hashed/tokenized references are shown and binary artifacts are redacted
Audit Log Search & Filters
Given logs exist across multiple dates, users, rules, marketplaces, and batches When filters for date range, user_id, rule_id, marketplace_id, and batch_id are applied Then only entries matching all selected filters are returned Given a date range filter When the start is after the end Then the API responds 400 with a validation error describing the invalid range Given more than 10,000 matching entries When querying Then results are returned with cursor-based pagination and stable ordering by timestamp with asc/desc support Given a valid filtered query of up to 10,000 matches When executed Then the first page responds within 2 seconds at p95 in a production-sized dataset
CSV and JSON Export of Filtered Audit Logs
Given a filtered search result When export=CSV or export=JSON is requested Then the download contains exactly the filtered records in the same order and includes a schema_version and generated_at timestamp Given the result set exceeds 100,000 records When export is requested Then the system runs an async export job, notifies on completion, and splits output into chunks of up to 100,000 records each Given no records match the filters When export is requested Then a valid empty file is produced with headers (CSV) or an empty array with schema metadata (JSON) Given a user without export permission When export is requested Then the request is denied with 403 and no file is generated
Secure API Access to Audit Logs
Given an API client with a valid OAuth2 access token scoped to audit:read When requesting GET /audit-logs Then the API responds 200 and returns only fields permitted by the client’s scopes and role Given a request made without TLS or with an invalid/expired token When accessing any audit endpoint Then the API responds 401 and returns no data Given a client lacking pii:read scope When requesting PII-restricted fields Then the API responds 403 and those fields are omitted or redacted Given sustained high-frequency requests over 600/min per client When limits are exceeded Then the API responds 429 with a Retry-After header and the rate-limit event is itself logged
Compliance Dashboards Accuracy & Freshness
Given audit logs for the last 24 hours When viewing dashboards Then pass/fail rates, top violations, average time-to-fix, override frequency, and trend lines match recomputation from raw logs within 0.5% absolute difference Given new audit events are ingested When time passes Then dashboards refresh within 5 minutes of event ingestion at p95 Given a Viewer role user When accessing dashboards Then only aggregated metrics are displayed and drill-down respects access controls with no PII exposure Given a dashboard widget is exported as CSV or PNG When requested Then the export matches the on-screen data at the time of export and includes a timestamp and filter summary
Webhook/Event Delivery to External SIEM
Given a compliance event of type evaluation.failed, fix.applied, override.granted, export.blocked, or export.released When it occurs Then a webhook is enqueued within 5 seconds containing minimal payload (event_id, event_type, occurred_at, batch_id, item_id, rule_id, marketplace_id, actor_id) with no names/emails Given a subscriber with a configured signing secret When a webhook is delivered Then the request includes an HMAC-SHA256 signature and timestamp header that validates against the stored secret Given the subscriber endpoint returns non-2xx or times out When retrying Then deliveries use exponential backoff for up to 24 hours with idempotency keys ensuring at-least-once and de-duplication on the receiver Given repeated delivery failures beyond the retry window When the subscription is marked failing Then the system surfaces alerts in-app and via configured notification channels
Retention, PII Minimization, and Access Controls
Given a workspace retention policy of 365 days (configurable) When an audit entry exceeds the retention horizon Then the entry and linked artifacts are purged within 24 hours while aggregated metrics remain Given PII minimization is enabled by default When audit entries are created Then only pseudonymous IDs and hashed/tokenized artifact references are stored; no emails, full names, or full-resolution images are persisted in the log Given a user without the Compliance Admin role When requesting raw artifacts or override notes Then access is denied with 403 and those fields are redacted in responses Given a legal hold applied to a batch or case When retention jobs run Then affected entries are excluded from purge until the hold is lifted and the hold lifecycle is itself audited

Live Clean

See background removal and key fixes as you frame the shot. Real‑time previews show pure‑white backgrounds, centering, and margins so you compose once, avoid reshoots, and ship marketplace‑ready images straight from your phone.

Requirements

Real-time Background Removal Preview
"As a solo e-commerce seller, I want to see my product on a pure-white background in real time so that I can compose correctly and avoid reshoots."
Description

Stream the live camera feed through an on-device segmentation model and composite the subject onto a pure #FFFFFF background with sub-100ms end-to-end latency and a stable 24+ FPS preview. Ensure WYSIWYG parity between preview and final export by reusing the same matting parameters and color management. Provide edge-aware smoothing, halo suppression, and transparency handling for fine details (e.g., hair, transparent packaging). Include a toggle to compare Original vs Clean view, and clearly surface low-confidence mask states. Implement graceful degradation: dynamically reduce preview resolution, or fall back to server inference if device capability is insufficient, with user consent and bandwidth safeguards. Maintain session persistence for chosen settings and ensure minimal battery and thermal impact via GPU/NPU acceleration and adaptive frame skipping.

Acceptance Criteria
Realtime Latency & FPS on Capable Devices
Given a device that passes the Capable hardware check (GPU/NPU available, camera 30+ FPS) When Live Clean preview is active for 60 seconds with Auto quality Then the 90th percentile end-to-end latency from sensor timestamp to displayed composited frame is <= 100 ms And the average preview frame rate is >= 24 FPS with no dips below 22 FPS exceeding 500 ms And dropped or compositor-skipped frames are <= 5% of total And time-to-first composited frame is <= 800 ms And hardware acceleration is utilized for inference/compositing (verified by runtime hardware-accel logs) And big-core CPU utilization averages <= 40% during steady-state preview
WYSIWYG Parity Between Preview and Export
Given a test frame captured during preview and its corresponding exported image When both are compared in sRGB with identical ICC profiles Then the median CIEDE2000 (ΔE00) across overlapping pixels is <= 1.0 And the mean absolute pixel difference is <= 1.5% And subject centering offset and safe-box margins match within 1 px And background pixels are pure #FFFFFF (RGB 255,255,255) with no values below 254 And the same matting model and parameter version are recorded in metadata for both preview and export
Edge Fidelity & Halo Suppression
Given the curated edge-case test set (hair, fur, transparent packaging, fine details) When the on-device matte is evaluated Then mean IoU across subjects is >= 0.90 And boundary F-score at 1-px tolerance is >= 0.85 And average foreground-to-background halo width is <= 1 px on high-contrast edges And semitransparent regions retain alpha within ±10% of ground truth And no background color bleed is detected within 2 px of the subject edge (RGB deviation <= 2)
Original vs Clean Toggle & Confidence Surfacing
Given the preview is in Clean mode When the user taps the Original/Clean toggle Then the view switches within 100 ms and preserves current framing And the toggle state persists for the session and across app restarts And capturing/exporting while in either mode uses Clean processing unless explicitly set to Original export And when mask confidence mean < 0.60 or > 10% of pixels have confidence < 0.50, a Low Confidence indicator and dotted mask overlay appear within 150 ms
Graceful Degradation & Server Fallback with Consent
Given Auto quality is enabled When measured latency > 100 ms or FPS < 24 for > 1 s Then preview resolution steps down one tier (1080p→900p→720p→540p) and logs the change And resolution never drops below 540p Given ongoing underperformance after two downscales and user has granted consent When connected to an unmetered network (Wi‑Fi by default) and within the bandwidth cap Then server inference activates and preview achieves >= 15 FPS and <= 300 ms latency And all frames are transmitted over TLS 1.2+ and discarded server-side within 60 s And if the bandwidth cap (default 50 MB/session) would be exceeded, the app prompts the user; otherwise stay on-device Given no network or consent is available When underperformance persists Then the app remains on-device at the lowest tier, shows a non-blocking notice, and continues preview
Composition Guides: Centering & Margins Compliance
Given marketplace preset Amazon Product Image with subject occupancy target 85%±5% and centering within ±3% When the Clean preview is active with guides enabled Then the live subject bounding box derived from the matte is displayed And occupancy percentage is computed relative to the export crop and shown And guide color turns green when within spec and red when out of spec And at capture/export the measured occupancy and centering match preview within ±2% and 1 px respectively
Settings Persistence Across Sessions
Given the user sets preferences (Original/Clean mode, guides on/off, marketplace preset, confidence overlay, Auto quality, server fallback consent, bandwidth cap) When the app is force-closed and relaunched Then all preferences persist without network access And resetting settings restores defaults And the last used model/parameter version is recorded in session metadata
Marketplace Safe-Area Overlays
"As a marketplace seller, I want visual guides that show centering and required margins while I shoot so that my images meet listing rules without manual trial and error."
Description

Display live composition guides purpose-built for marketplace compliance, including center crosshair, rule-of-thirds grid, and preset-safe areas for Amazon, eBay, Etsy, and Shopify. Show dynamic bounding boxes for required margins, subject coverage (e.g., 85% rule), and target aspect ratios, with color-coded warnings when the subject is off-center or exceeds safe bounds. Allow quick preset switching, margin fine-tuning in %, and lockable aspect ratios. Apply non-destructive live crop that is carried into the captured asset and metadata so downstream exports match what was framed on-device.

Acceptance Criteria
Quick Preset Switching for Marketplace Overlays
Given Live Clean mode is active and the preset picker is visible When the user selects Amazon, eBay, Etsy, or Shopify from the preset list or swipes to change presets Then the overlay updates within 200 ms to the selected preset and displays the center crosshair, rule‑of‑thirds grid (respecting last toggle state), preset safe‑area mask, and target aspect ratio And the selected preset label is shown for 1.5 seconds and haptic feedback confirms the switch And previously saved margin fine‑tuning and aspect‑ratio lock for that preset are restored
Real-Time Subject Coverage and Margin Compliance
Given a subject is detected in frame and a marketplace preset with required coverage and margins is active When the subject moves or the camera reframes Then dynamic bounding boxes render for required margins and a live subject coverage percent is computed And the compliance indicator is green when subject coverage is within the preset range and all margins are within bounds, yellow when within 2% of any threshold, and red when below/above required coverage or margins are violated And numeric readouts show coverage % and per‑edge margin % to one decimal place
Off-Center Detection Warnings
Given the center crosshair overlay is enabled and a subject centroid is tracked When the subject centroid offset from frame center is calculated Then the centering indicator is green when offset ≤ 2% of the short edge, yellow when > 2% and ≤ 5%, and red when > 5% (unless the preset defines stricter values) And directional nudge arrows display toward the nearest compliant center when yellow or red And the warning state clears within 150 ms after the subject re‑enters the compliant zone
Margin Fine-Tuning in Percent
Given a marketplace preset is active When the user adjusts the margin control Then margins can be tuned in 0.5% increments per edge across a 0–20% range, with an option to link/unlink edges And the safe‑area overlay updates within 100 ms of each change And the configured margin values persist per preset across app relaunches And a Reset action restores the preset’s default margins
Aspect Ratio Lock with Non‑Destructive Live Crop
Given the aspect‑ratio menu is open in Live Clean When the user selects and locks an aspect ratio (Marketplace Default, 1:1, 4:5, 5:4, 3:2, 16:9) Then crop rails constrain composition and pinch/zoom respects the lock without drifting And on capture, the full‑resolution original is saved plus crop metadata {x,y,w,h,aspectRatio,presetId,margin%} normalized to [0..1] And reopening the asset applies the same live crop non‑destructively and allows reverting to the full image
Export Matches On‑Device Framing
Given an asset captured with live crop metadata and a chosen marketplace preset When exporting via PixelLift using that preset Then the exported image framing matches the live preview within max(1 px, 0.2% of the short edge) And the output aspect ratio equals the locked ratio within ±0.1% And configured margins are met or exceeded (never undercut) on all edges And background removal respects the crop with no unintended padding outside the safe area
Real-time Exposure & White Balance
"As a product photographer on my phone, I want live exposure and white balance corrections tailored to white backgrounds so that colors look accurate and consistent across my listings."
Description

Provide auto-exposure and auto white balance tuned for a pure-white composited background to prevent clipping and color cast. Render a live histogram and highlight warnings, with tap-to-expose and tap-to-white controls. Support exposure/white balance lock to maintain consistency across a batch. Calibrate to D65/6500K white with optional gray-card quick calibration for high fidelity color of the product. Persist chosen settings per session and record them in asset metadata to enable consistent bulk edits and auditability in PixelLift.

Acceptance Criteria
Live Auto Exposure with Histogram & Highlight Warnings on White Composite
Given Live Clean mode is active with a pure-white composite background And auto-exposure is enabled When the scene luminance changes by ≥1 EV Then exposure adjusts within 300 ms to keep the composited background at 90–95% IRE And fewer than 0.5% of pixels within the foreground mask are at ≥99% IRE (clipped) And the live histogram updates at ≥15 Hz reflecting the new exposure And highlight warnings (zebra) are overlaid for pixels at ≥95% IRE with overlay latency ≤100 ms
Auto White Balance Calibrated to D65
Given auto white balance is enabled with a D65 target When a neutral gray surface occupies ≥10% of the frame Then computed CCT is 6500 K ± 150 K And tint deviation is within ±5 mired of neutral And the neutral patch has mean channel ratio R:G:B within ±3%
Tap-to-Expose Targeting
Given the live preview is active When the user taps a point on the foreground product area Then exposure is set so the average of a 9% radius region around the tap is 50% IRE ±5% And clipped pixels within the foreground mask do not exceed 0.5% And time from tap to stabilized exposure is ≤250 ms
Tap-to-White Neutralization
Given the live preview is active When the user taps on a neutral area of the product Then white balance adjusts so the tapped region has R, G, and B mean values within ±2% of each other And computed CCT relative to D65 is within ±200 K And time from tap to stabilized white balance is ≤250 ms
Exposure and White Balance Lock for Batch Consistency
Given exposure and white balance are locked When capturing a batch of 20 images over 60 seconds with ambient variation up to ±0.5 EV and ±200 K Then exposure parameters remain constant within ±0.1 EV And white balance remains constant within ±50 K and ±2 tint units And automatic adjustments are disabled until unlock is performed And a lock indicator is visibly present during capture
Gray-Card Quick Calibration Workflow
Given gray-card calibration is initiated And a neutral 18% gray card covers ≥10% of the foreground area When the user taps Calibrate Then white balance is set so the gray-card region has ΔE00 ≤ 2 relative to D65 And exposure is set so the gray-card region measures 45–55% IRE And calibration completes within 2 seconds with clear success or failure feedback
Session Persistence and Metadata Audit Trail
Given a capture session with custom exposure/white balance settings or lock states When the user exits and returns to the session Then the last chosen exposure/white balance settings and lock states are restored And when exporting a batch of images Then each asset’s metadata includes ExposureTime, ISO, ExposureCompensation, WhiteBalanceMode (Auto/Locked/GrayCard), ColorTemperature (K), Tint, CalibrationMethod (D65/GrayCard), and Timestamp And 100% of exported assets contain these fields with values matching the in-app settings at capture time
Touch Mask Refinement
"As a seller shooting complex items, I want to quickly fix any masking mistakes by brushing areas in or out so that edges look clean without retouching later."
Description

Enable quick, intuitive on-screen refinement of the live mask with add/remove brushes, edge feather, and smart scribble gestures. Update the preview within 50ms to preserve real-time feel. Provide undo/redo, zoomed loupe for edges, and hair/mesh mode for challenging textures. Allow applying refinements pre-capture and post-capture, and store refinement strokes so the same adjustments can be replayed across a batch in PixelLift’s desktop/web editor if desired.

Acceptance Criteria
Live Preview Latency Under Active Refinement
Given Live Clean preview is active on a supported device and the user continuously draws refinement strokes for 30 seconds When mask updates are computed and rendered Then the 95th-percentile time from input sample to visible preview update is <= 50 ms And the median time is <= 25 ms And preview frame rate remains >= 24 FPS with < 1% dropped frames And no single update stall exceeds 150 ms
Brush Tools and Smart Scribble Refinement
Given the user selects Add or Remove brush with size adjustable from 2–80 px (device-independent) and opacity 100% When the user draws over the image Then pixels under the stroke are added to or removed from the mask according to the selected tool with a tolerance radius equal to the brush size And switching between Add and Remove occurs in one tap or a press-and-hold gesture with visual/haptic feedback within 100 ms Given the user performs foreground/background scribble gestures When the scribble ends Then the mask expands or contracts to regions of similar color/texture connected to scribbled areas while respecting hard boundaries And at least 90% of pixels within 5 px of the scribble are classified as indicated And overshoot beyond intended region is <= 15% of the scribbled area on the standard test set And the visible mask update occurs within 50 ms
Edge Feather Control with Precision Loupe
Given a feather control with range 0–10 px in 0.5 px increments When the user adjusts the feather value Then edge softness changes accordingly and the preview reflects the change within 50 ms, with the current value displayed numerically Given the brush tip approaches an edge within 20 px When the user holds for 200 ms or taps the loupe control Then a loupe appears with 3× magnification and a crosshair, positioned so it does not occlude the touch point And the loupe follows finger movement with latency <= 30 ms and can be pinned/unpinned with a tap
Undo/Redo Functionality and Limits
Given a sequence of refinements (strokes, scribbles, feather changes, mode toggles) has been applied When Undo is invoked Then the last atomic refinement is reverted and the preview updates within 50 ms And at least 50 undo steps per image are supported And invoking a new refinement after an Undo clears the Redo stack And the Undo/Redo history persists across tool switches, zoom/loupe usage, and pre/post-capture transitions
Hair/Mesh Mode Detail Preservation
Given Hair/Mesh mode is enabled When refining around hair, fur, or mesh fabric Then semi-transparent regions retain alpha detail without halos wider than 2 px on the standard test set And edge recall improves by >= 20% compared to normal mode on the same set And preview update latency remains <= 50 ms (95th percentile)
Pre- and Post-Capture Parity and Persistence
Given the user refines the mask in pre-capture live view When switching to post-capture edit view (or back) Then all refinement tools (add/remove brush, scribble, feather, hair/mesh, loupe) are present with identical behavior and UI And all applied refinements and history persist across the transition without loss And capturing a photo does not reset or alter the current mask unless the user confirms a reset action
Stroke Persistence and Batch Replay in Desktop/Web Editor
Given the user saves an image with refinements When the project is opened in PixelLift desktop/web editor and batch replay is invoked on similar images Then all strokes, scribbles, feather values, and mode toggles are stored with timestamps, brush parameters, and coordinate transforms relative to the image And replay applies adjustments with spatial alignment error <= 3 px after auto-align And if auto-align fails, the user is prompted to place anchor points and retry, with success/fail reported per image And refinement stroke data is exported in versioned JSON and passes schema validation
Instant Export to PixelLift Workflows
"As a busy seller, I want captured images to flow straight into my PixelLift projects and export templates so that I can list faster without manual transfers."
Description

On capture, automatically save the composed, masked image to a selected PixelLift project with SKU tags, applied preset, and crop. Queue assets for background upload with offline-first caching and automatic retry. Attach session metadata (preset, margins, exposure/WB locks, app version) for reproducibility. Offer one-tap routing into existing bulk export templates and A/B variant tasks so images shot via Live Clean flow seamlessly into listing pipelines without additional setup.

Acceptance Criteria
Auto Save to Selected Project on Capture
Given a Live Clean session with a selected PixelLift project and the user taps the shutter When the capture is successful Then the app creates an asset record linked to that project with status "Queued" within 500 ms of capture Given network connectivity is available at upload start When background upload begins Then the composed, masked image is uploaded and appears in the project asset list within 5 seconds for a 10 MB file on a 10 Mbps uplink (simulated), and status transitions to "Uploaded" Given the same capture event is retried due to app relaunch When export logic runs Then no duplicate asset is created for the same capture UUID (idempotent export)
SKU Tagging and Validation
Given the user has entered one or more SKU tags before capture When the asset is saved to the project Then all valid tags are attached to the asset metadata and visible via project UI and API Given SKU input includes leading/trailing spaces When tags are saved Then whitespace is trimmed and original casing is preserved Given a tag exceeds 64 characters or contains characters outside [A–Z, a–z, 0–9, -, _, .] When the user attempts to capture/export Then the app blocks export, highlights invalid tags, and shows a corrective message without losing other capture data
Preset and Crop Fidelity
Given a Live Clean preset and crop are applied before capture When the asset is exported Then the exported image's visible region and aspect ratio match the on-screen preview within ±1 px and ±0.1% respectively Given a preset with identifier and version (e.g., preset_id, preset_version) is active When the asset is exported Then metadata includes preset_id, preset_version, and a deterministic parameter hash for reproducibility Given the user changes preset after capture but before upload completes When the upload finishes Then the asset uses the preset and crop captured at shutter time, not any later changes
Offline-First Queue and Automatic Retry
Given the device is offline at capture time When export is triggered Then the asset binary and metadata are cached locally within 500 ms and marked "Queued (Offline)" Given connectivity resumes When the background service detects a usable network Then upload starts within 10 seconds and retries automatically with exponential backoff on transient failures (HTTP 5xx, timeouts) for up to 24 hours Given repeated upload failures exceed the retry window When the queue processes the asset Then the asset remains with state "Action Required", presents a manual retry action, and no duplicate assets are created Given the app is force-quit or the device reboots When the app relaunches Then the export queue is restored from disk and resumes without data loss
Session Metadata Attachment
Given a capture occurs in Live Clean When the asset is exported Then metadata includes: preset_id, preset_version, margins (top/right/bottom/left, px), center_offset (x,y, px), exposure_lock (boolean), wb_lock (boolean), app_version, device_model, and capture_timestamp (ISO 8601) Given the asset is viewed in PixelLift web or retrieved via API When metadata is fetched Then all fields are present, immutable, and match the values at capture time Given server-side validation detects missing or corrupted required metadata When the upload is received Then the server rejects with a descriptive error code/message and the client surfaces the error and allows retry after correction
One-Tap Routing to Bulk Export Templates
Given the selected project has one or more existing bulk export templates When the user taps "Route to Template" after capture Then the asset is added to the chosen template's job queue within 3 seconds and a success confirmation is shown Given the user has configured a default template When capture completes Then a one-tap action routes the asset to that default template without additional configuration Given the template defines output sizes and formats When the job runs Then the asset inherits those settings without additional setup and the job appears under the template's run history
A/B Variant Task Enrollment
Given the project has A/B variant tasks configured When the user selects "Create A/B Variants" on capture confirmation Then at least two variant tasks (A and B) are created and linked to the parent asset within 3 seconds Given variant tasks produce derivative images When processing completes Then each variant retains the parent's SKU tags and session metadata and includes a unique variant_id Given the user routes the asset to both a bulk template and A/B tasks When processing begins Then task dependencies are respected and no duplicate processing runs are created for the same asset
Device Performance & Compatibility Layer
"As a user on a mid-range phone, I want the live preview to be smooth and reliable so that I can trust what I see matches the final output."
Description

Provide a capability layer that detects device GPU/NPU availability, thermal headroom, and camera API support, then selects the optimal inference path (Metal/ANE on iOS, Vulkan/NNAPI on Android). Enforce targets of 24+ FPS preview, <100ms latency, and <500MB peak memory, with adaptive resolution scaling and model quantization. Implement feature gating for low-end devices, preflight benchmarking, crash/ANR telemetry, and remote configuration to roll out model updates safely. Ensure privacy controls for any optional cloud fallback and a seamless UX across a broad device matrix.

Acceptance Criteria
Runtime Capability Detection & Inference Path Selection
Given an iOS device with Metal and ANE available When the user opens Live Clean preview Then the inference backend selects Metal/ANE and logs backend=ANE and device_capabilities=detected And no unsupported API calls are attempted Given an Android device with NNAPI 1.3 and Vulkan available When the user opens Live Clean preview Then the backend selects NNAPI with GPU delegate via Vulkan and logs backend=NNAPI_VULKAN and device_capabilities=detected Given a device lacking GPU/NPU or required driver versions When the user opens Live Clean preview Then the backend falls back to CPU or simplified model path and logs backend=CPU_FALLBACK And feature flags disable unsupported effects without crashes or ANRs Given a device exposing limited camera APIs (e.g., iOS legacy AVCapture features or Android Camera2 LIMITED) When the user opens Live Clean preview Then the app selects a compatibility camera pipeline and maintains a stable preview without crash or ANR
Real-time Preview Performance Budgets
Given normal thermal state (no throttling) and indoor lighting ≥300 lux When Live Clean preview runs for 30 seconds at default Auto quality Then average FPS ≥ 24 and p95 end-to-end frame latency ≤ 100 ms And p99 app resident memory ≤ 500 MB And <1% of frames drop below 20 FPS Given FPS drops below 24 for >1 second When adaptive controls are enabled Then the system reduces input resolution and/or compute tier within 200 ms And restores FPS ≥ 24 within 2 seconds without app restart
Adaptive Scaling and Quantization Under Load
Given the device enters a throttled thermal state or concurrent GPU/NPU load is detected When Live Clean preview runs Then the pipeline steps down resolution by at most two tiers and switches to INT8 quantized model if needed And segmentation mask quality remains IoU ≥ 0.85 vs FP16 baseline on the validation set And SSIM on reference frames remains ≥ 0.90 Given thermal/headroom recovery is sustained for 10 seconds When monitoring detects FPS ≥ 28 and latency ≤ 80 ms Then the pipeline steps up one tier without causing FPS dips >10% for >500 ms
Feature Gating on Low-End Devices
Given the preflight benchmark score is below threshold T or required APIs are missing When Live Clean starts Then real-time background removal is disabled and Preview Only mode is enabled And a non-blocking banner states Reduced quality mode with a Learn more link Given features are gated When the user captures a photo Then the app offers queue-for-offline processing and (if enabled) cloud fallback And shutter-to-capture latency remains ≤ 200 ms And no crashes or ANRs occur
Preflight Benchmark & Capability Cache
Given first launch on a device or after app/OS update or remote-config model pack change When Live Clean is first opened Then a micro-benchmark runs for ≤ 5 seconds to measure throughput, memory, and thermal rise And results are cached by device_model+OS+app_version+model_pack_version for 30 days And subsequent launches skip the benchmark unless cache is stale or a major change is detected Given the benchmark is in progress When the user exits Live Clean within 3 seconds Then benchmarking pauses and resumes on next entry without impacting capture UX
Telemetry, Crash/ANR, and Remote Config Safe Rollouts
Given telemetry consent is granted When the user uses Live Clean Then the app logs backend choice, FPS, latency p50/p95, memory p95, throttling events, gating decisions, crashes/ANRs with session/device class And no image pixels or sensitive content are included in telemetry Given remote configuration fetch succeeds When a new model or backend is rolled out Then staged rollout by cohort percentages is applied and a kill switch can disable the rollout within 10 minutes And if fetch fails, the last-known-good configuration is used Given a crash occurs in Live Clean When the app relaunches Then the crash report uploads per consent on Wi‑Fi/cell policy And the app starts with safe settings (reduced tier) for the next 3 sessions
Privacy Controls for Optional Cloud Fallback
Given the on-device path cannot meet performance budgets and cloud fallback is enabled via remote config When the user is first prompted Then the consent dialog offers Allow once, Always allow, and Don’t allow And no frames or metadata are uploaded until consent is granted Given the user consents When cloud fallback is used Then images are encrypted in transit (TLS 1.2+) and deleted within 24 hours of processing And the app provides Revoke consent and Clear cloud history controls in Settings Given the user declines When Live Clean runs Then on-device degraded mode continues and no network requests with image data are made

SafeZone Overlay

Marketplace‑specific guides (grids, aspect ratios, and margin rings) overlay your camera view with a live fill score. Maximize product size while staying compliant with Amazon, Etsy, and TikTok rules—no more guesswork crops.

Requirements

Real-time SafeZone Overlay Rendering
"As a solo seller shooting product photos, I want a live overlay that shows the safe area while I frame the shot so that I can maximize product size without violating marketplace rules."
Description

Render marketplace-specific guides (grids, aspect-ratio masks, and margin rings) in real time over the camera view and editor canvas to guide framing and cropping. Provide adjustable opacity, color-coded zones (safe, warning, prohibited), snap-to-safe-area guides, and orientation support (portrait/landscape/square). Maintain smooth performance (target 60 FPS; gracefully degrade to 30 FPS on low-power devices), and ensure parity across web (webcam), mobile capture, and the existing editing canvas. Persist user preferences per account, support multi-image sessions, and cache overlay assets for fast loads.

Acceptance Criteria
Real-time Overlay Performance and Graceful Degradation
Given a standard-capability device and 1080p camera feed with SafeZone Overlay enabled at default settings When capturing for 30 seconds Then average FPS is >= 58 and 95th percentile frame time <= 22 ms and dropped frames <= 2% Given a low-power device as detected by capability profiling When the overlay is active Then the renderer switches to simplified mode automatically and average FPS is >= 28 and 95th percentile frame time <= 35 ms Given any device When enabling or disabling the overlay Then the transition completes within 150 ms without a frozen frame or UI input lock
Adjustable Opacity and Color-Coded Zones
Given the overlay opacity control (0–100%) When the user sets any value in 5% increments Then the rendered overlay alpha matches within ±2% of the chosen value and persists across sessions for the signed-in account Given a selected marketplace template When the overlay renders Then zones are consistently colored Safe=green, Warning=amber, Prohibited=red with WCAG contrast ratio >= 3:1 against the live feed at default opacity across all platforms Given the user switches marketplace templates When the overlay updates Then color semantics (safe/warning/prohibited) remain consistent and legend/tooltips reflect the current template
Snap-to-Safe-Area Guides in Camera and Editor
Given snap-to-safe-area is ON (default) When the crop box or subject boundary is dragged within 8 px or 0.5% of the shorter image dimension (whichever is greater) from a safe boundary Then the element snaps to that boundary and a visual snap indicator appears within 50 ms Given snap-to-safe-area is ON When the user continues dragging away from a snapped state Then de-snap occurs after >4 px movement (hysteresis) and the indicator hides Given the user toggles snap-to-safe-area OFF When dragging or resizing Then no snapping occurs and the preference persists per account across sessions
Orientation Support Across Portrait, Landscape, and Square
Given the device rotates or the user selects portrait, landscape, or square When the orientation/aspect changes Then the overlay reflows to the correct aspect within 100 ms without stretching or misalignment Given square orientation is active When switching to portrait or landscape for the same marketplace Then safe, warning, and prohibited boundaries map to the correct template definitions for that orientation Given both camera view and editor canvas are open When the user changes orientation in either context Then both surfaces reflect the same orientation and boundary positions within 1 px consistency
Cross-Platform Visual Parity (Web, Mobile, Editor Canvas)
Given the same marketplace template and source image dimensions When rendering on web (webcam), mobile capture, and the editor canvas Then boundary coordinates for safe/warning/prohibited zones match within ±0.5% of dimension or ±2 px (whichever is greater) Given device pixel ratios from 1.0 to 3.0 When grids and margin rings render Then line weights vary by no more than ±1 px and align exactly to image edges Given the user inspects a zone edge via debug cursor/inspector When comparing platforms Then reported normalized coordinates are identical modulo rounding rules
User Preference Persistence per Account and Multi-Image Sessions
Given a signed-in user sets overlay preferences (selected marketplace template, opacity value, snap toggle, last used orientation) When starting a new session or opening on another device Then the same preferences auto-apply within 300 ms of opening the camera/editor Given a multi-image capture or edit session of N images (N>=2) When advancing from image i to image i+1 Then overlay settings remain unchanged unless the user explicitly modifies them Given the user logs out and logs back in When opening the camera/editor Then the previously saved overlay preferences are restored
Overlay Asset Caching for Fast Loads
Given first-time load of a marketplace overlay on a fresh cache When opening the camera/editor Then time to first overlay render is <= 500 ms and total network requests for overlay assets <= 1 per template version Given a template has been loaded once in the current app version When switching back to that template Then no network request is made for its assets and render begins within 100 ms Given overlay assets are updated (version bump) When loading after the update Then the cache invalidates old assets, fetches new ones once, and renders without missing or stale visuals
Marketplace Rules Engine & Presets
"As a seller listing on multiple marketplaces, I want presets for each marketplace and category so that I can switch contexts without manually configuring overlays."
Description

Implement a centralized, versioned rules engine that encapsulates marketplace-specific constraints (Amazon, Etsy, TikTok, etc.) by region and category, including aspect ratios, minimum/maximum margins, background allowances, and text/logo restrictions. Expose presets via an internal API for the overlay renderer and validation services, with schema validation, default fallbacks, unit conversion (px/%, metric/imperial), and effective-date handling. Log rule usage for analytics, support custom team presets, and document rule mappings to categories/SKUs.

Acceptance Criteria
Preset Retrieval for Overlay and Validator Clients
Given a client type of overlay or validator and a valid marketplace=Amazon, region=US, category=Home and effectiveDate=today When the client GETs /internal/rules/presets?marketplace=Amazon&region=US&category=Home&effectiveDate=YYYY-MM-DD Then the response status is 200 and content-type is application/json And the body includes fields: id, marketplace, region, category, version, effectiveStart, effectiveEnd (nullable), aspectRatio, minMargin, maxMargin, backgroundAllowed, textLogoRestrictions, units, mapping (categoryPath, skuPatterns[]), source (namespace, ownerId) And the body includes versionTag (ETag) and schemaVersion And the preset returned is the one effective on the requested effectiveDate And the same request with client=validator receives an identical preset payload
Versioning and Effective-Date Resolution
Given two published versions for marketplace=Etsy, region=UK, category=Jewellery: V1 effectiveStart=2025-07-01 effectiveEnd=2025-09-30 and V2 effectiveStart=2025-10-01 effectiveEnd=null When requesting the preset with effectiveDate=2025-09-15 Then V1 is returned with versionTag matching V1 When requesting the preset with effectiveDate=2025-10-15 Then V2 is returned with versionTag matching V2 When requesting without effectiveDate Then the engine resolves using the server current date When requesting with a date before any effectiveStart and a global default exists Then the global default is returned and response.resolution.fallback=true with fallbackFrom="no-effective-version"
Schema Validation and Required Fields
Given a ruleset payload to create/update via POST /internal/rules/presets with missing required field aspectRatio When the request is submitted Then the response status is 400 and body includes errors[0].path="/aspectRatio" and errors[0].code="required" Given a payload where minMargin is a percentage outside [0, 100] When the request is submitted Then the response status is 400 and errors[0].code="out_of_range" Given a payload containing unknown property fooBarBaz When the request is submitted Then the response status is 400 and errors[0].code="unknown_field" Given a payload where textLogoRestrictions contains unsupported value When submitted Then the response is 400 and errors[0].code="enum" Given a valid payload matching schemaVersion=v1 When submitted Then the response is 201 and the stored preset validates against JSON Schema v1
Unit Conversion and Canvas-Aware Outputs
Given a preset with minMargin=5% and maxMargin=15% and aspectRatio=1:1 When the client GETs /internal/rules/presets?...&canvasWidthPx=1000&canvasHeightPx=1000 Then the response includes computed.margins.px.min=50 and computed.margins.px.max=150 and computed.margins.percent.min=5 and computed.margins.percent.max=15 Given a request with units=imperial&dpi=300&canvasWidthIn=4&canvasHeightIn=4 When the client requests the preset Then the response includes canvasWidthPx=1200 and canvasHeightPx=1200 and conversions.rounding="half_up" Given aspectRatio=4:5 When the client requests computed.safeZone for canvasWidthPx=2000 Then computed.safeZone.widthPx=2000 and computed.safeZone.heightPx=2500 is not allowed and response indicates required crop heightPx=2500 to satisfy aspectRatio
Fallback Chain with Team Overrides and Traceability
Given no exact match for marketplace=TikTok, region=JP, category=Toys but teamId=team-123 has a category-level custom preset When the client requests a preset with teamId=team-123 Then the team preset is returned and response.resolution.chain=["team.category","marketplace.category","marketplace.default","global.default"] and response.resolution.selected="team.category" Given a request for SKU=SKU-9 that matches a team SKU-level preset When requested Then the SKU-level preset takes precedence over team category-level Given no team or marketplace category preset exists When requested Then marketplace default is returned; if absent, global default is returned And the response includes resolution.fallback=true and resolution.trace listing each attempted key in order
Validation Service Compliance Checks
Given a preset for Amazon US with minMargin=5% maxMargin=15%, backgroundAllowed=false, textLogoRestrictions=["no_text","no_logo"] When POST /internal/rules/validate with canvasWidthPx=2000 canvasHeightPx=2000 bboxPx={left:100,top:100,right:1900,bottom:1900} and detectedBackground="solid_white" and detectedTextLogo=false Then response.status=200 and body.pass=true and violations=[] When POST /internal/rules/validate with bboxPx={left:0,top:0,right:2000,bottom:2000} Then body.pass=false and violations contains code="MARGIN_MIN" with actual=0% requiredMin=5% When backgroundAllowed=false and detectedBackground!="solid_white" Then body.pass=false and violations contains code="BACKGROUND_DISALLOWED" When aspect ratio of bbox differs from preset aspectRatio beyond tolerance=0.5% Then body.pass=false and violations contains code="ASPECT_RATIO_MISMATCH" with deltaPercent reported
Usage Logging and Analytics Events
Given any successful or failed call to /internal/rules/presets or /internal/rules/validate When the response is produced Then an analytics event is emitted with fields: eventId, timestamp, clientType, endpoint, marketplace, region, category, sku (nullable), teamId (nullable), presetId, versionTag, effectiveDate, resultType (hit|miss|fallback|validation_pass|validation_fail), latencyMs And the event is delivered to the analytics sink with p99 delivery latency <= 5s and error rate < 0.1% And each API response includes a correlationId that matches analytics.event.correlationId
Live Fill & Compliance Scoring
"As a user, I want a live score that tells me if my product fills the allowable area and meets rules so that I can adjust framing before taking the photo."
Description

Calculate and display a live fill score and compliance status based on the detected foreground object within the marketplace’s safe area thresholds. Use PixelLift’s segmentation/background-removal mask to compute fill percentage and evaluate pass/warn/fail in under 100 ms per frame. Show numeric fill, visual indicators, and actionable hints (e.g., move closer/zoom out). Support single and multi-object scenes, work during capture and on existing images, emit analytics events, and function offline with last-known rules.

Acceptance Criteria
Live Fill Score During Camera Capture
Given camera preview is active and a marketplace preset is selected When a foreground object is detected via segmentation Then the app computes fill_percent = masked_pixels_in_safe_area / total_safe_area_pixels * 100 using the current marketplace safe zone And updates fill_percent, compliance_status indicator, and actionable_hint in the UI at >= 10 Hz And p95 end-to-end latency from frame arrival to on-screen update is <= 100 ms; p99 <= 120 ms under normal lighting And fill_percent is shown as a numeric value with 1 decimal place And visual indicator color maps: Pass=green, Warn=amber, Fail=red And actionable hints follow rules: - If fill_percent < pass_min - 3.0 pp => "Move closer" - If fill_percent > max_allowed or subject touches safe margin => "Zoom out" - If object centroid offset from safe-area center > 12% of the smaller dimension => "Re-center" - If selected aspect ratio mismatches marketplace required ratio => "Switch to <required_ratio>" And hints change only if the underlying condition persists for >= 300 ms
Marketplace Compliance Status Evaluation
Given the active marketplace has loaded safe area rules with pass/warn/fail bands When fill_percent and margin violations are evaluated per rules Then status=Pass when all rules are satisfied (fill_percent >= pass_min and <= pass_max if defined and no margin violations) Then status=Warn when within the warn band or confidence < threshold but no hard violations Then status=Fail when outside thresholds or any hard margin violation is detected And status changes within 1 frame of marketplace change And a reason_codes array is produced explaining the decision
Mask-Based Fill Computation Accuracy
Given a test set with ground-truth masks and safe areas When computing fill_percent using the PixelLift segmentation mask Then the absolute error vs. ground truth <= 1.5 percentage points for 95% of cases and <= 3.0 p.p. for 100% And anti-aliased mask edges contribute proportionally (subpixel area weighting) And bounding boxes are not used for fill calculations
Multi-Object Scene Handling
Given multiple foreground components are present within the safe area When selecting the object(s) to score Then the primary object is the largest connected component by area within the safe area And if the top two components differ by <= 15% in area, use the union for fill computation and set multi_object=true And primary selection must not switch more than once per second (hysteresis) And provide hint "Isolate primary object" when multi_object=true and status != Pass
Scoring on Existing Images
Given the user opens an existing image in analyze mode and selects a marketplace When segmentation, fill, and compliance are computed on-device Then results render within 200 ms for images up to 12 MP And the UI presents numeric fill, status, indicators, and hints identical to live mode And analytics events are emitted with mode=still
Offline Mode with Cached Rules
Given the device is offline When last-known rules for the selected marketplace exist (synced within 30 days) Then scoring uses cached rules without error and shows an "Offline rules" badge with last_sync_date And if no cached rules exist, status=Warn with reason "NO_RULES" and hints instruct to connect; fill_percent still computed And offline operation adds <= 10 ms per frame overhead And analytics events are queued locally (FIFO) up to 5000 events and auto-flushed on reconnection
Analytics Emission for Scoring Events and Hints
Given analytics is enabled and a scoring update occurs When emitting events Then emit scoring_update at most 2 Hz with fields: session_id, marketplace_id, timestamp_ms, fill_percent, compliance_status, reason_codes, hint_code, latency_ms, mode, object_count, offline_flag And 99% of events are accepted by the collector within 5 minutes of connectivity And no image pixels or PII are transmitted And disabling analytics results in zero events stored or sent
Auto-Preset Selection from Listing Metadata
"As a busy seller, I want the correct SafeZone preset to be chosen automatically from my listing info so that I don’t waste time configuring each session."
Description

Auto-select the correct SafeZone preset using connected listing metadata (marketplace, category, and region) from SKU tags, imported CSVs, or marketplace integrations. Provide zero-click setup for common cases, with clear rationale messaging and one-tap override. Fall back to the last used preset when metadata is unavailable. Respect privacy/permissions, record selection events for auditability, and expose a simple selector UI for manual changes.

Acceptance Criteria
Auto-Select Preset From Marketplace Metadata
Given a connected listing with marketplace, category, and region metadata from SKU tags When the SafeZone Overlay opens Then the system auto-selects the preset matching the mapping and applies it without user interaction within 300ms And the applied presetId equals the mapping table output for the metadata combination Given a connected listing with marketplace, category, and region metadata from an imported CSV When the SafeZone Overlay opens Then the system auto-selects the preset matching the mapping and applies it without user interaction within 300ms Given a connected listing with marketplace, category, and region metadata from marketplace integrations When the SafeZone Overlay opens Then the system auto-selects the preset matching the mapping and applies it without user interaction within 300ms
Rationale Messaging for Auto Selection
Given an auto-selection occurs When the preset is applied Then a rationale message is displayed containing marketplace name, category, region, metadata source, and mapping version/date And the message appears within 200ms of preset application and is visible until dismissed or overridden And the message is localized to the user’s current locale and is accessible to screen readers And dismissing the message does not change the selected preset
One-Tap Override of Selected Preset
Given an auto-selected preset is active for a listing When the user selects a different preset from the selector Then the new preset is applied within 150ms and becomes the current preset for that listing And an audit event is recorded with source="manual_override" And the manual override persists for the current listing until the user chooses Reset to Auto or selects another preset Given the user taps Reset to Auto When metadata is available Then the system re-evaluates metadata and applies the mapped preset within 300ms
Fallback to Last Used Preset When Metadata Unavailable
Given metadata is unavailable or access is denied for a listing When the SafeZone Overlay opens Then the system applies the last used preset for the current user within the same workspace And an info message indicates the fallback source as "Last used preset" Given no last used preset exists for the user in the workspace When the SafeZone Overlay opens Then no preset is auto-applied and the preset selector is opened to prompt manual selection
Privacy and Permission Compliance
Given the user has not granted permission to access marketplace integrations When the SafeZone Overlay opens Then the system makes no marketplace API calls and performs no background fetch of listing metadata Given permissions were previously granted but are now revoked When the user opens the SafeZone Overlay Then the system does not access marketplace metadata and uses the defined fallback behavior Then only the minimum fields required for selection (marketplaceId, categoryId, regionCode, sku, listingId) are processed and stored within the selection event; no raw credentials or full listing payloads are persisted
Selection Event Audit Logging
Given any preset selection occurs (auto, fallback, or manual override) When the preset is applied Then an immutable audit event is written with fields: timestamp (ISO 8601), userId, workspaceId, listingId, presetId, source (auto|fallback|manual_override), metadataSource (sku_tag|csv|integration|none), metadataFingerprint (SHA-256 of normalized metadata), permissionState (granted|denied|revoked), rationaleMessageId, appVersion And the event is persisted within 200ms and survives application crash/restart And events are retrievable via an internal API filterable by listingId and date range And events store identifiers only; no raw marketplace credentials or un-hashed metadata values are stored
Manual Preset Selector UI Accessibility and Performance
Given the user opens the preset selector When navigating with keyboard and screen reader Then all interactive elements are reachable in logical tab order, have accessible names, and announce the current selection and changes And each interactive target has a minimum 44x44 px touch area and meets WCAG AA contrast (>= 4.5:1) And the selector opens and renders the preset list within 200ms on a 1.5 Mbps connection And selecting a preset applies it and closes the selector with a single tap/click
Compliance Report & Export Annotations
"As a seller preparing bulk exports, I want a compliance report attached to each image so that I can confidently publish and resolve issues before upload."
Description

Generate a per-image compliance artifact capturing fill percentage, rule version, preset used, pass/warn/fail status, and any violations (e.g., margin underfill). Attach as embedded XMP metadata and produce sidecar JSON/CSV for bulk export. Include a small overlay thumbnail preview, enable export filters to include only compliant images, and surface a review UI to resolve violations. Integrate with PixelLift’s batch export pipeline and provide an API for partners to consume compliance data.

Acceptance Criteria
Per-Image XMP Compliance Metadata Embedded
Given an image processed in a batch with marketplace preset M and ruleVersion R When the export completes Then the output image file contains an XMP block under the PixelLift compliance namespace with fields: imageId (UUID), presetId=M, ruleVersion=R, status ∈ {Pass, Warn, Fail}, fillPercent ∈ [0.0,100.0] with 0.1 precision, violations[] (0+ items each with code, message, metricName, actual, expected, unit), generatedAt (ISO-8601 UTC), processorVersion And reading the XMP via ExifTool returns the same values produced by the compliance engine for that image And embedding the XMP does not alter the rendered pixels (MD5 of decoded pixel buffer before vs. after XMP write matches)
Sidecar JSON/CSV Compliance Files Generated for Batch Export
Given a batch of N images exported with Compliance Sidecars = On When the export completes Then each exported image has a sidecar JSON named {basename}.compliance.json containing: imageId, filename, presetId, ruleVersion, status, fillPercent, violations[], generatedAt, jobId And a batch CSV {jobId}.compliance.csv exists with header columns [imageId,filename,presetId,ruleVersion,status,fillPercent,violationCount,violationCodes,generatedAt] And the CSV row count equals the number of exported images And values in JSON and CSV match (e.g., fillPercent identical to 0.1 precision; violationCodes is a semicolon-separated list of codes) And sidecar filenames share the same basename as their image and are written to the same output directory
Overlay Thumbnail Preview Attached to Outputs
Given Include overlay thumbnail preview = On When the export completes Then a JPEG thumbnail at {basename}.compliance-thumb.jpg is created with max dimension ≤ 256 px and file size ≤ 60 KB, visually showing the safe-zone grids and margin rings over the image And the JSON sidecar embeds the same thumbnail as base64 at thumbnail.base64 and provides thumbnail.sha256 matching the file And the XMP contains a compliance.thumbnailHref referencing the thumbnail file name And sampling pixels at known overlay grid coordinates confirms overlay color presence (non-background color at those coordinates)
Export Filter: Include Only Compliant Images
Given a batch containing images with statuses Pass, Warn, and Fail When the user selects Export filter = Pass only Then only images with status = Pass are exported and all others are skipped And the export summary displays counts for exported and skipped by status When the user selects Export filter = Pass + Warn Then images with status ∈ {Pass, Warn} are exported and Fail are skipped When the user selects Export filter = All Then Pass, Warn, and Fail are exported And the batch CSV includes only the records of images that were actually exported
Violation Review UI: Resolve and Re-Evaluate
Given an image with status = Fail due to violation code MARGIN_UNDERFILL When the user opens the Review UI and adjusts the crop or extends background to address the violation Then the system recomputes compliance within ≤ 2 seconds and updates fillPercent, violations, and status in the UI When the user applies Override to Pass and enters a reason (minimum 10 characters) Then status updates to Pass, and artifacts include override fields {overriddenBy, overrideReason, overrideAt} while preserving original violations under violations.original And upon re-export, the XMP, JSON, and CSV reflect the updated status and override fields
Partner API: Retrieve Compliance Data
Given a partner authenticated via API key When they request GET /v1/compliance?jobId={jobId}&page=1&pageSize=100&status=Pass with If-None-Match Then the response is 200 with JSON payload containing pagination {page,pageSize,total} and records with fields [imageId,filename,presetId,ruleVersion,status,fillPercent,violations,generatedAt,thumbnailHref] And ETag is returned and subsequent request with a matching ETag yields 304 Not Modified And unauthorized requests (missing/invalid key) return 401; requests missing jobId return 400 And rate limiting is enforced at 600 requests/min with 429 and Retry-After on breach And p95 latency ≤ 500 ms for pageSize ≤ 100 on a dataset of up to 5,000 records
Artifact Consistency and Reproducibility
Given an exported image and its artifacts (XMP, JSON sidecar, batch CSV) Then fields status, fillPercent, ruleVersion, presetId, and violation codes are identical across all three artifacts And the JSON sidecar's SHA-256 is recorded in XMP as compliance.sidecarHash and matches the computed hash And re-exporting the same input with the same preset and ruleVersion produces byte-identical XMP, JSON sidecar, and CSV row (stable ordering and formatting) And exporting after a rules update increments ruleVersion and marks the new artifacts with the new ruleVersion while leaving previous artifacts unchanged
Accessibility & Customization Controls
"As a user with different accessibility needs, I want to customize how the overlay looks and alerts me so that I can use it comfortably and accurately."
Description

Offer accessible and customizable overlay options: colorblind-safe palettes, adjustable line thickness/opacity, haptic and audio cues for pass/warn/fail, keyboard shortcuts, and localized rule descriptions. Support metric/imperial units, per-user defaults that sync across devices, tooltips and quick tutorials, and ensure WCAG 2.1 AA contrast compliance within the UI.

Acceptance Criteria
Colorblind‑Safe Palettes with WCAG AA Contrast
Given the user opens Overlay Settings When the user opens the Color Palette menu Then at least 4 named colorblind‑safe palettes are available and selectable Given any palette is selected When the overlay renders over light and dark images Then non‑text overlay elements (grids, rings, lines) maintain ≥ 3:1 contrast and text labels maintain ≥ 4.5:1 contrast per WCAG 2.1 AA Given the user enables color‑vision simulation (Deuteranopia/Protanopia/Tritanopia) When viewing pass/warn/fail states Then the three states remain distinguishable by color and maintain their associated legends consistently
Adjustable Line Thickness and Opacity
Given the user opens Overlay Settings When adjusting Line Thickness Then the user can select 1–8 px in 1 px increments and the overlay updates within 100 ms of each change Given the user adjusts Overlay Opacity When using the opacity control Then the user can select 10%–100% in 5% increments and the overlay updates within 100 ms Given the user clicks Reset to Defaults When confirming reset Then thickness and opacity revert to system defaults immediately
Haptic and Audio Cues for Pass/Warn/Fail
Given the device supports haptics and/or audio and cues are enabled in Settings When the live fill score first enters Fail, Warn, or Pass Then a distinct cue plays for that state and does not repeat until the state changes again Given the user opens Cue Settings When adjusting audio volume or toggling haptics/audio independently Then changes apply immediately and respect system mute/vibration settings Given the device lacks haptics When cues are triggered Then only audio cues play without error
Keyboard Shortcuts and Full Keyboard Navigation
Given the user navigates with keyboard only When opening Overlay Settings Then all controls are reachable via Tab/Shift+Tab in a logical order and have a visible focus indicator with ≥ 3:1 contrast Given the overlay is active When the user presses the documented shortcuts Then the following actions occur: toggle overlay, cycle palettes, increase/decrease thickness, increase/decrease opacity, toggle metric/imperial, open help/shortcuts Given a modal or help panel is open When the user presses Esc Then the panel closes and focus returns to the invoking control
Localized Rule Descriptions and Units Toggle
Given the app language is set to a supported locale When viewing marketplace rule descriptions (Amazon/Etsy/TikTok) Then text appears in the selected language with correct locale formatting and falls back to English if unsupported Given the user toggles Units between Metric and Imperial When viewing measurements in overlays and rule text Then all values update instantly with correct conversions (cm/in) to 1 decimal place and consistent rounding Given the locale changes When reopening the overlay Then the last selected units setting is preserved
Per‑User Defaults Sync Across Devices
Given the user is signed in on Device A and Device B When the user changes overlay settings on Device A (palette, thickness, opacity, cues, units) Then the changes are saved to the cloud within 2 seconds and appear on Device B within 5 seconds of connectivity Given conflicting edits occur within 60 seconds on two devices When syncing Then the most recent change by server timestamp wins and the losing device displays a non‑blocking toast indicating sync applied Given Device B was offline When it reconnects Then it fetches and applies the latest saved defaults within 5 seconds
Tooltips and Quick Tutorial Onboarding
Given the user focuses or hovers any overlay control When the tooltip trigger condition is met (hover 300–500 ms, focus immediate) Then a tooltip appears with a concise description and any shortcut, and is dismissible on Esc or blur Given the user opens the Quick Tutorial When the tutorial begins Then it completes in ≤ 60 seconds across 3–5 steps, is navigable by keyboard, localized to the current language, and can be marked “Don’t show again” Given the user marked “Don’t show again” on one device When signing in on another device Then the preference is respected and the tutorial does not auto‑launch
Admin Rule Management Console
"As a product operations manager, I want a console to manage and safely roll out marketplace rule updates so that our overlays stay accurate as policies change."
Description

Provide an internal console for Ops/Content teams to author, test, approve, and publish rule presets. Include staged environments, scheduled releases with effective dates, diff/rollback, approval workflows, audit logs, and feature flags. Validate changes against a schema, run test suites on sample images, and broadcast update notifications to clients. Ensure safe rollouts and rapid corrections when marketplaces update policies.

Acceptance Criteria
Drafting and validating a new marketplace rule preset
Given I am an Editor in the Admin Rule Management Console When I create a new rule preset for a selected marketplace and enter aspect_ratio, margin_percent, grid_spec, tolerance, and metadata Then the preset is validated against the JSON Schema and the save action is blocked if invalid And validation errors identify fields, rejected values, and constraint messages inline When all fields are valid Then the preset can be saved as Draft with a unique immutable version_id and created_at timestamp And duplicate marketplace+version combinations are prevented And Draft presets are visible only in the Console and are not broadcast to clients
Executing rule test suite on sample images
Given a Draft preset that passes schema validation When I run the SafeZone test suite against a configured sample set of at least 50 images Then the system returns a summary including pass_rate, average_fill_score, violations_count, and runtime_seconds And annotated overlays for failing cases are downloadable as a ZIP and previewable individually And total execution time for 50 images is at or below 5 minutes at the 95th percentile And the test run is stored and linked to the preset version with a unique run_id
Multi-step approval and gated publishing
Given a Draft preset with a completed test suite in the last 24 hours and pass_rate of at least 95% When the author submits the preset for approval Then the system requires approvals from two distinct Approver-role users who are not the author And each approval must include a comment; rejections require a reason and return the preset to Draft And the Publish action is disabled until all required approvals are collected And any change to the preset after submission invalidates prior approvals and requires re-approval
Staged deployment with scheduled effective date
Given an Approved preset When I schedule a release to Production with a specific effective UTC datetime Then the preset is deployed to Staging immediately for preview and smoke tests And the scheduler prevents overlapping or conflicting releases for the same marketplace and surfaces a conflict error And scheduling is blocked unless the last test suite run is within 24 hours and passed And at the effective time the preset becomes Active in Production within 2 minutes and is stamped with effective_at And an Approver can cancel the scheduled release up to 5 minutes before the effective time
Version diffing and one-click rollback
Given two preset versions for the same marketplace When I view their diff Then field-level changes are shown as added/removed/modified with before/after values and a JSON Patch view Given a Production Active preset is causing issues When I initiate a rollback to a prior version Then at least one Approver (not the executor) must confirm the rollback And the system re-runs the test suite for the target version; if it fails, rollback is blocked unless Emergency Override is selected with a justification And upon rollback the target version is republished to Production immediately and the audit log records a link to the diff
Comprehensive immutable audit logging
Given any action (create, edit, test run, submit, approve, reject, schedule, publish, cancel, rollback, feature-flag change) When the action occurs Then an immutable audit record is stored with actor_id, role, action, marketplace, version, timestamp (UTC ISO8601), IP, request_id, before_hash, after_hash, and optional reason/comment And audit records are read-only, searchable by actor, action, marketplace, and date range, and exportable to CSV and JSON And audit records are retained for at least 24 months
Client notifications and feature-flagged rollout
Given a preset is published, scheduled, or rolled back When the change is committed to Production Then subscribed clients receive a webhook within 60 seconds including marketplace, active_version, change_type, effective_at, and a signed checksum; failed deliveries are retried up to 6 times with exponential backoff And in-app notifications and release notes are visible to Console users and emails are sent to configured contacts Given a feature flag is configured for a preset When I target a percentage rollout or specific client accounts Then only targeted clients receive the new rules while others remain on the previous version And a global kill switch can disable the active preset and revert clients to the last stable version within 2 minutes

SKU Scan

Scan a barcode/QR or enter a SKU to auto‑apply the correct preset, name files, and tag batches. Keeps mobile shoots organized, speeds capture, and ensures every export lands in the right catalog with zero manual renaming.

Requirements

Real-time Barcode/QR and Manual SKU Entry
"As a solo e-commerce seller, I want to quickly scan or type a SKU before shooting so that the correct settings and organization are applied without manual steps."
Description

Enable users to scan barcodes/QR codes via device camera or enter SKUs manually to initiate a capture session. The scanner must decode common symbologies (QR, Code 128, EAN-13, UPC-A) with on-device processing for low latency and privacy, provide haptic/audio feedback on successful reads, and support continuous scan mode for rapid multi-item shoots. Integrate with PixelLift’s mobile and web capture flows, auto-focus in low light, validate SKUs against allowed patterns, and gracefully fall back to manual entry when scans fail. The output of this step is a normalized SKU token that downstream systems (preset mapping, naming, tagging, export routing) consume. Performance target: <300ms decode on modern devices; accessibility target: WCAG-compliant prompts and keyboard-only entry on web.

Acceptance Criteria
Mobile Camera Scan with On‑Device Decoding and Feedback
Given camera permission is granted on a supported mobile device When a valid QR, Code 128, EAN‑13, or UPC‑A code is presented within 5–40 cm of the lens Then decoding is performed entirely on‑device with no outbound network calls containing image/frame data during decoding And Then the first successful decode completes in ≤300 ms at p95 across the supported device list And Then the UI confirms success with one haptic pulse and a 200–400 ms confirmation tone (respecting system mute/vibrate) And Then the decoded value is handed to the normalization step without requiring additional taps
Supported Symbologies and Unknown Code Handling
Given a scanning session is active When the user scans QR, Code 128, EAN‑13, or UPC‑A Then each symbology is decoded successfully and routed to normalization And When the user presents an unsupported or unreadable symbology Then the UI displays an "Unsupported code" message within 200 ms and no token is emitted And Then the scanner remains active for the next attempt without requiring user re‑activation
Continuous Multi‑Item Scan Session
Given Continuous Scan Mode is enabled When the user scans multiple items sequentially Then the scanner remains active after each successful read without additional taps And Then inter‑scan ready time is ≤500 ms after feedback completes And Then each successful read triggers haptic/audio feedback per item And Then duplicate reads of the same SKU within 2 seconds are ignored by default (unless the "Allow duplicates" setting is enabled) And Then a session list logs each accepted SKU with a timestamp for later export
Manual SKU Entry and Fallback from Failed Scans
Given the user experiences 3 consecutive failed decode attempts or selects "Enter manually" When the user types or pastes a value Then the input is validated against the allowed pattern ^[A-Za-z0-9._-]{3,64}$ with inline error messaging on violation And Then on submit (Enter/Done), the value is trimmed of surrounding whitespace and converted to uppercase And Then a normalized SKU token is produced identical in structure to a scanned token And Then this path functions fully offline and returns to the active capture session without page reload
Low‑Light Autofocus and Torch Assist
Given the scanner detects a low‑light condition When scanning is active Then tap‑to‑focus is available and focus confirmation is visually indicated And Then a torch/flash toggle is available on supported hardware and persists its state during the session And Then autofocus converges within ≤1000 ms in 10–30 lux test scenes And Then at 10–30 lux with torch enabled, decode p95 remains ≤300 ms on supported devices
Web Capture Accessibility and Keyboard‑Only Operation
Given the web capture page is loaded When using only a keyboard and a screen reader Then the user can start/stop scanning, switch to manual entry, enter a SKU, and submit without using a mouse And Then focus order is logical, focus is always visible, and no keyboard traps exist And Then error/success messages are announced via aria‑live within 500 ms And Then all actionable controls meet WCAG 2.2 AA (e.g., color contrast ≥ 4.5:1, role/name/state exposed) And Then shortcut keys are provided (e.g., S to start/stop scan, M for manual entry) and are remappable or documented
Normalized SKU Token Emission and Capture Flow Integration
Given a SKU is obtained via scan or manual entry When normalization completes Then a NormalizedSkuToken (uppercase, pattern‑compliant) is emitted within ≤200 ms and attached to the active capture session context And Then the token is consumed by preset mapping, file naming, batch tagging, and export routing in the same session And Then export previews and filenames reflect the token without additional manual renaming And Then the token and its source (scan/manual) are recorded in analytics logs without storing image frames
SKU-to-Preset Mapping Engine
"As a seller, I want the right preset to auto-apply from the SKU so that my photos are consistently edited for each product and marketplace."
Description

Implement a deterministic engine that resolves a scanned/entered SKU to the correct editing preset and export profile. Support exact matches, prefix/range rules, and marketplace-specific overrides. Provide fallback behavior (default preset) when no rule matches and surface warnings for ambiguous or deprecated mappings. The engine must be stateless at runtime with cached reads, backed by a versioned data store to ensure reproducibility of past edits. Expose an internal API to retrieve mapping results in <100ms and emit structured events for audit and analytics. Integrate with the processing pipeline to auto-apply the resolved preset before image enhancement begins.

Acceptance Criteria
Exact SKU Match Resolution
Given a mapping store version V_current contains an exact mapping for SKU "ABC123" to preset "P1" and export profile "E1" When the internal resolve API is called with SKU="ABC123", marketplace="generic", mappingVersion="V_current" Then the API responds 200 with payload including preset_id="P1", export_profile_id="E1", rule_id not null, mapping_source="exact", mapping_version="V_current" And no warnings are present And repeated calls with the same inputs return identical payloads
Prefix and Range Rule Resolution
Given version V_current defines a prefix rule "ABC-" -> preset "P2" export "E2" and a numeric range rule 1000-1999 -> preset "P3" export "E3" When resolve is called with SKU="ABC-789", marketplace="generic", mappingVersion="V_current" Then the response contains preset_id="P2", export_profile_id="E2", mapping_source="prefix", rule_id not null And when resolve is called with SKU="SKU-1500", marketplace="generic", mappingVersion="V_current" Then the response contains preset_id="P3", export_profile_id="E3", mapping_source="range", rule_id not null
Marketplace-Specific Override Precedence
Given version V_current defines a generic rule for SKU family "CAM-" -> preset "P4" export "E4" and an override for marketplace="amazon" -> preset "P5" export "E5" When resolve is called with SKU="CAM-001", marketplace="amazon", mappingVersion="V_current" Then the response contains preset_id="P5", export_profile_id="E5", mapping_source="marketplace_override", rule_id not null and no warnings And when resolve is called with SKU="CAM-001", marketplace="ebay", mappingVersion="V_current" Then the response contains preset_id="P4", export_profile_id="E4", mapping_source="prefix", rule_id not null
Default Fallback With Warning
Given version V_current has a configured default preset "P_DEF" and export profile "E_DEF", and SKU "UNKNOWN-42" matches no rule When resolve is called with SKU="UNKNOWN-42", marketplace="generic", mappingVersion="V_current" Then the response contains preset_id="P_DEF", export_profile_id="E_DEF", mapping_source="default", rule_id is null And a warning is present with code="no_match_default_applied" and includes the SKU And an audit event is emitted including warning code and default preset identifiers
Ambiguity Resolution and Deterministic Priority
Given version V_current contains overlapping rules: exact SKU "PRO-100" -> P6/E6, prefix "PRO-" -> P7/E7, range 100-199 -> P8/E8 When resolve is called with SKU="PRO-100", marketplace="generic", mappingVersion="V_current" Then the exact rule wins per priority [exact > marketplace_override > prefix > range > default], returning preset_id="P6", export_profile_id="E6", mapping_source="exact" And a warning is present with code="ambiguous_rule_matches" listing the losing rule_ids in evaluation order And repeated evaluations produce the same winning rule and warning contents
Versioned Reproducibility and Deprecation Handling
Given mapping store has versions V1 and V2 where rule R1 (SKU prefix "LEG-" -> P9/E9) exists in V1 and is marked deprecated in V2 with successor R2 (-> P10/E10) When resolve is called with SKU="LEG-777", marketplace="generic", mappingVersion="V1" Then the response returns preset_id="P9", export_profile_id="E9", rule_id="R1", mapping_version="V1" and no deprecation warning And when resolve is called with mappingVersion omitted (defaults to current V2) Then the response returns preset_id="P10", export_profile_id="E10", rule_id="R2", mapping_version="V2" and includes a warning code="rule_deprecated" referencing R1 if used or migrated And the emitted event includes mapping_version, rule_id, successor_rule_id (if applicable), and reproducibility_id correlating to the job
Internal API Performance, Statelessness, Caching, Events, and Pipeline Integration
Given the resolve API is deployed with read caching enabled and no session state When load test sends 10 rps for 5 minutes with warmed cache Then p95 latency is < 100ms and error rate is 0% And repeated requests with identical inputs across different connections return identical outputs, and no server-side session identifiers are created And cache hit ratio is >= 80% after warm-up, validated via metrics And each resolve emits a structured event with fields: event_type, request_id, sku, marketplace, mapping_version, rule_id, mapping_source, warnings[], duration_ms, cache_hit(boolean) And when the processing pipeline ingests a new image job with SKU present Then the pipeline invokes resolve before enhancement starts and attaches preset_id/export_profile_id to the job context, verified via trace logs precede the enhancement step
Mapping Management UI
"As a store owner, I want an easy way to manage which preset maps to each SKU so that I can keep my catalog organized as products change."
Description

Provide an admin UI to create, edit, search, and bulk-import/export SKU-to-preset rules. Features include rule types (exact, prefix, regex-safe patterns), priority ordering with drag-and-drop, validation previews (test a SKU against rules), and change history with version labels and rollback. Support role-based access control so only authorized users can modify mappings. Include CSV upload with schema validation and error reporting. Changes should publish invalidation events to refresh runtime caches without downtime.

Acceptance Criteria
Create and Validate SKU Mapping Rules (Exact, Prefix, Pattern)
- Given a user with Mappings:Write permission, When they create an Exact rule with key="SKU-123" and preset="Studio Basic v2", Then the rule is saved as Active, appears in the list, and an audit entry is recorded with user and timestamp. - Given a Prefix rule with key="ABC*", When saved, Then the system normalizes and stores it as type=Prefix, enforces key uniqueness per type, and prevents overlapping identical prefixes at the same priority. - Given a Pattern rule with key="ABC[0-9]{3}", When saved, Then it passes regex-safe validation (whitelisted tokens only) and is stored as type=Pattern; otherwise, Then field-level errors list disallowed tokens (e.g., lookaheads, backreferences). - Given a duplicate Exact key or a key > 64 characters, When saving, Then the operation is blocked with specific field errors and nothing is persisted. - Given an unknown preset reference, When saving, Then a “Preset not found” error is shown and the form remains in edit state.
Search and Filter Mappings
- Given a search query "SKU12", When the user searches, Then results include rules where key, preset name, or version label contains the query, ranked by priority, limited to 50 per page with pagination controls. - Given filters for rule type, preset, creator, and date range, When applied, Then only matching rules are shown and active filters are visibly indicated and removable. - Given no matches for the current query/filters, Then an empty state is shown with a “Clear filters” action and zero results are returned. - Given a dataset of 50,000 rules, When searching with a simple text query, Then the first page of results returns within 500 ms on the standard environment.
Priority Ordering via Drag-and-Drop
- Given two rules A and B that match the same SKU, When A is dragged above B and the change is saved, Then the preview and runtime resolution select A for that SKU. - Given a reordered list, When the page is reloaded, Then the new order persists and is reflected in subsequent exports. - Given a concurrent edit is detected (stale version), When attempting to save a reorder, Then the user is prompted to reload and cannot overwrite without reconciling. - Each reorder save creates an audit entry capturing previous and new positions and the acting user.
Validation Preview (Test SKU Against Rules)
- Given a test SKU input (e.g., "ABC123"), When the user clicks Test, Then the UI shows the first matching rule by current priority, including rule type and resolved preset. - When matches exist, Then the UI also lists all matching rules in evaluation order with their match reason (exact, prefix, pattern). - When the user edits a rule or reorders rules, Then the preview updates within 1 second without a full page reload. - Given no matching rules, Then the UI displays “No match” with an action to create a new rule prefilled with the test SKU.
Role-Based Access Control for Mappings
- Given a user with Mappings:Read, When viewing the UI, Then create/edit/delete/import/reorder/rollback controls are disabled or hidden, and any modify API calls return 403 with an audit log of the attempt. - Given a user with Mappings:Write, When using the UI, Then they can create, edit, reorder, import, and export mappings, but cannot manage user roles. - Given an Admin, When accessing History, Then they can label versions and perform rollbacks in addition to all write actions. - All modifying actions record user id, IP address, timestamp, and action details in the audit log.
CSV Import (Schema Validation and Error Reporting) and Export
- Given the published CSV template with columns [rule_type,key,preset,priority,version_label], When a file matching the schema is uploaded and validated, Then either all rows pass and the import is committed (atomic), or none are saved and a downloadable error report is generated with row numbers and messages. - Validation rules include: rule_type in {Exact,Prefix,Pattern}; key length ≤ 64; pattern must pass regex-safe whitelist; preset must exist; priority is an integer ≥ 1; no duplicate (type,key) pairs within the file or against existing active rules at the same priority. - On successful import, Then a new version is created with the provided version_label (or a system-generated one if omitted), row counts are displayed, and an audit entry is recorded. - Given an Export action, When triggered, Then a UTF-8 CSV downloads containing all current rules with columns [rule_type,key,preset,priority,version_label], with proper delimiter escaping and consistent ordering by priority.
Change History, Version Labels, Rollback, and Cache Invalidation
- Every create, edit, reorder, import, or delete action produces an immutable history entry capturing before/after diff, user, timestamp, and optional version_label. - Given selecting a past version and confirming Rollback, Then the full ruleset state is restored exactly to that version, a new history entry is created referencing the source version, and the UI reflects the restored priorities and rules. - Upon any committed change (including rollback), Then a cache invalidation event is published to the topic "mappings.invalidate" with the new version id and timestamp. - Runtime cache listeners apply the new version atomically; during refresh, requests are served without errors and see either the previous or new mapping consistently; propagation completes within 5 seconds in the standard environment.
Templated File Naming Engine
"As a seller, I want files to be named automatically from the SKU and context so that I never have to rename images manually."
Description

Create a flexible naming system that auto-generates filenames using templates tied to SKU and context. Support tokens such as {sku}, {preset}, {variant}, {timestamp}, {marketplace}, and incremental indices. Enforce character normalization, marketplace-specific constraints, and deterministic deduplication with collision-safe suffixes. Provide live preview in capture and export flows, and allow default templates per catalog with user-level overrides. Ensure idempotent naming for re-exports to prevent duplication and facilitate easy A/B variant identification.

Acceptance Criteria
Auto-Apply Catalog Template on SKU Scan
Given a user scans or enters a SKU within a catalog that has a default naming template and a preset resolved from the SKU When the first image of a new batch is captured for export Then the engine applies the catalog’s default template automatically without manual selection And tokens resolve as {sku}=scanned SKU, {preset}=resolved preset name, {marketplace}=current export marketplace, {variant}=current variant or default "A", {timestamp}=UTC formatted as YYYYMMDDTHHmmssZ, {index}=01 And the filename preview equals the final saved filename exactly And the batch is tagged with the resolved SKU and preset
Live Preview Updates in Capture and Export Flows
Given the user edits the template string or changes any token source (preset, marketplace, variant, SKU) When the change is made Then the filename preview updates within 200 ms to reflect the exact resolved name after normalization and constraints And invalid or unknown tokens are highlighted and block export until resolved And undoing the change restores the previous preview deterministically
Token Resolution and Formatting Rules
Given a template containing {sku}, {preset}, {variant}, {timestamp}, {marketplace}, and {index} When the engine resolves tokens Then {timestamp} defaults to the export time in UTC formatted as YYYYMMDDTHHmmssZ And {index} starts at 01 for the first image per SKU in a batch and increments by 1 for subsequent images of the same SKU And {index} supports zero-padding width via syntax {index:W} where W is digits (e.g., {index:3} -> 001) And {variant} defaults to "A" when not explicitly set And unknown tokens cause a validation error and prevent export
Character Normalization and Sanitization
Given raw token values may include spaces, diacritics, emojis, punctuation, or multiple separators When generating the filename Then values are normalized using NFKD, diacritics removed, and characters transliterated to ASCII And spaces and disallowed punctuation are replaced with a single hyphen And multiple hyphens or underscores collapse to a single hyphen, and leading/trailing separators are trimmed And exactly one dot precedes the file extension; the extension is preserved from the output format And if the base name becomes empty after normalization, it falls back to {sku} or "unnamed" plus {timestamp}
Marketplace-Specific Constraint Enforcement
Given the active marketplace has a configured rule set (allowed-character regex, max-length, case rules, required prefixes/suffixes) When the resolved filename violates any rule Then the engine auto-corrects to the nearest compliant name (apply case rule, strip disallowed chars, and truncate to max-length while preserving the extension) And the preview indicates that corrections were applied And the final saved filename conforms exactly to the marketplace rule set
Deterministic Deduplication with Collision-Safe Suffixes
Given multiple images in a batch resolve to the same normalized base name within the same destination When exporting the batch Then the first item receives the base name, and subsequent collisions receive suffixes "-2", "-3", … appended before the extension And suffix assignment is deterministic based on capture timestamp ascending, then original filename as tie-breaker And re-running the same batch produces the same suffix mapping And existing files in the destination are accounted for when assigning the next available suffix without gaps
Idempotent Naming for Re-exports
Given an asset that was previously exported with a template and token values persisted in metadata When the asset is re-exported using the same template and context (same SKU, preset, marketplace, variant) Then the generated filename string exactly matches the original, with no additional suffixes due to existing files And {timestamp} resolves to the original export timestamp for that asset, not the current time And changing the variant (e.g., from "A" to "B") results in distinct filenames that differ only by the {variant} token value
Batch Tagging and Session Organization
"As a high-volume shooter, I want images grouped and tagged by SKU session so that I can find and export the right sets quickly."
Description

Automatically create a scan session when a SKU is captured and apply consistent tags (SKU, catalog, preset version, shoot location/device). Group images under the session for bulk actions, searchability, and export selection. Allow adding custom tags and notes, and persist session context across multiple captures to speed high-volume shoots. Expose filters and saved views in the library to quickly find all assets for a specific SKU or campaign.

Acceptance Criteria
Auto Session Creation on First SKU Capture
- Given no active session exists for the scanned or entered SKU, When the user captures the first image after scanning/entering the SKU, Then a new session is created within 1 second, assigned a unique session_id, and the captured image is attached to that session. - Given the SKU maps to a catalog and preset, When the session is created, Then the session stores tags: sku, catalog, preset_version, shoot_location, device, created_by, created_at. - Given the SKU lacks a mapping, When the session is created, Then default catalog and preset_version="default" are applied and a non-blocking warning is logged. - Given the session is created, When reviewing audit logs, Then an entry "session_created" exists with session_id, sku, user_id, and timestamp.
Automatic Metadata Tagging for Sessions and Assets
- Given a session is active, When an image is captured, Then the asset metadata includes the session tags {sku, catalog, preset_version, shoot_location, device} with non-empty values or "unknown" placeholders. - Given the user changes the preset during the session, When subsequent images are captured, Then their preset_version reflects the new version while previous images retain the prior version. - Given an asset belongs to a session, When a user attempts to delete mandatory tags (sku, catalog, preset_version), Then the action is blocked with an error explaining that mandatory tags are immutable. - Given device location permission is denied, When images are captured, Then shoot_location is set to "unknown" and a non-blocking banner informs the user.
Session Grouping and Bulk Actions
- Given multiple images in a session, When the session is selected in Library or Capture review, Then all member assets are selected as a group and bulk actions become available: Apply/Change Preset, Add/Remove tags, Add notes, Delete, Export. - Given a bulk action is executed, When the operation completes, Then 100% of targeted assets are updated and a summary dialog shows success and any per-asset failures. - Given a session export is initiated, When no destination is manually chosen, Then the export defaults to the session's catalog tag. - Given a session export is initiated, When files are generated, Then filenames follow "SKU-{sku}_{session_id}_{seq}.jpg" with zero duplicate names in the batch. - Given bulk updates complete, When the Library is refreshed, Then updated metadata is searchable within 2 seconds.
Custom Tags and Session Notes
- Given an active session, When a user adds a custom tag key:value meeting validation (key 1-32 [a-z0-9_], value 1-64 printable, max 20 tags), Then the tag is added to the session and immediately applied to all existing and future assets in the session. - Given a duplicate custom tag key is submitted, When saving, Then the system rejects the change with a clear validation message. - Given a session has custom tags, When a tag is removed at session level, Then it is removed from all associated assets unless the user confirms preserving asset-level overrides. - Given a session note (<=2000 chars) is added, When viewing asset details or exporting metadata, Then the note is visible in session/asset views and included where supported. - Given tag/note changes occur, When viewing audit logs, Then the changes are recorded with user, timestamp, and diffs.
Persistent Session Context Across Captures
- Given a session is active for SKU X, When the user captures additional images without scanning a different SKU or ending the session, Then all images are added to the same session. - Given the app is backgrounded for up to 30 minutes, When it is reopened, Then the active session banner persists and new captures continue in the same session. - Given the user scans a different SKU, When capturing the next image, Then the prior session is ended and a new session for the new SKU is created and used. - Given the user scans the same SKU within 30 minutes of the last session activity, When prompted, Then "Resume existing session" is the default, with an option to start a new session.
Library Filters and Saved Views for SKU/Campaign Retrieval
- Given assets exist across multiple sessions and SKUs, When the user filters by SKU=X or by custom tag campaign=Y, Then the Library shows all and only assets matching those filters. - Given multiple filters are applied, When results are shown, Then the filter logic is an AND across criteria and the active filters UI exposes each applied filter with an option to clear it. - Given the user saves a view with selected filters and sort, When they load that saved view later (same account, any device), Then identical filters and sort are applied and matching results are displayed given unchanged data. - Given the library index contains up to 10,000 assets, When a filter is applied, Then results render within 1 second; for up to 100,000 assets, within 3 seconds. - Given assets are selected via a session or filters, When "Export selection" is initiated, Then the export includes exactly the displayed assets, no extras or omissions.
Export Routing by Catalog
"As a seller, I want exports to land in the right catalog automatically so that I don’t waste time moving files or fixing misfiled images."
Description

Route processed images to the correct catalog or marketplace destination based on the resolved SKU mapping. Support multiple destinations per SKU (e.g., main + thumbnail sets), destination-specific transformations, and delivery methods (folder structure, cloud bucket, or API push). Validate destination credentials, simulate routes with a dry-run preview, and ensure transactional integrity—either all files for a batch route successfully or clear, actionable errors are presented with retry options. Maintain compatibility with existing bulk export workflows.

Acceptance Criteria
Route to Storage Destinations (Folder/Cloud Bucket)
Given a resolved SKU maps to one or more storage destinations (local/network folder path and/or cloud bucket/prefix) with valid write access When a batch of N processed images is exported for that SKU Then for each destination defined in the mapping, exactly N files are delivered And if a folder destination is defined, files appear under the specified root with the configured subfolder hierarchy and naming pattern And if a cloud bucket destination is defined, objects are created under the specified bucket/prefix with the configured hierarchy and naming pattern And per-destination export logs record object counts, total bytes written, start/end timestamps, and the mapping version used
Route via Marketplace API Push
Given a resolved SKU maps to a marketplace API destination with valid endpoint, authentication, and payload schema When a batch of N processed images is exported for that SKU Then the system submits N payloads (or one payload with N assets, according to the destination’s protocol) and receives 2xx responses for all required calls And remote asset IDs/URLs returned by the API are stored and associated to the source files and SKU And the audit log captures request IDs, rate limiting events, and response codes And if any call returns a non-retriable 4xx, the batch is marked failed with an actionable error referencing the exact asset and API message
Multi-Destination per SKU with Destination-Specific Transforms
Given a SKU is mapped to multiple destinations (e.g., Main and Thumbnail) with distinct transform rules (dimensions, format, quality, color profile, watermarking) When a batch of N source images is exported Then for each source image, one variant per destination is generated according to that destination’s transform rules And the produced variants meet the exact specs (e.g., Main 2000px JPG q=90 sRGB; Thumb 400px WebP q=80) verified by automated checks on dimensions, format, ICC profile, and file size thresholds And destination-specific naming conventions (suffixes/prefixes) are applied as configured And each variant is delivered only to its mapped destination(s) and not to others
Credential Validation Preflight and Error Messaging
Given one or more destinations are configured for the routing plan When the user initiates export (or saves the routing configuration) and preflight is enabled Then the system validates connectivity, authentication, and required permissions for each destination without transferring files And destinations that fail validation are clearly flagged with the failing check (auth, permission, connectivity), the exact endpoint/bucket/path, and the returned error message/code And export start is blocked until all required destinations pass validation And the UI/API provides direct remediation guidance (e.g., re-authenticate, adjust bucket policy) and allows re-run of preflight
Dry-Run Preview of Routing Plan
Given Dry Run is selected for a prepared batch with resolved SKU mappings When the user runs the export in Dry Run mode Then no files are written and no external APIs are called And the system presents a preview including: list of destinations, per-destination file counts, example final paths/keys, per-destination transform summaries, estimated total bytes, and estimated duration And the preview can be exported as JSON for auditing/automation And running the same batch with Dry Run off produces results consistent with the previewed plan (destinations, counts, paths) barring external system changes
Transactional Integrity with Rollback and Retry
Given a batch contains N files routed to M destinations When an error occurs during transfer or API push for any file or destination Then the batch completes atomically: either all N files are successfully delivered to all intended destinations, or none of the batch’s files are left visible in final destinations And any partial artifacts created during a failed attempt are automatically cleaned up or moved to a staging/quarantine location within 120 seconds And the user receives a consolidated error report identifying files/destinations, error codes, and suggested next steps And a single-click retry is available that is idempotent (no duplicate assets on re-run) and resumes efficiently
Compatibility with Existing Bulk Export Workflows
Given a user runs an export via existing Bulk Export UI/API without SKU routing configured When the export completes Then behavior matches legacy export (destination selection, naming, folder layout) with no breaking changes to required inputs And when SKU routing is configured, the same Bulk Export entry points execute routed delivery without additional manual steps And for an identical workload, routed export throughput is within ±10% of legacy bulk export throughput And existing automation scripts calling Bulk Export endpoints continue to function without modification
Audit Trail and Activity History
"As an operator, I want a clear history of what happened for each SKU scan so that I can troubleshoot issues and prove compliance."
Description

Log end-to-end events for each scan session: who scanned, SKU, mapping result, preset version, file names generated, tags applied, exports attempted and outcomes. Provide a searchable activity view with filters by SKU, user, date, and outcome, plus CSV export for reporting. Retain logs per data retention policy and protect PII. Surface inline diagnostics to speed resolution when mappings fail or exports are misrouted, and link events to the exact configuration version used at the time.

Acceptance Criteria
End-to-End Scan Session Event Logging
- Given a user initiates a SKU Scan session, When a barcode is scanned or a SKU is entered and processing completes, Then a session record is created with: session_id, user_id, timestamp_utc (ISO 8601), sku, mapping_result (matched|missing), preset_id, preset_version, generated_file_names, applied_tags, exports_attempted, per_export outcomes (destination, status, response_code), total_processing_ms. - Given any event in the session is recorded, When a correction is needed, Then the original event is immutable and a new corrective event is appended referencing the prior event_id. - Given a session completes, When the activity view is refreshed, Then all session events are queryable and visible within 5 seconds.
Searchable Activity View With Filters
- Given the activity view is open, When the user filters by sku (exact or prefix), user, date range, and outcome (success|fail|mixed), Then the result set reflects the filters with 100% precision and no unfiltered records appear. - Given up to 10,000 matching records, When filters are applied or pagination is changed, Then the first page renders within 2 seconds and pagination controls are available. - Given no records match the filters, When the search is executed, Then an empty state "No activity found" is displayed and no rows render. - Given a record appears in results, When viewed in the table, Then columns include session_id, timestamp_utc, sku, user_id, preset_version, outcome_summary, and a link to session details.
CSV Export Of Activity Logs
- Given filters are set in the activity view, When the user selects Export CSV, Then the CSV contains exactly the filtered result set and no additional records. - Given the CSV is generated, Then it includes headers: session_id, timestamp_utc, user_id, sku, mapping_result, preset_id, preset_version, generated_file_names, applied_tags, export_destination, export_status, export_response_code, processing_ms. - Given more than 50,000 rows match, When export is requested, Then an asynchronous export starts and the user receives an in-app notification with a secure download link that expires in 24 hours. - Given the CSV is downloaded, Then timestamps are ISO 8601 UTC, values are comma-delimited with proper quoting/escaping, and the row count matches the reported total.
Data Retention Enforcement
- Given the retention policy setting is configured (e.g., 90 days), When a log record exceeds the retention period, Then it is purged within 24 hours and becomes inaccessible via UI and export. - Given the retention policy value is changed, When 24 hours elapse, Then purge jobs honor the new value for subsequent purge cycles. - Given a legal hold is applied to a session_id or sku, When purge executes, Then records under legal hold are excluded until the hold is removed. - Given a purge completes, Then a purge audit entry is recorded with timestamp_utc, policy_days, and count_purged.
PII Protection and Access Control
- Given a user without Audit.View permission attempts to access activity logs, When the request is made, Then the system returns 403 and no activity data is exposed. - Given a user without Audit.Export permission attempts to export CSV, When the export is triggered, Then the action is blocked (disabled UI or 403) and no file is generated. - Given activity data is stored, Then user-identifying fields are limited to user_id and role; display_name/email are not persisted in logs; and all activity data is encrypted at rest and in transit. - Given activity data is displayed, Then user_id is rendered as an internal identifier and no PII beyond policy is displayed.
Inline Diagnostics and Config Version Linking
- Given a mapping failure or export misrouting occurs, When the session details are opened, Then a diagnostics panel displays event_id, error_code, human-readable reason, affected file(s), and routing decision trace. - Given a session used a specific configuration, When viewing details, Then deep links to the exact preset_id and preset_version (and mapping table version, if applicable) are shown and open the read-only snapshot used at the time. - Given diagnostics are viewed, When a support case is needed, Then a copyable correlation_id is available to reproduce and trace the event in logs. - Given the root cause is resolved outside the session, When a new export attempt is triggered from the correct workflow, Then the new attempt is logged under the same session with a distinct event_id and outcome.

Angle Coach

AR ghost outlines and distance prompts tuned to category (e.g., apparel front/side/back, shoes 45°, jewelry macro). Capture consistent, conversion‑ready hero and detail shots with instant feedback on tilt, glare, and framing.

Requirements

Category AR Ghost Overlays
"As a solo e-commerce seller, I want category-specific AR outlines while shooting so that I can align products quickly and get consistent, marketplace-ready angles without manual measuring."
Description

Provide real-time AR ghost outlines and orientation markers tailored to product category (e.g., apparel front/side/back, shoes 45 degrees, jewelry macro). Supports adjustable opacity and scale, alignment anchors, horizon/tilt indicators, and category-specific pose tolerances. Overlays preload based on the selected category and cache for offline use. Works with ARKit/ARCore, with WebXR/WebView fallback where available. Maintains 30+ FPS preview on mid-tier devices. Integrates with PixelLift category metadata and preset rules to drive which overlays appear for each required shot.

Acceptance Criteria
Category-Driven Overlay Selection & Preload
Given the user selects a product category with preset rules, When the camera view opens, Then the first required overlay for that category is displayed within 300 ms. Given the category is selected and the device is online, When preloading begins, Then all required overlays for that category are downloaded and cached within 2 s on Wi‑Fi (≥25 Mbps) and within 5 s on 4G (≥10 Mbps). Given overlays for a category are cached, When the device is offline and the camera view opens, Then the first required overlay appears within 300 ms and no network request is made. Given a category has no mapped overlay, When the camera view opens, Then a default generic overlay is shown and an analytics event "overlay_mapping_missing" is logged with the category id.
Overlay Opacity, Scale, and Persistence Controls
Given an overlay is visible, When the user adjusts the opacity between 10% and 100% in 5% increments, Then the overlay alpha updates within 50 ms and the current value is displayed numerically. Given an overlay is visible, When the user adjusts the scale between 50% and 150% via slider or pinch gesture in 5% increments, Then the overlay scales about its anchor without aspect distortion and updates within 50 ms. Given the user has set opacity and scale for a category, When switching between required shots within that category, Then those settings persist; When switching to a different category, Then settings reset to that category’s defaults unless "Lock across shots" is enabled. Given the user taps Reset, When the confirmation is accepted, Then opacity and scale return to the category default values immediately.
Alignment Anchors and Tilt/Horizon Indicators
Given device IMU sensors are available and calibrated, When roll and pitch are measured, Then the horizon/tilt indicators reflect values within ±1.0° roll and ±1.5° pitch of the system-reported orientation. Given alignment anchors are shown, When product edges align within 5 px of the anchors for at least 500 ms, Then the guide state changes to "Aligned" (green) and a single haptic tick is emitted. Given roll or pitch exceed the category tolerance, When the deviation persists for >300 ms, Then a red warning outline and text prompt appear (e.g., "Reduce tilt", "Level horizon"). Given a depth sensor is available, When subject distance is between 20–120 cm, Then the distance readout accuracy is within ±2 cm; When no depth sensor is available, Then numeric distance is hidden and qualitative prompts ("Move closer/farther") are shown based on subject bounding box size.
Pose Tolerances and Real-time Feedback by Category
Given the Shoes category is selected, When the system estimates yaw/roll and distance, Then "Ready to capture" appears only when yaw is 45° ± 3°, roll is within ±2°, and subject distance is 60–90 cm sustained for ≥500 ms. Given the Apparel - Front shot is active, When the subject pose is analyzed, Then "Ready to capture" appears only when yaw is 0° ± 2°, pitch is within ±2°, and subject centroid is within ±3% of frame center for ≥500 ms. Given the Jewelry - Macro shot is active on a device with macro capability, When focus and highlights are analyzed, Then "Ready to capture" appears only when focus distance ≤ 15 cm, specular highlight area < 5% of frame, and hand jitter < 0.5° RMS over 300 ms. Given any category shot is active, When tolerances are not met, Then no more than two actionable prompts are shown at once, prioritized by largest deviation (e.g., "Rotate right 4°", "Lower glare").
Runtime Performance at 30+ FPS on Mid-tier Devices
Given a mid-tier device (e.g., A14 Bionic or Snapdragon 888 class) with 1080p preview, When up to two overlays and all indicators are enabled, Then the camera preview maintains ≥30 FPS for ≥95% of frames during a continuous 60 s session as measured by an internal frame timer. Given FPS drops below 28 FPS for >2 s, When adaptive quality is enabled, Then non-critical effects are disabled in order (shadows → parallax → animated highlights) and FPS returns to ≥30 within 2 s or a user toast suggests "Reduce effects". Given the device reports thermal throttling, When detected, Then overlay refresh rate reduces to 15 Hz while maintaining preview ≥30 FPS, and a discreet performance badge is shown.
Offline Caching and Invalidation
Given overlay packs are downloaded for a category, When the user later opens the camera offline, Then the category’s overlays load from cache with zero network requests and first overlay shows within 300 ms. Given a 100 MB cache budget, When a new overlay pack would exceed the budget, Then least-recently-used packs are evicted until total cache size ≤ 100 MB. Given PixelLift preset rules publish a higher version, When the app receives the new version metadata, Then cached overlays for affected categories are marked stale and refreshed on next online launch; stale assets are used until the refresh completes.
Platform Compatibility and Fallback
Given ARKit/ARCore is available and permissioned, When the camera starts, Then native AR mode initializes within 500 ms and uses plane/depth data for overlays and indicators. Given ARKit/ARCore is unavailable but WebXR or device orientation is available, When the camera runs in a WebView, Then a 2D overlay with gyro-based tilt indicators starts within 700 ms and category tolerances are evaluated using available sensors. Given neither AR nor gyro is available, When the camera starts, Then a static overlay with manual grid is shown and a "Limited AR mode" banner appears while capture remains enabled. Given platform capability detection runs, When the camera opens, Then capability detection completes within 200 ms and an analytics event records the chosen mode.
Pose and Distance Guidance
"As a seller, I want the app to tell me when I’m at the right angle and distance so that every shot matches guidelines and reduces reshoots."
Description

Compute and display real-time camera pose and subject distance guidance with visual, haptic, and audio prompts. Highlights when angle and distance fall within preset tolerances (e.g., angle ring turns green at +/- 3 degrees; distance within target range). Supports optional fiducial marker or reference card for scale, uses device focus and LiDAR (if available) or monocular estimation otherwise. Latency under 50 ms per frame. Tolerances and prompts are driven by category presets. Integrates with overlays to form a single guidance UI and exposes pose metrics as metadata for downstream processing.

Acceptance Criteria
Real-time angle and distance guidance within latency budget
Given category preset angleToleranceDeg = 3 and distanceRange = [dMin, dMax] And the device camera preview is running at ≥30 FPS When the user moves the camera relative to the subject Then the guidance UI updates with p95 per-frame latency ≤ 50 ms over a continuous 60 s session And the angle indicator turns green when abs(angleErrorDeg) ≤ 3 And the distance indicator turns green when estimatedDistance ∈ [dMin, dMax] And out-of-tolerance state is reflected within 1 frame of crossing the threshold
Category preset-driven tolerances and prompts
Given the preset "Shoes-45" with targetYawDeg = 45 ± 3 and distanceRange = [40 cm, 60 cm] And the preset "Apparel-Front" with targetYawDeg = 0 ± 3 and distanceRange = [120 cm, 150 cm] When the user switches between these presets Then the on-screen target angle, distance range, and ghost overlay adjust to the selected preset within 100 ms And in-range/out-of-range evaluation uses the selected preset values And the active preset ID is exposed to the UI and metadata
Multi-modal feedback controls and behavior
Given settings visual = On, haptic = On, audio = Off When the subject enters in-range for angle and distance Then the visual indicators turn green within 1 frame And a single haptic pulse is emitted within 100 ms And no audio is played When the user enables audio prompts Then audio cues are rate-limited to ≤ 1 per 2 s and respect system Do Not Disturb And haptic feedback is rate-limited to ≤ 2 pulses/s And toggling any modality takes effect within 100 ms and persists for the session
Fiducial/reference card scale calibration with sensor fallback
Given a fiducial reference card of known size (85.6 x 53.98 mm) is detected in-frame and plane-aligned When distance is estimated with the fiducial Then distance MAE ≤ 1% of ground truth in the 0.3–1.5 m range Given a device with LiDAR is available When distance is estimated using LiDAR Then distance MAE ≤ 2 cm in the 0.25–2.0 m range Given no fiducial and no LiDAR When monocular estimation is used Then median absolute percentage error ≤ 5% and p95 ≤ 10% in the 0.3–1.5 m range And the UI indicates the active modality as one of {"LiDAR","Fiducial","Monocular"} And modality fallbacks occur within 1 frame without exceeding the 50 ms latency budget
Unified guidance overlay integration
Given the guidance overlay is enabled When the user opens the capture screen Then the angle ring, distance bar, AR ghost outline, and tilt/glare/framing indicators render in a single cohesive UI without occluding the shutter or primary controls And overlay render frame time adds ≤ 5 ms per frame on mid-tier devices And visual jitter is ≤ 2 px RMS during device motion And the overlay correctly rotates with device orientation changes within 150 ms
Pose metrics metadata capture and export
Given a photo is captured while guidance is active Then the image is saved with pose metadata including: yawDeg, pitchDeg, rollDeg (1 decimal), distanceCm (1 decimal), angleErrorDeg (1 decimal), distanceInRange (bool), categoryId, modality, focusDistanceCm (1 decimal), timestampMs, deviceModel And the metadata is available via API and exported as either embedded XMP or a JSON sidecar per image And batch export includes one metadata record per image and preserves capture order And downstream processing can read the fields without schema errors
Instant feedback on tilt, glare, and framing
Given the category framing guides are visible When camera roll error exceeds 3° for > 100 ms Then a tilt correction prompt appears within 50 ms and clears within 100 ms after correction When specular highlight area exceeds 4% of the subject ROI for > 100 ms Then a glare warning appears with directional cue within 50 ms When the subject bounding box deviates > 10% from the ghost center or coverage is outside the preset range Then a framing prompt appears within 50 ms and clears within 100 ms after correction
Glare, Tilt, and Motion Alerts
"As a seller, I want instant warnings about glare, tilt, or shake so that I avoid unusable photos and save time in post."
Description

Detect glare hotspots, blown highlights, underexposure, camera tilt/roll, and motion blur in preview frames and surface actionable prompts (e.g., reduce overhead glare, rotate 10 degrees, hold steady). Combines device IMU with histogram- and specular-map analysis and a lightweight on-device model. Thresholds are configurable per category (e.g., stricter for jewelry). Provides pass/fail indicators and can block shutter until issues are within tolerance (configurable). Targets under 60 ms end-to-end feedback. Logs quality flags with each capture for audit and downstream adjustments.

Acceptance Criteria
Realtime Feedback Latency Under 60 ms
Given Angle Coach is enabled on a supported device profile and preview is running at ≥30 FPS When glare/tilt/motion/exposure analysis runs on live frames Then the 95th percentile end-to-end feedback latency from frame timestamp to UI prompt render is ≤60 ms over a 60-second session, with median ≤40 ms And instrumentation records per-frame latencies and publishes a summary with p50/p95/max at session end And no dropped-frame rate attributable to the feature exceeds 5% over baseline
Glare Hotspot Detection and Prompting
Given a scene containing specular highlights on the subject exceeding the configured glare threshold for the active category When a preview frame exhibits ≥1 hotspot that covers ≥0.5% of the subject mask and peak luminance ≥98% (8-bit scale) or specular score ≥ T_glare Then an actionable prompt to reduce glare is displayed within 1 frame (≤33 ms at 30 FPS) And on the validation set, glare detection achieves ≥85% precision and ≥80% recall at the configured threshold per category And when hotspots are below threshold for 300 ms, the glare prompt is cleared and the glare check reports Pass
Tilt and Roll Guidance
Given the device IMU reports roll or pitch deviation exceeding the active category tolerance (e.g., apparel ±4°, jewelry ±2°) When deviation persists for ≥100 ms Then a prompt appears with the magnitude and direction to rotate, where the displayed angle error is within ±2° of ground truth and updates at ≥10 Hz And when deviation returns within tolerance and remains stable for ≥300 ms, a green Pass indicator is shown And if shutter block on misalignment is enabled, the shutter is disabled while out of tolerance and re-enabled when back in tolerance
Motion Blur Alert and Optional Shutter Block
Given the blur estimator (optical-flow/MTF proxy) or IMU angular velocity exceeds the configured blur threshold for the active category When the user attempts to capture or the condition is detected during preview Then a Hold steady prompt is shown within 1 frame, and if shutter block on blur is enabled, the capture button is disabled until stability metrics are below threshold for ≥300 ms And on a test suite of panning/handshake clips, blur detection yields ≥90% true positive rate at ≤10% false positive rate
Exposure Alerts for Highlights and Underexposure
Given histogram and subject mask analysis of the preview frame When clipped highlights in the subject mask exceed 1.5% of pixels (value ≥250 on 8-bit sRGB) for ≥100 ms Then display Reduce overhead glare or Lower exposure prompt and mark highlights check Fail And when median luminance in the subject mask falls below 20% for ≥100 ms, display Increase light or Raise exposure prompt and mark exposure check Fail And when both metrics return within thresholds for ≥300 ms, clear prompts and mark Pass
Category-Specific Threshold Profiles
Given the user selects a product category (e.g., apparel, shoes, jewelry) When the category changes Then the system loads the corresponding threshold profile within ≤200 ms and applies it to subsequent analysis And jewelry thresholds are stricter than apparel by default (e.g., glare tolerance ≤50% of apparel, tilt tolerance ±2° vs ±4°, blur threshold ≤80% of apparel) And changes made via remote config are reflected without app restart and are persisted between sessions
Capture-Time Quality Flags Logged
Given a capture occurs with Angle Coach active When the shutter fires (blocked or allowed) and the frame is saved Then a quality log entry is stored and associated with the asset ID including: timestamp, device model, app version, category, thresholds used, raw metrics (glare %, tilt °, blur score, exposure stats), pass/fail per check, and feedback latency p50/p95 And 99% of captures in a 30-minute session contain a valid quality log And logs are available via local export and API within ≤2 seconds of capture
Framing Grid and Safe Zone Guides
"As a seller, I want clear framing guides and safe zones so that my product fills the frame correctly and meets marketplace rules without guesswork."
Description

Offer live framing grids, centerlines, and safe zone masks for supported aspect ratios (1:1, 4:5, 3:2, 16:9) and marketplace-specific bleed/safe areas. Show percent fill and headroom guides and warn when the subject falls below minimum fill or exceeds crop bounds. Preview automatic crop recommendations and record composition metadata (subject bounding box, rotation) for PixelLift’s enhancement pipeline. Use color-blind-safe UI and allow quick toggling between grid presets. Ensure performance does not degrade preview responsiveness.

Acceptance Criteria
Selecting Aspect Ratio Shows Live Grids and Centerlines
Given the camera preview is active and the user selects an aspect ratio (1:1, 4:5, 3:2, 16:9) When the grid overlay is enabled or the aspect ratio is changed Then the framing grid and centerlines render within 100 ms of the action And vertical and horizontal centerlines align at 50% ±1 px of the preview And grid lines align to frame edges with ≤1 px deviation at all zoom levels And the overlay remains correctly aligned after device rotation or pinch-zoom (≤1 px drift)
Marketplace Safe Zone and Bleed Mask Rendering
Given a supported marketplace preset is selected When the safe zone and bleed masks are toggled on Then safe zone and bleed boundaries match marketplace specs within ±1% of frame dimensions And safe/bleed regions are visually distinct using a color-blind-safe palette and/or patterns with contrast ≥4.5:1 against the background And a non-interactive legend labels safe and bleed with their percentages And toggling masks on/off updates the preview within 100 ms
Percent Fill and Headroom Warnings for Subject Framing
Given a subject bounding box is detected in the preview When percent fill and headroom are computed relative to the selected aspect ratio/safe zone Then percent fill is displayed with one decimal precision And a warning appears if percent fill < 65% (default) or the category-specific minimum threshold And a warning appears if headroom is outside 5%–15% of frame height (default range) And warnings clear automatically when values return within thresholds And thresholds can be overridden by category presets when available
Out-of-Bounds Detection and Auto-Crop Preview
Given the selected aspect ratio or marketplace safe area is active When the subject bounding box intersects or exceeds crop/safe bounds Then an on-screen warning is shown within 100 ms indicating the out-of-bounds condition And an auto-crop recommendation preview is offered with before/after thumbnails And applying the recommendation updates the preview crop within 150 ms without dropping the preview And dismissing the recommendation leaves the framing unchanged And the system records whether the recommendation was applied or dismissed
Quick Toggle Between Grid Presets with Persistence
Given multiple grid presets (rule-of-thirds, centerlines, diagonal, etc.) are available When the user cycles presets via the overlay toggle control or shortcut Then the selected preset switches within 75 ms and is reflected on the preview And the last-used preset persists across app restarts for the same user/device profile And toggling off removes all overlays within 50 ms And a preset picker can be opened within one interaction (tap/long-press or equivalent)
Composition Metadata Capture and Pipeline Handoff
Given overlays are active and a capture or save action is performed When the image is saved to the session or library Then composition metadata is stored: subject bounding box (x,y,w,h normalized 0–1), rotation (degrees), selected aspect ratio, marketplace preset ID, percent fill, headroom, grid preset ID, timestamp And metadata values match on-screen overlays within ±1 px or ±0.5 percentage points And the metadata is attached to the asset and made available to the PixelLift enhancement pipeline via API within 1 second of capture
Overlay Performance and Preview Responsiveness
Given grids, safe zones, and percent-fill overlays are enabled simultaneously When the user pans, tilts, or zooms the camera Then the live preview maintains ≥30 FPS on target test devices And overlay input-to-render latency is ≤50 ms And enabling/disabling any overlay does not cause a frame drop below 28 FPS And overlay memory usage stays within +50 MB of the baseline preview session
Preset Library and Compliance Rules
"As a store owner, I want presets that reflect each marketplace’s rules so that my team shoots correctly without memorizing guidelines."
Description

Centralize category and marketplace shooting presets (angles, distance ranges, aspect ratios, background requirements, reflection thresholds) in a versioned server-side library. Support tenant-level customization, A/B variants, rollout controls, and audit history. Client apps auto-sync and cache presets for offline use, with a remote kill switch and safe defaults. All Angle Coach guidance (overlays, tolerances, prompts) resolves from the active preset, ensuring updates propagate without app releases. Map presets to SKUs/categories via PixelLift taxonomy.

Acceptance Criteria
Versioned preset library with audit trail
Given a new category preset 'Shoes' is created with required fields When the admin publishes it as v1.0 Then the system stores it as an immutable version with id, semantic version, createdBy, createdAt, checksum, and it is retrievable by id and version Given an existing preset v1.0 When the admin updates fields and publishes v1.1 with a changelog Then v1.1 is created immutably, v1.0 remains accessible, and the audit log lists actor, timestamp, diff summary, and reason Given a publish attempt missing required metadata (e.g., changelog) When publishing Then the API rejects with a validation error and no new version is created
Tenant overrides with inheritance and safe defaults
Given a global 'Shoes' preset and tenant 'Acme' defines overrides for angle tolerances and background color When Acme resolves the effective preset Then overridden fields equal Acme's values and other fields inherit from the global preset Given tenant 'Beta' has no overrides for 'Shoes' When resolving Then the effective preset equals the global preset Given a field is removed in the tenant override When resolving Then the system falls back to the global value for that field
A/B variants with rollout controls and deterministic bucketing
Given tenant 'Acme' configures two preset variants A and B at 50/50 with start and end times and bucketing key 'SKU' When 10,000 resolve requests are made during rollout Then variant assignment is deterministic per SKU and within ±5% of target distribution Given the admin pauses variant B When new resolve requests arrive Then 100% receive variant A within 60 seconds of the change Given the admin archives variant B When resolving Then no clients receive B and the audit log records the action
Client auto-sync and zero-release propagation to Angle Coach
Given the server publishes preset v1.2 When a client with v1.1 online checks in Then it fetches updates, refreshes local cache, and applies v1.2 to Angle Coach overlays and tolerances within 60 seconds without requiring an app update Given multiple presets are updated When the client syncs Then only changed presets are downloaded and applied; unchanged presets remain cached Given a sync error occurs When the retry policy runs Then the client backs off exponentially and retains the last-known-good preset without crashing
Offline cache with last-known-good and TTL
Given a client has cached presets synchronized within the last 7 days When the device is offline Then Angle Coach uses the cached active preset to render guidance Given the cached preset is older than 7 days When offline Then the client falls back to a safe default preset and flags the preset as stale in local logs Given connectivity is restored When sync completes Then cached presets are refreshed and Angle Coach switches to the resolved active preset on next session or within 60 seconds
Remote kill switch and rapid fallback
Given the admin activates a kill switch for preset 'Shoes' v1.2 When clients sync Then they stop using v1.2 and revert to the configured safe default or last known non-revoked version within 5 minutes Given a global kill switch is activated When clients start Angle Coach Then all guidance uses safe defaults until the switch is cleared Given a kill switch is activated When auditing Then the system records who toggled it, when, scope, and affected tenants
Taxonomy-based preset resolution for SKUs and categories
Given SKU 'ABC123' is mapped to category 'Shoes > Boots' When resolving the active preset Then the system selects the most specific preset in precedence order: SKU override > category > global Given SKU 'XYZ999' has an unknown category When resolving Then the system applies the safe default preset and logs a mapping error for review Given a SKU belongs to multiple categories When resolving Then tie-breaking rules (e.g., explicit tenant mapping priority) are applied and the chosen preset is recorded in resolution metadata
Capture Session and Batch Sync
"As a seller, I want a guided shot list tied to my SKUs and instant sync to PixelLift so that I can capture, validate, and export images in one flow."
Description

Provide a capture session flow tied to a SKU list with a required-shot checklist (e.g., hero, side, back, detail). Enforce a quality gate (meets guidelines) based on live checks before accepting captures. Auto-name files, tag with SKU, angle, and quality metadata, and upload immediately to PixelLift for enhancement and A/B variant generation. Support progress tracking, resumable sessions, and bulk export mappings to marketplace listings. Include robust retry and offline queueing with privacy-safe handling of local media.

Acceptance Criteria
Session Creation from SKU List with Required Shots
Given a valid SKU list with per-SKU required shot types (e.g., hero, side, back, detail) is imported, When a user starts a capture session, Then the app displays a per-SKU checklist with all required shots set to Pending. Given a SKU with N required shots, When a shot is captured and passes the quality gate, Then that shot’s checklist item changes to Complete and the remaining count decrements until all N are Complete. Given a SKU with incomplete required shots, When the user attempts to mark the SKU done or close the session, Then the app blocks the action and lists the missing shots. Given a SKU is selected, When the angle is chosen, Then the required-shot label, overlay, and instructions match the SKU’s category template.
Quality Gate with Live Angle/Glare/Framing Checks
Given Angle Coach overlays are active for the current category and angle, When the live preview deviates from tolerances (tilt > ±3°, subject frame fill outside 70–90%, glare hotspots > 2% of subject area, horizon skew > 2°), Then the shutter is disabled and inline prompts indicate the corrections needed. Given all live checks pass, When the user captures the photo, Then the shot is auto-validated as Pass and can satisfy the checklist item for that angle. Given any check fails post-capture review, When validation runs, Then the shot is marked Fail and cannot be used to complete the checklist item. Given the user switches category or angle, When overlays refresh, Then updated overlays and tolerances render within 300 ms.
Auto-Naming and Metadata Tagging
Given a shot passes the quality gate, When it is saved locally, Then the filename follows {SKU}_{angleCode}_{yyyyMMdd-HHmmss}_{seq}.jpg and is unique within the session. Given the file is created, Then XMP metadata includes sku, angleCode, quality=pass, sessionId, captureTimestamp (ISO 8601), deviceId, and category; EXIF GPS is omitted. Given the upload payload is prepared, Then it includes the same metadata fields and a SHA-256 checksum of the file.
Immediate Upload and Enhancement Job Trigger
Given a shot has quality=pass and network is available, When the shot is saved, Then upload starts within 2 seconds, and the server responds 202 with a jobId that is stored with the shot record. Given the enhancement pipeline completes, When the server posts status=completed, Then the client stores references to baseline and at least 2 A/B variants linked to the SKU and angle, and the shot status updates to Enhanced. Given an upload attempt receives a transient error (HTTP 5xx or network timeout), When retry policy applies, Then it retries up to 3 times with backoff (1s, 5s, 30s) before moving the item to the offline queue.
Resumable Sessions with Progress Tracking
Given a session is in progress, When the app is closed or the device restarts, Then reopening the app restores the session, SKUs, and per-shot statuses within 2 seconds of login. Given the device is offline, When shots are captured, Then the checklist updates to Complete for passed shots and the dashboard shows their state as Awaiting Upload with counts. Given a user returns to a saved session, When they select a SKU, Then the UI shows which required shots are Complete, Pending, or Failed and allows continuing from the next Pending item.
Bulk Export Mapping to Marketplace Listings
Given marketplace templates are configured (e.g., Amazon MAIN/PT01/PT02, eBay Gallery/Additional), When enhancements for a SKU are available, Then images are auto-assigned to template slots per mapping rules and filenames are transformed per channel spec. Given the user initiates bulk export, When validation runs, Then required slots must be populated; missing or failed items are listed with SKU, slot, and reason, and export is blocked until resolved or acknowledged. Given export proceeds, When packaging completes, Then a ZIP or API payload is produced per marketplace with a manifest (CSV/JSON) including SKU, slot, filename, checksum, and variant label, and the operation result is logged.
Offline Queueing, Retry, and Privacy-Safe Local Media
Given the device has no connectivity, When shots are captured, Then files are stored encrypted at rest (AES-256) in the app sandbox, with EXIF GPS stripped and no PII in filenames, and a visible queue count increments. Given connectivity resumes, When the uploader runs, Then items retry with exponential backoff (1s, 5s, 30s, 2m, 10m) up to 10 attempts per item before marking Failed—Needs Attention with an error code. Given the server confirms receipt with checksum match, When the client records success, Then the local original and temporary derivatives are purged within 10 minutes and an audit log entry with timestamp, jobId, and checksum is stored.

Smart Sync

A resilient offline queue built for on‑the‑go work: auto‑retry on Wi‑Fi, cellular data caps, pause/resume, and conflict‑safe reprocessing. Clear per‑batch status and credit use so you can keep shooting anywhere without babysitting uploads.

Requirements

Persistent Offline Job Queue
"As a mobile seller, I want my edits and uploads to queue and persist offline so that I can keep working without losing progress when I’m out of coverage or close the app."
Description

Implement a durable, device-local job queue that persists all image uploads and processing tasks to disk, survives app restarts/crashes, and guarantees ordered execution per batch. Support job dependencies (e.g., upload before transform), priority bumping, and deduplication of identical tasks. Store compact metadata (asset IDs, variants, target marketplaces, credits forecast) with encryption at rest and configurable retention to auto-clean completed records. Provide schema versioning for safe upgrades, bounded storage limits, and safeguards against queue corruption. Expose a lightweight API for enqueue, dequeue, cancel, and requeue to integrate with PixelLift’s capture flow and export pipelines.

Acceptance Criteria
Resume After Crash with Pending Jobs
Given 50 queued jobs across 3 batches are persisted to disk And 10 jobs were In-Progress when the app crashed When the app restarts with no network connectivity Then the queue is restored from disk in <= 2 seconds And the total job count remains 50 with identical job IDs And the 10 In-Progress jobs are marked Retryable and rescheduled as the next pending tasks in their respective batches And per-batch ordering is unchanged for all jobs And a recovery event is logged with restored counts and no unhandled errors
Ordered Execution with Dependencies per Batch
Given a batch B with 5 assets each requiring Upload -> Transform -> Export with explicit dependencies When the queue processes batch B Then Transform never starts before Upload receives server acknowledgment for the same asset And Export never starts before Transform completes for the same asset And within batch B, assets execute in the original submission order for each stage And tasks from other batches do not interleave between dependent tasks of the same asset And upon any parent task failure, dependent tasks do not execute and the chain is retried only according to the configured retry policy
Priority Bumping and Scheduling
Given two batches A (priority=Normal) and B (priority=High) and one worker slot available When the user bumps batch B to Highest priority Then the scheduler selects a job from batch B for the next available slot And currently running jobs are not preempted And time from bump action to next B job start is <= 1 second when a slot is idle And scheduling order is determined by priority (Highest > High > Normal > Low) and FIFO within the same priority
Idempotent Queue API: Enqueue/Dequeue/Cancel/Requeue
Given the client calls enqueue with idempotency key K and payload for asset X, variant V, marketplace M Then the first call returns 201 Created with jobId J and subsequent calls with K return 200 OK with the same jobId J And enqueue rejects payloads missing required fields with 400 and a structured validation error And multiple enqueue calls with identical computed dedup keys result in a single execution instance with dedupCount >= 1 And dequeue returns the next eligible job according to scheduling rules and never returns the same job twice concurrently And cancel(J) transitions a Queued or Retryable job to Canceled within 200 ms and returns 409 Conflict if J is In-Progress And requeue(J) moves a Failed or Canceled job back to Queued, increments attempt count, and preserves original batch order And all API operations are durably logged with correlation IDs and are safe to invoke offline (requests persist and execute when connectivity is available)
Secure Persistent Metadata Storage
Given a queued job persisted with metadata: jobId, assetId, variant(s), target marketplace(s), dependency IDs, batchId, priority, credits forecast, createdAt, updatedAt, status When inspecting the on-device storage directly Then metadata is encrypted at rest using platform keystore-backed encryption And no plaintext assetId, variant, marketplace, or credits values are readable without decryption And the serialized record size per job is <= 2 KB on average for the specified fields And attempts to access the store without process keys fail with an authorization error
Retention and Storage Bounds Enforcement
Given retention is configured to keep completed records for 7 days or up to 10,000 records and a storage cap of 200 MB for the queue store When jobs complete Then records older than 7 days are auto-deleted during maintenance without affecting pending or running jobs And if completed records exceed 10,000, the oldest completed records are purged until under the threshold And when store size reaches 200 MB, new enqueue attempts are rejected with error code QUEUE_STORAGE_LIMIT and an explanatory message, while existing jobs continue And telemetry warnings are emitted at 80% and 95% utilization
Safe Upgrade and Corruption Recovery
Given a device with schema version N and pending jobs in the queue When the app upgrades to schema version N+1 Then a migration runs atomically with a verified backup snapshot created before changes And migration completes within 5 seconds for 10,000 records on a mid-tier device with zero job loss or duplication And if migration validation fails or corruption is detected (checksum or index mismatch), the system rolls back to the backup, enters read-only safe mode, and surfaces error QUEUE_MIGRATION_FAILED And on next launch, self-healing rebuilds indexes from logs and resumes processing after user confirmation, preserving original ordering and dependencies
Network-Aware Auto-Retry
"As a seller on the move, I want uploads to auto-retry intelligently based on connection type and limits so that my batches complete without manual babysitting or surprise data charges."
Description

Enable automatic, policy-driven retries that react to connectivity changes and server responses. Detect Wi‑Fi, cellular, and roaming states; respect user-defined cellular caps and Wi‑Fi-only preferences; and apply exponential backoff with jitter, retry budgets, and error-class handling (e.g., 429, 5xx, network timeouts). Integrate with OS background transfer services where available to continue progress when the app is backgrounded. Surface clear retry states and next-attempt times to the status layer while ensuring retries never duplicate work.

Acceptance Criteria
Wi‑Fi‑Only and Roaming Policies Enforced
Given the user preference "Wi‑Fi only" is enabled And the device is on cellular data When a queued job becomes eligible for retry Then no network request is made And the job status is WaitingForWiFi And nextAttemptAt is null Given the user preference "Block retries on roaming" is enabled And the device enters a roaming state When a queued job becomes eligible for retry Then no network request is made And the job status is BlockedOnRoaming Given the device transitions from cellular (blocked) to Wi‑Fi And at least one job is blocked solely by Wi‑Fi-only policy When connectivity changes are detected Then the blocked jobs are evaluated and enqueued within 5 seconds And their status changes to ScheduledRetry
Cellular Data Cap Compliance
Given a daily cellular cap of 200 MB (UTC day) And the user preference "Respect cellular cap" is enabled And 195 MB of cellular data has been consumed by Smart Sync today When a retry would upload 10 MB over cellular Then the retry is not attempted And the job status is BlockedDataCap And remainingCellularCapMB is 5 Given Wi‑Fi becomes available while the cap is exhausted When the same job is eligible for retry Then the retry proceeds over Wi‑Fi within 5 seconds Given the UTC day rolls over (00:00 UTC) When previously cap‑blocked jobs are reevaluated on cellular Then remainingCellularCapMB resets to the configured daily cap And eligible jobs proceed according to backoff
Exponential Backoff With Jitter
Given backoff parameters: initialDelay=2s, multiplier=2.0, maxDelay=900s, jitter=±20% And a job has attemptNumber=1 after a retryable failure When scheduling the next retry Then the delay is within [1.6s, 2.4s] And nextAttemptAt is now + computedDelay (ISO 8601 UTC) Given the same job fails again with attemptNumber=4 When scheduling the next retry Then the unjittered delay target is 16s And the actual delay is within [12.8s, 19.2s] Given the computed unjittered delay exceeds maxDelay When scheduling Then the applied delay does not exceed 900s × 1.2 (upper jitter bound) Given a job succeeds When computing future retries Then backoff state resets (attemptNumber=0)
Error‑Class Driven Retry Handling
Given an attempt receives HTTP 429 with header Retry-After: 30 When scheduling the next retry Then the nextAttemptAt is now + 30s (no exponential backoff applied) And status is ScheduledRetry Given an attempt receives an HTTP 5xx response When scheduling the next retry Then exponential backoff with jitter is applied Given an attempt times out due to network timeout or DNS failure When scheduling the next retry Then exponential backoff with jitter is applied Given an attempt receives HTTP 401, 403, 404, or 422 When evaluating retry policy Then the job is marked NonRetryableError And no further retries are scheduled (nextAttemptAt is null) Given an attempt receives HTTP 408 (Request Timeout) When scheduling Then exponential backoff with jitter is applied
Retry Budgets and Cutoff Behavior
Given per‑job retryBudget.maxAttempts=8 And a job has reached attemptNumber=8 with retryable errors When the next retry would be scheduled Then the job is marked ExhaustedRetryBudget And no further retries are scheduled (nextAttemptAt is null) Given a batch has batchRetryBudget.maxAttemptsPerHour=20 And the batch has consumed 20 retries in the current UTC hour When another job in the batch becomes eligible Then it is delayed until the next hour window opens And its status is DeferredByBatchBudget
Background Transfer Continuity
Given the device OS supports background transfer services And a job retry has started uploading When the app is sent to background and terminated by the OS Then the transfer continues via the OS service And upon next app launch the job status reflects accurate progress or completion within 3 seconds Given the device OS does not support background transfer services When the app is sent to background Then retries are paused And status is PausedByOS And retries resume within 3 seconds of app returning to foreground, honoring backoff and network policies
Status Visibility and Idempotent Retries
Given a job has a deterministic idempotencyKey = SHA256(assetHash + transformSpec + version) When a retry is performed Then the same idempotencyKey is sent with the request And the server processes the job at most once Given the server responds that the operation was already processed for this idempotencyKey (e.g., 200 AlreadyProcessed or 409 Duplicate) When handling the response Then the client marks the job as Succeeded And no additional credits are consumed Given any change to retry schedule or blocking reason occurs When updating the status layer Then status.state ∈ {WaitingForWiFi, BlockedOnRoaming, BlockedDataCap, ScheduledRetry, Retrying, PausedByOS, ExhaustedRetryBudget, NonRetryableError, Succeeded} And status.nextAttemptAt is present as ISO 8601 UTC when state=ScheduledRetry And attempt counters (attemptsUsed, attemptsRemaining) are updated within 1 second of the change
Resumable Chunked Uploads with Integrity Checks
"As a user with large image sets, I want uploads to resume from where they left off so that flaky connections don’t force me to start over and waste time and data."
Description

Support large-file and flaky-network scenarios via chunked, resumable uploads. Split assets into size-tuned chunks, compute per-chunk and whole-file checksums, and resume from the last acknowledged chunk after interruptions. Constrain concurrency to respect bandwidth limits and device resources. Verify end-to-end integrity on completion and fail fast on corruption with actionable remediation. Use TLS 1.3 in transit and coordinate with server-side session tokens for secure, resumable transfer. Fall back gracefully to single-shot uploads for small files.

Acceptance Criteria
Resume Upload After Network Interruption
Given an upload session with fileId and sessionId and a chunk size of 4 MB (configurable between 1–8 MB) And the server has acknowledged chunks 0..N When connectivity is lost and later restored Then the client queries the server for the last acknowledged chunk index for the session And the client resumes uploading from chunk N+1 without re-uploading any acknowledged chunks And any duplicate submission of an already-acknowledged chunk is treated idempotently by the server and does not overwrite data And the client resumes dispatching chunks within 2 seconds of connectivity restoration
Per-Chunk Checksum Verification and Retry
Given each chunk includes a client-computed SHA-256 checksum in headers and metadata (chunkIndex, totalChunks) When the server computes a checksum that does not match the provided value Then the server responds with a checksum-mismatch error including the chunkIndex And the client retries the failed chunk up to 3 times with exponential backoff (1s, 2s, 4s) And upon a subsequent success the server acknowledges the chunk and persists it And after 3 failed attempts the client aborts the upload and surfaces error code PL-UPLOAD-CORRUPT-CHUNK with remediation instructions to retry or re-source the file
Whole-File Integrity Verification on Completion
Given the client provides a whole-file SHA-256 in the finalize request after the last chunk is acknowledged When the server assembles the file and computes its checksum Then if the checksum matches the provided hash the server returns a success response with fileId and stored checksum And if the checksum does not match the server responds with a mismatch error and discards the assembled artifact And the client performs one automatic full re-upload attempt; if the second attempt also fails the upload is marked Failed with code PL-UPLOAD-CORRUPT-FILE
Secure Transport and Session Token Resumption
Given all upload requests are made over HTTPS using TLS 1.3 When a connection is attempted with TLS < 1.3 Then the client blocks the attempt and surfaces error code PL-SEC-TLS-REQUIRED Given a valid server-issued resumable session token is attached to each request When the token expires mid-upload Then the client refreshes the token and resumes using the same sessionId within 5 minutes without re-uploading acknowledged chunks And the server rejects unauthenticated chunk requests with 401 and does not persist partial data
Adaptive Concurrency Throttling
Given default maximum concurrent chunk uploads are Wi‑Fi=4 and Cellular=2 (configurable caps) When measured per-stream throughput falls below 512 kbps for 20 seconds or device CPU usage exceeds 80% for 10 seconds Then the client reduces concurrency by 1 down to a minimum of 1 When per-stream throughput exceeds 2 Mbps for 20 seconds and CPU usage is below 50% Then the client increases concurrency by 1 up to the network-specific cap And total memory used by the upload session stays under 100 MB at all times And if a user-defined bandwidth cap is set the client does not exceed it
Small-File Single-Shot Fallback
Given a file size less than or equal to 8 MB When initiating an upload Then the client performs a single-shot upload over TLS 1.3 instead of chunking And if the server returns 413 or the request exceeds a 30-second timeout the client switches to chunked upload with 1 MB chunks and completes the transfer And success and error events reported to the user are consistent between single-shot and chunked paths
Manual Pause/Resume with Progress Preservation
Given an in-progress chunked upload When the user taps Pause Then in-flight chunk requests are allowed to complete and no new chunks are dispatched And the client persists session state (last acknowledged chunk, chunk map, checksums) to disk within 500 ms When the user taps Resume within 7 days Then the client resumes from the last acknowledged chunk within 1 second and does not re-upload acknowledged chunks
Conflict-Safe Reprocessing & Idempotency
"As a seller updating listings, I want PixelLift to safely detect and resolve version conflicts so that reprocessing never overwrites newer changes or creates duplicates."
Description

Guarantee safe, repeatable processing by attaching idempotency keys to jobs and enforcing server-side deduplication. Track asset and variant versions; detect when remote items changed since enqueue; and apply conflict strategies (skip, create new version, or prompt) based on workspace policy. Ensure deterministic reprocessing and maintain lineage linking outputs to original assets for rollbacks. Expose clear conflict states and recovery actions in the status layer without risking duplicate charges or overwrites.

Acceptance Criteria
Idempotent Job Submission Deduplicates and Prevents Duplicate Charges
Given a job submission for asset A and variant V with parameters P and idempotency key K within the configured idempotency window And a duplicate submission arrives with the same A, V, P, and K (including across network retries or concurrent requests) When the server receives the duplicate Then the server returns 200 with the original jobId and idempotency_status = deduplicated And no new processing task is enqueued and no additional credits are deducted And audit logs show one processing task with dedupe_hit_count incremented appropriately And if a request arrives with the same K but a different payload (A, V, or P), the server returns 409 idempotency_conflict with no processing and no credit deduction
Deterministic Reprocessing Produces Identical Outputs
Given asset A at version V1 with content hash H, pipeline version PV, and processing parameters P When the job is reprocessed with the same A, V1, PV, and P (including via retry after transient failure) Then all produced output files have byte-identical hashes to the prior outputs for the same inputs And output metadata (dimensions, profiles, transformation list) matches exactly And the job record includes a deterministic_fingerprint composed of H + PV + P that matches previous runs And no additional credits are deducted for retry attempts that do not produce new work
Conflict Policy: Skip When Remote Asset Changed Since Enqueue
Given a queued job J referencing asset A at version V1 with enqueue_time T And asset A is updated to version V2 at time > T before J starts processing And the workspace conflict policy is set to skip When Smart Sync evaluates J at start of processing Then J transitions to status = conflict_skipped with conflict_reason = asset_version_changed And zero outputs are produced and zero credits are consumed And the UI/API expose recovery_action = requeue_against_latest with preserved original parameters And the batch summary increments the conflict_skipped counter by 1
Conflict Policy: Create New Version and Process Safely
Given a queued job J referencing asset A at version V1 And asset A has advanced to version V2 before J starts processing And the workspace conflict policy is set to create_new_version When Smart Sync processes J Then the system creates a new derived version V3 based on the latest remote state and applies J's parameters And lineage links outputs to source_asset_id A, source_version_id V2, enqueue_reference_version V1, and job_id J And outputs are stored under version V3 without overwriting any prior variants And exactly one credit charge is recorded for J And J completes with status = processed_with_new_version
Conflict Policy: Prompt Requires Explicit User Decision
Given a queued job J for asset A at version V1 And asset A has changed to version V2 before J starts processing And the workspace conflict policy is set to prompt When Smart Sync detects the conflict Then J transitions to status = awaiting_user_decision with options = [skip, create_new_version] And no processing is performed and no credits are deducted until a choice is made And upon user selection, J resumes within 5 seconds and follows the selected path's behavior And other items in the batch continue unaffected
Lineage Tracking and Safe Rollback of Outputs
Given any completed processing job for asset A producing outputs O under version VX When inspecting the job and asset records via API/UI Then lineage data includes source_asset_id, source_version_id, pipeline_version, parameter_hash, idempotency_key, parent_job_id, and output_hashes for O And when a rollback request is issued to a previous version Vprev Then the active variant pointer reverts to Vprev without deleting outputs from VX And rollback events are logged with actor, timestamp, and reason And no credits are deducted for rollback operations
Status Layer Exposes Clear Conflict States, Credits, and Dedupe Savings
Given a batch containing jobs across various states including deduplicated, processed, conflict_skipped, awaiting_user_decision, processed_with_new_version, failed When retrieving batch status via API and UI Then per-item status includes jobId, assetId, version_ids (enqueue_reference and processed), idempotency_status, conflict_state, recovery_actions, and credits_consumed And batch summary shows counts per state, total credits_consumed, and credits_saved_via_deduplication And status updates propagate within 5 seconds of state change And representations are consistent between API and UI
Batch Status & Credit Visibility
"As a cost-conscious user, I want clear per-batch progress and credit usage so that I can plan work, catch failures quickly, and stay within my budget."
Description

Provide a real-time dashboard that shows per-batch and per-item states (queued, uploading, processing, paused, completed, failed), ETAs, and throughput. Display credits available, forecast credits needed before enqueue, and actual credits consumed as tasks complete. Include detailed error messages with one-tap retry, an audit log of actions, and exportable summaries. Integrate with billing/quotas to block enqueue when limits are reached and offer upgrade prompts without blocking ongoing work.

Acceptance Criteria
Real-Time Batch and Item Status, ETA, and Throughput
Given I have at least one active batch with items across states queued, uploading, processing, paused, completed, failed When I open the dashboard Then I see per-batch and per-item state badges reflecting the current state names exactly as specified. Given an item's state changes in the backend When I am viewing the dashboard Then the UI reflects the new state within 5 seconds and no later than 10 seconds under normal network conditions. Given a batch has at least one in-flight item When I view its details Then I see an ETA per batch and per item that updates at least every 30 seconds and upon any state change. Given the system is processing items When I view throughput Then I see current items/min and 5-minute rolling average throughput at batch and global levels. Given a batch progresses When I view progress bars Then the percentage complete matches completed_items/total_items within a tolerance of ±1%.
Credit Forecast Before Enqueue and Limit Enforcement
Given I select N images and chosen operations When I review the batch before enqueue Then the UI displays my available credits and a forecasted credits required for the batch with per-item and total estimates. Given forecasted credits required is less than or equal to my available credits When I press Enqueue Then the batch is accepted without quota errors. Given forecasted credits required exceeds my available credits When I press Enqueue Then the enqueue is blocked, a non-destructive upgrade prompt is shown, and no partial batch is created. Given I view the forecast When I change operations or item count Then the forecast updates within 1 second to reflect the new total.
Actual Credit Consumption and Balance Updates During Processing
Given a batch is enqueued When an item completes a billable processing step Then the item's actual credits consumed are displayed and the batch total and my credit balance are updated within 5 seconds. Given an item is retried after a failure When it completes successfully Then I am not double-charged; total credits for that item reflect a single successful processing path. Given forecast and actual credits differ When processing completes Then the variance is displayed at batch level and exported summary includes both values. Given my credit balance changes due to external purchase or adjustment While the dashboard is open Then the available credits indicator updates within 10 seconds.
Error Messaging, One‑Tap Retry, and Audit Logging
Given an item fails at any stage When I view the item Then I see a human-readable error message, error code, failed step, and timestamp. Given an item is in failed state When I tap Retry Then the item is re-queued once with exponential backoff and the UI shows its new state without requiring a page refresh. Given I retry a failed item When the retry is created Then an audit log entry records user ID, action retry, item ID, previous state, timestamp, and outcome success or failure. Given a network is offline When I tap Retry Then the action is queued and executed automatically when connectivity returns, with audit entries for queue and execution.
Exportable Batch Summary and Audit Log
Given a batch exists with at least one item When I select Export Then I can export CSV and JSON files containing batch metadata, per-item states, start/end times, ETAs, throughput snapshot, error details, credits forecast and actual, and user actions. Given a batch has up to 10,000 items When I export Then the export generates within 10 seconds and the file contains one row per item in CSV and a record per item in JSON. Given I apply filters on the dashboard When I export Then the exported dataset respects the current filters and includes a header describing applied filters and export timestamp. Given a batch is still in progress When I export Then the file marks in-progress items accordingly and includes latest available data without preventing ongoing processing.
Upgrade Prompt Does Not Block Ongoing Work
Given there are batches currently uploading or processing When my account hits its credit limit Then ongoing batches continue unaffected with no pause, cancellation, or throttling due to the limit. Given I am at credit limit When I attempt to enqueue a new batch Then enqueue is blocked and an upgrade or purchase prompt is shown with options to add credits or change plan. Given I complete an upgrade or purchase from the prompt When the transaction succeeds Then my available credits update within 10 seconds and the Enqueue action becomes enabled without reloading the app.
Pause/Resume and Status Consistency with Credit Visibility
Given I have a batch in uploading or processing state When I press Pause Then all in-flight items transition to paused within 5 seconds and the batch state displays paused. Given a batch is paused When I press Resume Then items continue processing from their correct next step and states reflect uploading or processing as appropriate. Given I pause or resume a batch When the action occurs Then an audit entry is recorded with user ID, action, timestamp, and affected scope. Given a batch is paused When I view credits Then credits forecast and consumed values remain visible and do not decrement while paused.
Pause/Resume & Bandwidth Controls
"As a user on limited data, I want to pause and throttle syncing with Wi‑Fi-only and daily caps so that I control when and how bandwidth is used."
Description

Add user controls to pause/resume all syncing or specific batches, schedule sync windows, and throttle bandwidth (e.g., KB/s caps). Allow Wi‑Fi-only mode, daily cellular data caps, and automatic pause upon cap exceedance with user alerts. Persist preferences locally, apply them in background, and ensure quick, reversible actions. Reflect control states in the status UI and coordinate with retry logic and chunking to instantly honor new limits without corrupting in-flight transfers.

Acceptance Criteria
Global Pause/Resume Halts All Sync Instantly
Given active uploads across multiple batches When the user taps Global Pause Then the control gives visual feedback within 300 ms and no additional bytes are sent after 1 second and no new chunks are started And any in-flight chunk either completes within 2 seconds or is checkpointed without data corruption (checksum on resume matches pre-upload) And the status UI updates to "Paused" for all batches within 1 second When the user taps Global Resume Then all previously active batches resume from the last confirmed chunk within 2 seconds and the status UI updates to "Syncing" within 1 second
Per-Batch Pause/Resume Isolated Control
Given two batches A and B are uploading When the user pauses batch A Then batch B continues unaffected and batch A sends 0 bytes within 1 second And batch A's card shows "Paused" with its progress percent unchanged When the user resumes batch A Then upload restarts from the next chunk boundary with no duplicated bytes and checksum validation passes for the final asset
Bandwidth Throttle Enforced and Immediate
Given a bandwidth cap of 100 KB/s is set When uploads are active Then the measured average send rate over any rolling 10-second window is between 90 KB/s and 110 KB/s And the new cap takes effect for in-flight transfers within 2 seconds of change When the cap is changed to 300 KB/s Then the measured average over the next 10-second window is between 270 KB/s and 330 KB/s When the cap is set to Unlimited Then no client-side rate limiting is applied and prior caps are not reapplied automatically
Wi‑Fi‑Only Mode and Network Transition
Given Wi‑Fi‑only mode is enabled and the device is on cellular only When a sync is triggered Then no uploads start and a "Wi‑Fi required" indicator appears within 1 second When Wi‑Fi becomes available Then syncing auto‑resumes within 5 seconds without user action When both Wi‑Fi and cellular are available Then only Wi‑Fi is used for data transfer (cellular bytes remain at 0 for the session)
Daily Cellular Data Cap Auto‑Pause and Alert
Given a daily cellular cap of 50 MB is set and uploads are using cellular When cumulative cellular upload bytes reach 50 MB within the current local day Then all cellular uploads auto‑pause within 3 seconds and an alert shows "Cellular cap reached" with used/total MB And no further cellular data is sent until the user increases the cap or disables the cap And Wi‑Fi uploads may continue if Wi‑Fi is available When the local day rolls over at midnight Then the used counter resets to 0 MB and cellular syncing may auto‑resume if other conditions allow
Scheduled Sync Windows Enforcement
Given a schedule window of 22:00–06:00 is enabled When current time is outside the window Then syncing remains paused and shows "Scheduled pause" within 1 second When time enters the window and network conditions allow Then syncing auto‑starts within 5 seconds When time exits the window Then no new chunks are started after the exit moment and any in‑flight chunk completes or is checkpointed within 2 seconds without corruption
Preference Persistence and Background Enforcement
Given the user sets Wi‑Fi‑only, a 100 KB/s cap, and a schedule window When the app is force-quit and relaunched or the device reboots Then the same preferences are restored before any sync begins And background sync tasks honor these preferences while the app is in background And any change to preferences is applied to active transfers within 2 seconds and reflected in the status UI within 1 second

Direct Push

Publish finished sets from your phone to Amazon, eBay, Etsy, TikTok Shop, or cloud storage in one tap. Includes preset‑compliant crops and an export log for easy tracking—no desktop handoff required.

Requirements

Marketplace Account Linking (OAuth + Token Management)
"As an e-commerce seller, I want to securely connect my marketplace and storage accounts to PixelLift so that I can publish images directly without manual downloads or desktop steps."
Description

Implement secure OAuth and token-based integrations for Amazon SP-API, eBay, Etsy, TikTok Shop, Google Drive, and Dropbox to enable Direct Push. Provide encrypted at-rest/in-transit token storage, automatic refresh, revocation, and scoped permissions per destination. Support multiple linked accounts per channel, connection health checks, sandbox/test modes, and regional endpoints. Expose a unified connection manager in mobile settings and enforce least-privilege access. Integrate with the publishing service so destinations can be selected at push time without additional authentication steps.

Acceptance Criteria
Mobile OAuth Link for Amazon SP‑API
Given I am in Connection Manager and tap "Link Amazon", When I complete OAuth consent for my selected region, Then the app exchanges the auth code for tokens and shows the account as Connected within 5 seconds. And Then the connection record displays marketplace seller name, merchant ID, and region, without exposing raw tokens. And Then no additional login is required when initiating Direct Push to this account.
Encrypted Token Storage At Rest and In Transit
Rule: Access, refresh, and app tokens are stored encrypted at rest using AES‑256‑GCM with keys in platform KMS; keys rotate at most every 90 days. Rule: All token transport uses TLS 1.2+ with certificate pinning enabled in the mobile client. Rule: Raw tokens are never rendered in UI or logs; masked format ****…#### is used in audit records. Test: Attempts to read tokens without KMS context return access denied and are audit‑logged with user/device ID and timestamp.
Automatic Token Refresh and Retry
Given an access token is expired, When a Direct Push request is made, Then the system refreshes the token using the refresh token and retries the request transparently, succeeding without user input. Given a refresh returns invalid_grant, When the next API call occurs, Then the connection status changes to Action Required, the user receives an in‑app prompt to relink, and publishing to that destination is blocked until resolved. Rule: Refresh attempts are retried up to 3 times with exponential backoff (0.5s, 2s, 5s) and are logged with correlation IDs.
Revocation and Unlinking
Given I tap Disconnect on a linked account, When I confirm, Then the app calls the provider revocation endpoint, purges all stored tokens and metadata within 5 seconds, and records an audit entry. Then the destination disappears from Direct Push selection and queued jobs targeting it are canceled with a user‑visible reason "Destination disconnected". Given a revoked webhook/event arrives from that provider, When processed, Then it is rejected and the connection remains in Disconnected state.
Multiple Accounts per Channel and Selection at Push Time
Given I have multiple accounts linked for the same marketplace, When I start Direct Push, Then I can select one or more accounts from a list labeled with account alias and region without any additional authentication. Then the last‑used account selection is preselected for that channel and persisted per project. Rule: Up to 20 accounts per channel are supported; the selection list loads within 1 second on a typical 4G connection.
Scoped Permissions and Least‑Privilege Access
Given I initiate linking for a destination, When the consent screen is displayed, Then only the minimum scopes required for Direct Push are requested (e.g., listing image upload/write; Drive/Dropbox limited to app folder/file content). Given an operation outside granted scope is attempted, When executed, Then it is blocked and an error "Insufficient scope" is surfaced without attempting to escalate permissions. Rule: Any scope change requires a new app config rollout and user re‑consent; silent scope expansion is not permitted.
Connection Health Checks, Sandbox Mode, and Regional Endpoints
Rule: A background health check runs every 24 hours and on demand; success sets status Connected, failures set Degraded with provider‑specific reason codes. Given Sandbox mode is toggled on for a connection, When publishing, Then all API calls use sandbox/test endpoints and the UI clearly indicates "Sandbox" on the destination chip. Given a region is selected during linking, When subsequent API calls are made, Then the provider base URL matches the region and median health‑check latency is under 800 ms for that region.
Preset-Compliant Export Profiles
"As a seller, I want channel-specific export presets to be automatically applied so that my images always meet each marketplace’s requirements and avoid rejections."
Description

Maintain up-to-date, versioned presets encoding each channel’s image requirements (dimensions, aspect ratios, background rules, file size limits, color profile, file formats, compression levels, naming conventions, and EXIF stripping). Automatically apply the selected preset during Direct Push and run a preflight validator that flags non-compliant assets with fix suggestions (e.g., auto-crop to aspect, adjust canvas, convert to sRGB JPEG/PNG/WebP). Allow per-destination defaults and per-asset overrides, and propagate preset updates centrally without app releases.

Acceptance Criteria
Central Preset Versioning and Propagation
Given a new preset version X.Y for a destination is published centrally When a mobile user opens Direct Push or refreshes presets Then the device fetches preset version X.Y without requiring an app update Given the device has cached version X.(Y-1) and network connectivity is available When the presets service announces X.Y Then the cache updates within 60 seconds and marks older versions deprecated Given multiple destinations are configured When presets are fetched Then each destination displays the current preset version and a change summary
Preflight Compliance Validation and Auto-Fix
Given a batch contains assets violating required aspect ratio or dimensions When preflight runs with Auto-Fix enabled Then each asset is auto-cropped or canvas-extended to meet the preset's aspect ratio and target dimensions Given assets have non-sRGB color profiles or disallowed formats When preflight runs with Auto-Fix enabled Then each asset is converted to sRGB and to an allowed format (JPEG/PNG/WebP) per preset Given assets exceed the preset's max file size When preflight runs with Auto-Fix enabled Then compression is adjusted to produce a file size at or below the limit without changing required dimensions Given assets contain EXIF/metadata and the preset requires stripping When preflight runs Then all EXIF is removed from the exported files Given Auto-Fix is disabled When preflight runs Then noncompliant assets are listed with specific fix suggestions and are blocked from push Given an issue is not auto-fixable (e.g., source is below minimum resolution) When preflight runs Then the asset is flagged as Non-Fixable with the reason and is excluded from push
Per-Destination Defaults and Per-Asset Overrides
Given a user sets a default preset for Amazon to version A.B When they initiate Direct Push to Amazon without manual selection Then preset A.B is applied automatically Given a user applies a per-asset preset override When the batch is exported Then the override is used for that asset only and the destination default applies to others Given destination defaults are updated by the user When subsequent pushes occur Then the new defaults persist and are applied until changed again Given an export completes When the export log is generated Then the log records the preset version used per asset and whether an override was applied
Direct Push Enforcement and Export Logging
Given preflight passes or all fixable issues are auto-fixed When the user taps Push to a destination Then only compliant assets are uploaded and any blocked assets are skipped with reasons Given an export job starts When it completes or fails per asset Then the export log records job ID, timestamp, destination, preset version, auto-fixes applied, final dimensions, format, file size, and outcome per asset Given a remote API failure occurs for an asset When the user retries Then a new attempt entry is appended to the log, linked to the original asset with the same preset details
Preset Field Completeness and Validation Coverage
Given a channel preset is created or edited Then it must include dimensions, aspect ratio range, background rules, max file size, color profile, allowed formats, compression levels, naming convention template, and EXIF handling before it can be published Given a preset is missing any required field When an admin attempts to publish it Then publishing is blocked and the missing fields are listed Given preflight runs against a batch with a selected preset When validation completes Then pass/fail results are produced per asset for each rule area (dimensions/aspect/background/file size/color profile/format/compression/naming/EXIF)
Backward Compatibility for Queued Jobs and Offline Cache
Given a batch is queued with preset version v2.1 When a newer preset v2.2 is published before the push starts Then the queued batch uses v2.1 unless the user explicitly opts to update to v2.2 Given the user opts to update the queued batch to v2.2 When preflight reruns Then differences between v2.1 and v2.2 are shown and the user confirms before pushing Given the device is offline with a cached preset version When the user pushes Then the cached preset is used and the export log notes the cached version and last fetch timestamp; the app refreshes to the latest version automatically upon reconnection
One-Tap Bulk Publish (Mobile)
"As a seller on mobile, I want to select finished image sets and publish them to multiple channels in one tap so that I can update listings quickly while on the go."
Description

Provide a mobile-first flow to select finished image sets, choose one or more destinations, map to listing IDs/SKUs, select or confirm presets, and publish with a single confirmation. Support A/B variation selection, per-channel metadata (e.g., main vs. gallery image), and an asynchronous queue with progress indicators. Integrate with system share sheets and deep links for quick access from notifications or completion screens. Enforce confirmation safeguards (e.g., overwrite warnings) and accessibility standards.

Acceptance Criteria
Multi-Destination One‑Tap Publish (Mobile)
Given I am authenticated on a mobile device and have at least one finished image set And I have connected at least one destination (Amazon, eBay, Etsy, TikTok Shop, or cloud storage) When I select 1–500 finished image sets and 1–5 destinations And I review the summary and tap Publish once Then a single confirmation sheet displays total listings, destinations selected, estimated image count, and preset summaries And any detected overwrite conditions are highlighted with counts per destination And when I confirm, publish jobs for all selected sets and destinations are enqueued within 2 seconds and visible in the queue screen And no additional confirmations are shown unless new overwrite risks are detected And the publish action is idempotent for identical payloads within a 10‑minute window to prevent duplicate publishes
Listing ID/SKU Mapping and Validation
Given I have selected one or more destinations that require listing ID/SKU mapping When I map each image set to a listing ID/SKU for each selected destination Then the mapping UI enforces destination‑specific format rules (e.g., required/optional fields, pattern/length) And inline validation occurs within 200 ms on entry and on blur And missing or invalid mappings are flagged and block the Publish action until resolved And I can scan a barcode or paste multiple IDs to populate mappings in bulk And previously used valid mappings are suggested and can be applied per destination And successful mappings persist for reuse and are scoped to my account and destination
Preset Compliance and Marketplace Crops
Given each selected destination has a preset applied or confirmed When I proceed to publish Then all images are validated against preset‑defined constraints for that destination (dimensions, aspect ratio, background, file type, size) And non‑compliant items are listed with the failing rule and a one‑tap Fix or Remove Destination option And publish is blocked until 100% of items for a destination pass validation or the destination is deselected And destination‑specific crops/variants are generated as required by the preset before upload And the final export set shows counts per destination and per variant prior to confirmation
A/B Variations and Per‑Channel Roles Assignment
Given I have a finished image set with multiple variations available And I have selected one or more destinations When I choose A/B (or A/B/C…) variations to include for a listing Then each variation is tagged with a stable variant label (A, B, C, …) and version metadata And I can assign per‑channel roles (e.g., Main, Gallery position 1..N) for each destination independently And destinations that require exactly one Main image enforce that constraint before publish And destinations with gallery limits enforce maximum counts and preserve the specified order And the publish payload includes variant labels and role metadata per destination
Asynchronous Queue, Progress, and Failure Handling
Given I have confirmed publish When jobs are created Then each destination/listing pair appears as a queue item with states: Queued, Uploading, Processing, Published, Failed And per‑item progress is displayed as a percentage and item counts (e.g., 12/50) And progress updates at least once per second while active And uploads continue in background for up to 10 minutes when the app is minimized, subject to OS limits And transient failures are retried up to 3 times with exponential backoff (e.g., 2s, 8s, 30s) And irreversible failures surface an error reason and a Retry action once the cause is resolved And partial failures do not block other destinations; completion notifications summarize per‑destination outcomes And I can Cancel an in‑flight item, which halts remaining uploads and marks the item as Canceled
System Share Sheet and Deep Link Entry
Given I invoke the OS share sheet from PixelLift or receive a PixelLift deep link from a notification/completion screen When I select Publish via PixelLift from the share sheet or open the deep link Then the app opens directly to the One‑Tap Bulk Publish screen with the referenced finished sets preselected And my last‑used destinations and presets are prefilled (if still valid) with an option to change And if I am not authenticated, I am prompted to sign in and then returned to the prefilled publish screen And the confirmation sheet displays immediately upon tapping Publish with all prefilled context intact
Accessibility: Mobile Publish Flow
Given I use the publish flow with screen reader or assistive technologies enabled When I navigate through destinations, mappings, presets, confirmation, and queue Then all actionable elements have accessible labels, roles, and hints And focus order follows visual order with no traps And progress and state changes are announced via live regions (e.g., VoiceOver/TalkBack) including percentage updates And touch targets are at least 44x44 dp and support Dynamic Type without truncating critical information And color contrast for text and essential icons is at least 4.5:1, with non‑color cues provided for status And all confirmations and warnings are reachable and operable via keyboard/assistive navigation
Export Log and Delivery Receipts
"As a seller, I want a detailed export log with statuses and receipts so that I can verify what was published where and troubleshoot any issues."
Description

Create an append-only audit trail capturing who published what, when, and where, including asset IDs, file hashes, preset version, destination account, request/response metadata, marketplace listing/item IDs, and per-asset delivery status. Provide filters (date, channel, user, SKU), full-text search, CSV export, and per-run summaries. Surface deep links to listings where available and expose webhooks for external reconciliation or BI pipelines.

Acceptance Criteria
Append-Only Export Log for Direct Push Runs
Given a user publishes a set via Direct Push When the run is initiated Then a run.start record is appended containing run_id, user_id, channel, destination_account_id, preset_version, started_at (UTC ISO 8601), and asset_ids Given any subsequent run or asset event When it is recorded Then it is stored as a new immutable append-only record with an incrementing sequence number and no API/UI capability exists to update or delete it (attempts return 405) Given a correction or retry is required When it occurs Then a new record is appended referencing prior_event_id without modifying previous records Given audit verification over a 24h window When update or delete operations are attempted via the public API Then 100% of attempts are rejected and the log contents remain unchanged
Required Per-Asset Metadata Capture
Given an asset is submitted to a destination When the asset.attempted event is logged Then the record includes asset_id, sku, file_name, file_size_bytes, file_hash_sha256, preset_version, channel, destination_account_id, request_id, request_timestamp (UTC), and request_payload_hash Given a destination responds When the asset.delivered or asset.failed event is logged Then the record includes response_status_code, response_latency_ms, response_request_id (if provided), response_error_code and response_error_message (if failed) Given the destination returns listing/item identifiers asynchronously When they are received Then an asset.linked event is appended containing listing_id and/or item_id and references the prior asset event via prior_event_id Given the file hash is computed When compared to the source file Then the stored file_hash_sha256 equals the SHA-256 of the exact exported bytes
Per-Asset Delivery Status and Retries
Given an asset is queued for delivery When the run executes Then the asset status transitions are logged as events in {pending -> attempted -> delivered|failed} with retry_attempt starting at 0 Given an asset delivery fails with a retriable error When an automatic retry occurs Then a new asset.attempted event is appended with retry_attempt incremented by 1 and the previous events remain unchanged Given retries exhaust the configured limit (default 3) without success When the run completes Then a terminal asset.failed event exists with error_code and error_message and no further attempts are made Given at least one delivery attempt succeeds When the run completes Then a terminal asset.delivered event exists with response_status_code in the 2xx range and delivery_status=delivered
Log Filtering, Pagination, and Full-Text Search
Given a user applies filters When date range (UTC, inclusive), channel (multi-select), user, and SKU are provided Then the API returns only matching records Given a query is executed When a full-text search term (<=64 chars) is provided Then results are matched case-insensitively across asset_id, sku, file_name, listing_id, item_id, request_id, and error_message fields Given results are returned When pagination parameters are provided Then responses are sorted by created_at desc, default page_size=50, max page_size=500, and include total_count Given the dataset contains up to 100k records matching the filters When a request is made Then the API responds in <= 2 seconds at the 95th percentile
CSV Export and Per-Run Summary
Given a user clicks Export CSV on a filtered view When the export completes Then a CSV (RFC 4180) is generated with UTC timestamps and columns: run_id, event_type, created_at, user_id, channel, destination_account_id, sku, asset_id, file_name, file_hash_sha256, preset_version, status, response_status_code, listing_id, item_id, error_code, error_message, retry_attempt Given fields contain commas, quotes, or newlines When the CSV is generated Then values are properly quoted and escaped and the file name follows log-YYYYMMDDTHHMMSSZ.csv Given a run_id is opened When the summary endpoint/view is requested Then it displays assets_total, delivered_count, failed_count, pending_count, retries_count, started_at, completed_at, duration_ms, destinations, and per-destination breakdown Given the CSV export exceeds 50k rows When generated Then it is produced via an async job and a downloadable link is available within 5 minutes
Deep Links to Listings and Cloud Objects
Given a marketplace listing/item ID is known for an asset When the log entry is viewed Then a channel-specific deep link is shown and clicking opens the listing in the marketplace seller portal in a new tab Given a deep link cannot be constructed for the channel or the ID is missing When the log entry is viewed Then the link column shows Link unavailable and no broken links are rendered Given a cloud storage destination is used When delivery succeeds Then the log shows a deep link to the object as a signed URL with expiry >= 24 hours from generation
Webhook Delivery for Export Events
Given a webhook subscription exists When events occur Then the system emits run.started, asset.attempted, asset.delivered, asset.failed, and run.completed payloads within 30 seconds (p95) of occurrence Given a webhook is delivered When the receiver validates the signature Then X-PixelLift-Signature (HMAC-SHA256 over timestamp + body with shared secret) and X-PixelLift-Timestamp headers are present and valid within a 5-minute tolerance Given the receiver returns a non-2xx status When delivery is attempted Then retries are performed with exponential backoff for up to 8 attempts and the same idempotent event_id is sent on each attempt Given the receiver acknowledges with 2xx When delivery completes Then the event is marked delivered in the export log with delivery_status=delivered and response_status_code recorded
Background Uploads, Resume, and Rate-Limit Control
"As a seller, I want uploads to continue reliably in the background with automatic retries so that my pushes complete even with poor connectivity or API throttling."
Description

Implement a resilient background job system that persists queued publishes on-device and server-side, supports resumable/multipart uploads, enforces per-API concurrency and quota policies, and applies exponential backoff with jitter. Ensure uploads survive app suspends, network changes, and partial failures via idempotency keys and granular retry. Provide offline queuing with automatic start when connectivity returns and per-destination throughput tuning.

Acceptance Criteria
Upload Persists Through App Suspend and Relaunch
Given a publish job with at least one asset actively uploading And the app is backgrounded or terminated by the OS When the app is relaunched within 24 hours Then the job state is restored within 3 seconds And uploads resume from the last confirmed byte/chunk without restarting completed parts And no duplicate listings or artifacts are created at the destination And the final checksum of each asset matches the source
Resumable Multipart Across Network Loss/Change
Given a multipart/resumable upload in progress to a supported destination And device connectivity is lost for up to 30 minutes and later restored on a different network When connectivity returns Then the upload resumes without re-uploading completed parts And redundant re-uploaded bytes are less than or equal to 5% of the total asset size And the upload completes successfully with integrity verified (e.g., ETag/MD5 match)
Per-API Concurrency and Quota Enforcement
Given configured concurrency limits: Amazon=2, eBay=3, Etsy=1, TikTok=2, Cloud=5 And configured quotas (requests/sec): Amazon=10, eBay=5, Etsy=2, TikTok=4, Cloud=20 And a queue containing at least 10 jobs per destination When workers are executing Then in-flight requests per destination never exceed the configured concurrency limit And observed request rate per destination over any rolling 60-second window does not exceed its configured quota And HTTP 429/Throttling responses are less than or equal to 1% of total requests during the test run
Exponential Backoff With Full Jitter
Given a transient error (HTTP 429, 408, 5xx, or network timeout) occurs on a request or part upload When retries are scheduled Then retry delay for attempt k is sampled uniformly from [0s, min(60s, 2^k s)] And each part is retried at most 7 times before surfacing a failure And cumulative retry wait per asset does not exceed 10 minutes unless progress (bytes confirmed) increased since the previous attempt And on final failure the job records error codes, attempt count, and exposes a manual retry action
Idempotent Retries Prevent Duplicates
Given an asset publish request is retried due to client crash, timeout, or duplicate enqueue And the same idempotency key derived from (asset_id + destination + variant + checksum) is reused for 24 hours When duplicate create/upload requests reach the server or destination API Then at most one listing or remote artifact is created for that asset and destination And subsequent attempts return idempotent success without creating additional listings And destination listing/inventory count for that asset increases by exactly 1
Offline Queue and Auto-Start on Connectivity
Given the device is offline when the user initiates Publish When the user confirms destinations Then jobs are enqueued locally within 1 second and marked "Waiting for Connectivity" And queued jobs persist for at least 72 hours without data loss And when connectivity returns, queued jobs auto-start within 5 seconds without user action And a user-initiated cancel removes the job from the queue within 1 second
Per-Destination Throughput Tuning
Given per-destination throughput settings are configured: Amazon=2 concurrent & 2 MB/s cap, eBay=3 concurrent & 1 MB/s cap, Etsy=1 concurrent, TikTok=2 concurrent, Cloud=5 concurrent & 5 MB/s cap When a mixed batch runs for at least 2 minutes Then measured 1-minute average throughput per destination is within ±15% of its configured bandwidth cap (where set) And no destination exceeds its configured concurrent worker limit at any time And a global bandwidth cap (when enabled) is not exceeded over any rolling 30-second window
Actionable Error Handling and Notifications
"As a seller, I want clear, actionable error messages and notifications so that I can fix problems fast and republish without guesswork."
Description

Normalize API errors into human-readable messages with clear remediation (e.g., re-authenticate account, reduce file size, adjust crop) and one-tap actions to fix and requeue. Distinguish transient vs. permanent failures, support partial success flows, and provide push/email/in-app notifications for blocked publishes. Include error analytics dashboards to surface recurring issues by channel and preset, feeding back into preset updates and reliability improvements.

Acceptance Criteria
Human-Readable Error Normalization
- Given a Direct Push fails on any supported destination, When the system parses the raw API error, Then a normalized message is shown within 2 seconds (p95) including: concise title (≤60 chars), description (≤240 chars), channel reference/code, severity, and a recommended fix. - Given historical incident data, When mapping coverage is evaluated, Then ≥95% of incident volume across the top 100 error codes per destination is normalized to the PixelLift taxonomy. - Given a normalized message is rendered, When support needs deeper context, Then a link to the raw error payload and request ID is available from the UI and stored for ≥90 days.
One-Tap Remediation and Requeue
- Given a normalized error has a known remediation, When the user taps the recommended action, Then the app opens the correct mobile flow (e.g., OAuth re-auth, image resize/compress, crop/preset adjustment, listing attribute fix) with context pre-filled. - Given remediation completes, When the user confirms, Then only the failed items/destinations are requeued in one tap, prior edits/metadata are preserved, and a new job ID is linked to the original. - Given an error without viable remediation, When surfaced, Then a "Contact Support" action and a knowledge-base article link are provided. - Given a requeued attempt, When it is submitted, Then the previous failure is closed with a reason and both attempts are visible in the export log timeline.
Transient vs Permanent Classification and Retry Policy
- Given an error occurs, When it is categorized as transient (e.g., HTTP 429 with Retry-After, 5xx, timeouts, network failures), Then the system auto-retries up to 3 times with exponential backoff (2s, 10s, 30s) and honors Retry-After. - Given an error occurs, When it is categorized as permanent (e.g., 4xx validation/content policy), Then auto-retry is skipped and the job is marked Blocked with remediation actions shown. - Given auto-retry is in progress, When the user views the job, Then a visible countdown and cancel control are present; on final failure, a Blocked state is set and notifications are triggered. - Given an override is needed, When a user forces requeue after addressing root cause, Then the classification is recomputed and retries reset.
Partial Success Handling for Multi-Channel/Bulk Publish
- Given a publish set targets multiple destinations or many items, When some succeed and others fail, Then successful publishes remain committed and visible; only failed destinations/items are selectable for requeue. - Given mixed outcomes, When viewing results, Then per-destination and per-item status counts, filters, and details are available without rolling back successes. - Given requeue is initiated, When idempotency is evaluated, Then destination item identifiers are used to avoid duplicate publishes on previously successful items.
Blocked Publish Notifications (Push/Email/In-App)
- Given a job transitions to Blocked after final retry, When notifications are enabled, Then a push notification is sent immediately and an email summary is sent within 10 minutes; in-app inbox updates in real time. - Given a notification is delivered, When opened, Then it deep-links to the remediation screen for the specific job and preselects failed destinations/items. - Given user preferences, When multiple failures occur, Then notifications are rate-limited to ≤2 push per 30 minutes per user and respect channel-level opt-in/opt-out. - Given push delivery fails, When a token is invalid, Then email is used as a fallback and the token health is flagged.
Export Log with Error Context
- Given any publish attempt completes, When the export log is viewed, Then each entry includes timestamp, destination, items attempted, success/failure counts, top error categories, and links to normalized messages and raw error payloads. - Given operational needs, When filtering is applied, Then the log supports time range, channel, preset, and error category filters, and supports CSV export. - Given a job completes, When log latency is measured, Then entries are written within 60 seconds (p95) and retained for ≥90 days.
Error Analytics Dashboard and Preset Feedback Loop
- Given error events stream in, When the analytics dashboard is opened, Then users can view trends by channel, preset, and error category over 7/30/90 days, with top recurring issues and drill-down to sample jobs. - Given data processing SLAs, When freshness is measured, Then error analytics have <5 minutes latency (p95) and capture ≥99% of failure events without PII exposure. - Given a preset exhibits recurring errors (e.g., ≥20 failures of the same category or >10% failure rate over 7 days), When thresholds are exceeded, Then the preset is flagged with a suggested remediation (e.g., crop ratio/resolution change) and one-tap actions to "Update Preset" or "Create Variant" are offered, with impact tracked post-change.

QuickSets

One‑tap pack builder that generates vertical, square, and main‑image variants per channel with consistent naming and folders. Perfect for TikTok Shop and Etsy, ready for instant listing or lightweight A/B checks.

Requirements

One-Tap Variant Pack Builder
"As a solo e-commerce seller, I want to generate vertical, square, and main-image variants for each channel in one tap so that I can list products faster with consistent, pro-quality visuals."
Description

Trigger creation of a complete image pack per SKU in a single action, producing vertical (9:16), square (1:1), and channel-specific main-image variants. The builder orchestrates PixelLift’s enhancement pipeline (background removal, edge cleanup, exposure and color normalization, shadow/reflection, safe margins) and applies variant-specific specs derived from the selected channel preset. It supports batch input, generates deterministic outputs with stable run IDs, and writes a per-pack manifest capturing all settings and assets. The UI exposes a single "Create QuickSet" control with a count preview, time estimate, and safeguards that flag low-resolution or non-isolated subjects with recommended fixes before processing.

Acceptance Criteria
One-Tap QuickSet Creation for Single SKU
Given a SKU with at least one eligible source image and a selected channel preset When the user clicks "Create QuickSet" Then the system generates exactly three variant types: vertical (9:16), square (1:1), and channel-specific main-image for that SKU And each variant is processed through the enhancement pipeline in order (background removal → edge cleanup → exposure/color normalization → shadow/reflection → safe margins) as recorded in the manifest And the resulting assets are saved without error and listed under the SKU’s latest QuickSet And the job status for the SKU is marked as Success in the UI and manifest
Batch Processing of Multiple SKUs
Given a selection of N SKUs and a single chosen channel preset When the user clicks "Create QuickSet" Then N independent packs are queued and processed without cross-contamination of assets or settings And the batch progress UI shows per-SKU statuses and an aggregate progress bar And failure of one SKU does not block others from completing And the UI supports retry for failed SKUs without reprocessing successful ones
Deterministic Outputs and Stable Run IDs
Given identical inputs (same source assets, same preset version, same enhancement parameters) When "Create QuickSet" is executed multiple times Then the runId for each SKU is identical across runs And each output file is byte-for-byte identical and has the same checksum and path And any change to inputs, preset version, or parameters yields a new runId and differing checksums
Channel Preset Compliance and Variant Specs
Given a selected channel preset defining geometry, format, color profile, background, and margin rules When a QuickSet is generated Then the vertical variant is exactly 9:16 and square variant exactly 1:1 at the pixel dimensions specified by the preset And the channel main-image matches the preset’s exact pixel dimensions, format, and background requirements And safe margins match preset rules (subject bounding box within allowed area) as verified in the manifest And exported files embed the required color profile and meet file size/format constraints of the preset
Per-Pack Manifest Generation and Contents
Given a completed QuickSet run When the manifest is written Then it is stored alongside the pack and includes: runId, timestamp, SKU, user/account, channel preset id and version, pipeline step order with parameters, input asset ids and checksums, output asset paths and checksums, preflight results and warnings, timing metrics, and job status per variant And the manifest is valid JSON and passes schema validation And each output file listed in the manifest exists and its checksum matches the recorded value
Preflight Safeguards and Recommended Fixes
Given source images for a SKU When the user initiates "Create QuickSet" Then the system runs preflight checks for low-resolution, non-isolated subjects, and other preset-defined risks before processing And any detected issue is surfaced with a severity tag and recommended fix (e.g., upscale, re-crop, re-shoot) and an option to proceed or cancel And if the user proceeds despite warnings, the risks and decision are recorded in the manifest and displayed in the UI
Consistent Naming and Folder Structure
Given a SKU and a generated QuickSet When assets are saved Then the pack root follows the structure: /{SKU}/QuickSet_{runId}/ And files follow the pattern: {SKU}_{variantType}_{channel}_{runId}.{ext} with variantType in {vertical,square,main} And a channel subfolder is created when the preset requires per-channel segregation And no filename collisions occur; existing identical files (same checksum) are not duplicated and are referenced in the manifest
Channel-Specific Presets (TikTok Shop & Etsy)
"As a marketplace seller, I want presets for TikTok Shop and Etsy that enforce their image rules so that my uploads pass validation and look optimized without manual tweaking."
Description

Provide a managed, versioned library of presets that encode each channel’s specifications, including dimensions, aspect ratios, background rules, padding/safe margins, maximum file size, format, and color profile. Presets are remotely updatable and carry a version tag that is embedded into each pack’s manifest for auditability. A preflight validator checks inputs against the active preset and surfaces actionable errors or auto-fixes prior to export. Multi-select enables generating packs for multiple channels in one run while keeping outputs separated by channel.

Acceptance Criteria
Preset Library Completeness per Channel
Given I view the managed presets for TikTok Shop and Etsy Then each preset exposes non-null fields: id, name, channel, version (semver), dimensions (widthPx, heightPx), aspectRatio, backgroundRules, paddingSafeMargins, maxFileSizeBytes, fileFormat, colorProfile And each preset passes schema validation and is selectable in QuickSets and via API GET /presets/{id} And selecting a preset applies its parameters to the workspace defaults immediately
Remote Preset Update and Version Pinning
Given the client has cached TikTok Shop preset v1.2.0 and the remote library publishes v1.3.0 When the client is online Then the client detects and syncs the new preset list within 5 minutes And new QuickSets runs default to v1.3.0 unless a user has pinned a prior version And previously exported packs remain tagged with their original version And the UI displays an "Update available" indicator with actions: View changes, Use latest, Pin to v1.2.0
Manifest Embeds Preset Version and Rules Hash
Given a QuickSets export completes for any channel Then the pack root contains manifest.json with keys: channel, presetId, presetName, presetVersion (semver), rulesHash (SHA-256), generatedAt (ISO-8601), itemCount And each image carries presetVersion in filename or XMP metadata And manifest.json validates against schema v1 and opens without parse errors
Preflight Validator Auto-Fixes and Blocks Export
Given a batch includes images that violate the active preset (e.g., non-sRGB, aspect mismatch, oversized file) When I run Preflight Then issues are listed per image with categories: Auto-fix, Warning, Blocker and actionable messages And auto-fixable issues (e.g., convert to sRGB, pad to safe margins, background cleanup) are corrected and previewed And export is blocked until all Blockers are resolved; after auto-fix, Preflight status shows Pass
Multi-Select Pack Generation and Channel Separation
Given I multi-select TikTok Shop and Etsy and start QuickSets for 25 SKUs Then two channel-specific packs are generated in one run, each with its own manifest.json And outputs are separated by channel folders under /Exports/{RunID}/{Channel}/... And total image count equals 25 × variantsPerChannel for each channel And a failure in one channel is reported without blocking export of the other
Per-Channel Variant Set and Naming Consistency
Given I run QuickSets with a channel preset selected Then for each SKU the system generates the preset-defined variants (e.g., vertical, square, main-image) meeting dimensions and safe margins And filenames follow {SKU}_{variant}_{channel}_v{presetVersion}.{ext} and are unique within the pack And the folder structure groups images by channel and variant consistently with the naming pattern
Consistent Naming & Foldering
"As a seller managing dozens of listings, I want consistent file names and folders so that I can bulk upload and track variants and tests without confusion or manual renaming."
Description

Apply configurable, collision-safe naming templates and folder structures to every generated asset to ensure predictable bulk uploads and easy traceability. Support tokens such as {sku}, {channel}, {variant}, {ab_tag}, and {run_id}, enforce marketplace-safe characters and length limits, and offer overwrite/append/skip policies on name collisions. Output a normalized hierarchy (e.g., /{run_id}/{sku}/{channel}/) with an index manifest (JSON/CSV) mapping sources to outputs, plus an option to package results as a single ZIP for quick download.

Acceptance Criteria
Tokenized Naming Template Resolution for QuickSets Variants and Channels
Given a user-defined filename template using tokens {sku}, {channel}, {variant}, {ab_tag}, and {run_id} And a QuickSets run producing vertical, square, and main-image variants for multiple channels When assets are generated Then each output filename is produced by substituting tokens with their concrete values for that asset And any missing token value resolves to an empty string and adjacent non-alphanumeric delimiters are collapsed to a single hyphen And the file extension from the source or output format is preserved exactly And the resulting names are unique within the destination directory before collision policy is applied And an example with template "{sku}-{channel}-{variant}-{ab_tag}" for sku=ABC123, channel=etsy, variant=main, ab_tag=A yields "ABC123-etsy-main-A.jpg"
Marketplace-Safe Sanitization and Length Enforcement
Given per-channel safe-name policies defining allowed characters (regex), case rules, and max lengths for filenames When generating folder names and filenames from templates Then characters not matching the channel's allowed set are removed or replaced with a hyphen And multiple consecutive hyphens are collapsed to a single hyphen and leading/trailing hyphens/underscores are trimmed And Unicode characters are transliterated to ASCII where possible; remaining disallowed characters are removed And reserved filenames (e.g., CON, PRN, NUL) are suffixed with "-safe" to avoid OS conflicts And the final filename length does not exceed the channel-configured max length, counting the extension; if too long, the stem is truncated before the extension to fit the limit And sanitization is applied consistently to each path segment ({run_id}, {sku}, {channel}) and to the filename
Name Collision Handling Policies (Overwrite, Append, Skip)
Given a destination directory may already contain a file with the target filename And the user selects a collision policy of Overwrite, Append, or Skip When a collision is detected for an output asset Then for Overwrite: the existing file is replaced and only one file exists at that path after the run And for Append: a numeric suffix "-1", "-2", ... is inserted before the extension until a unique filename is found; counting starts at 1 and is deterministic within the run And for Skip: the colliding asset is not written and no new file is created And in all cases, the manifest records the collision policy, the action taken (overwritten/appended/skipped), and the final output path
Normalized Folder Hierarchy Generation
Given the required normalized hierarchy pattern "/{run_id}/{sku}/{channel}/" When QuickSets produces outputs for a batch Then directories are created to exactly match the pattern with token values substituted and sanitized per channel policy And no extra nesting levels are introduced; variant placement is represented in the filename, not as additional folders, unless explicitly configured elsewhere And the resulting hierarchy is consistent across all assets in the run and is identical between repeated generations with the same inputs and run_id And path segments never exceed the channel-configured length limits after sanitization
Index Manifest Export (JSON and CSV)
Given outputs have been generated for a run When the run completes Then an index manifest is produced in both JSON and CSV formats within the run's root folder ("/{run_id}/") And each manifest row maps a single source asset to its outputs, including at minimum: source_id/path, output_filename, output_relative_path, sku, channel, variant, ab_tag, run_id, collision_policy, action (created/overwritten/appended/skipped), file_size_bytes, checksum_sha256, and timestamp And the number of manifest rows equals the number of attempted outputs (including skipped entries) And the manifest files themselves are included in any packaged ZIP and are downloadable independently
Single ZIP Packaging with Structure Preservation
Given the user selects the option to package results as a single ZIP When the run completes Then a ZIP file named "{run_id}.zip" is created containing the full normalized folder hierarchy and both manifest files at the ZIP root And all internal ZIP paths match the on-disk relative paths under "/{run_id}/" And ZIP archive integrity validates successfully (CRC checks pass) and the archive can be extracted without path collisions on supported OSes And the ZIP contains only the run's outputs and manifests, with no temporary or hidden system files
Lightweight A/B Variant Generation
"As a marketer, I want quick A/B-ready image variations so that I can test and pick higher-CTR visuals without spending time on manual edits."
Description

Generate up to N lightweight visual variations per base variant (e.g., crop offset, lighting contrast, background intensity, shadow strength) using deterministic seeds and labeled tags (A, B, C). Provide a quick-compare view to accept, discard, or regenerate options and persist the chosen winner tag in the manifest for downstream tracking. Ensure variations remain compliant with channel presets and export each pair/group with ab_tag included in filenames and metadata for simple A/B checks.

Acceptance Criteria
Generate Up to N Variations Per Base Variant
Given a base variant is selected in QuickSets and the user sets N within the allowed range And variation parameters (crop offset, lighting contrast, background intensity, shadow strength) are enabled per preset When the user runs Generate Variations Then exactly N variations are created for the base variant And each variation has a unique parameter signature and deterministic seed recorded in its metadata And the UI displays N tiles labeled with their ab_tag and parameter deltas And no generated variation violates the active channel preset
Deterministic Seeds Produce Repeatable Variations
Given a base variant, a channel preset, and a recorded set of seeds and parameter ranges When generation is repeated with the same inputs Then the resulting images are pixel-identical to the prior run And filenames and embedded metadata (except timestamps) match the prior run And when generation is repeated with different seeds, the resulting images are not pixel-identical and parameter signatures differ
A/B/C Tag Assignment and Stability
Given N variations are generated for a base variant Then the system assigns ab_tag labels sequentially starting at "A" with no duplicates within that base variant And each variation’s ab_tag is immutable until the user discards that variation And when the user regenerates a specific slot, the new variation retains the same ab_tag
Quick-Compare: Accept, Discard, Regenerate
Given variations exist for a base variant When the user opens the quick-compare view Then all variations appear in a grid with thumbnail, ab_tag, key parameter deltas, and seed And the user can mark exactly one variation as Winner And the user can discard one or more variations, removing them from the grid And the user can regenerate any slot, replacing its image while retaining its ab_tag And all actions persist immediately to the working manifest
Persist Winner Tag in Manifest
Given the user marks a variation as Winner When the session is saved or an export is triggered Then the manifest stores winner_ab_tag, winner_seed, and winner_parameter_signature for the base variant And reopening the pack restores the Winner state And downstream payloads expose winner_ab_tag for analytics and tracking
Channel Preset Compliance Enforcement
Given an active channel preset (e.g., TikTok Shop, Etsy) When variations are generated Then each variation passes the preset validator for aspect ratio, background rules, minimum resolution, and disallowed overlays And if a candidate would violate a rule, parameters are auto-clamped or the candidate is excluded with a visible reason And the system attempts regeneration as needed so that the displayed count equals N, or informs the user if N cannot be met
Export With ab_tag in Filenames and Metadata
Given a base variant with generated variations and an optional Winner When the user exports the set Then each exported file name includes the ab_tag and follows the pattern {base_name}_{channel}_{variant_id}_{ab_tag}.{ext} And exported file metadata includes ab_tag and seed (e.g., XMP/PNG tEXt) And files are organized into channel-specific folders with consistent naming, and the manifest/CSV lists ab_tag for each row And if a Winner exists, it is flagged in the manifest and export payloads
Batch Processing, Queueing, and Resume
"As a power user handling large catalogs, I want robust batch processing with progress and resume so that long jobs complete reliably without babysitting or losing work."
Description

Enable reliable batch execution for hundreds to thousands of images with a job queue, parallel workers, and automatic backoff/retry on transient failures. Provide progress tracking (percent, ETA, per-SKU status), the ability to pause/resume, and chunked processing so partial results are preserved. Implement resource guardrails to prevent memory/CPU contention and ensure deterministic output ordering. Persist job state so interrupted sessions resume without duplicating outputs.

Acceptance Criteria
Large Batch Processing with Deterministic Output Ordering
Given a batch of 5,000 images across multiple SKUs and a configured worker concurrency of 8 When the batch is submitted to the queue Then all items are enqueued within 10 seconds And no more than 8 workers process items concurrently at any time And outputs are written in deterministic order: by SKU ascending, then source filename ascending, then channel ascending, then variant type in the order [main, square, vertical], regardless of worker scheduling And the job completes with 100% of items either Completed or Failed and 0 items left In Progress
Real-time Progress, ETA, and Per-SKU Status
Given a running batch job with at least 100 items When 10% of items have completed Then the API exposes progress with fields percentComplete (0–100), itemsCompleted, itemsTotal, etaSeconds, and perSku[] And percentComplete updates at least once every 2 seconds during active processing And etaSeconds error is within ±20% of actual remaining time after 10% completion And perSku[] reports, for a selected SKU, counts of completed, failed, and pending items that sum to that SKU’s total
Pause and Resume Without Duplication
Given a running batch with at least 500 items When the user requests Pause Then no new items start within 2 seconds and in-flight items are allowed to finish And job status becomes Paused with a stable checkpoint index When the user requests Resume Then processing continues from the next pending item using the checkpoint And no duplicate outputs are created (verified by file count and hash set equality against expected unique items)
Transient Failure Backoff and Retries
Given processing of an item that encounters transient errors (HTTP 429/5xx, network timeout) When the first failure occurs Then the system retries with exponential backoff and jitter: initial delay 1s, multiplier 2.0, max delay 30s, up to 5 attempts total And the cumulative per-item processing window does not exceed 5 minutes And on final failure the item is marked Failed with a machine-readable errorCode and attemptCount=5 And permanent errors (e.g., unsupported format) are not retried and are marked Failed on first attempt
Crash/Restart Resume with Idempotent Outputs
Given a batch in progress with 30–70% completion When the application process is terminated and restarted Then the job is reloaded from persisted state within 15 seconds And processing resumes only for items not yet completed at the time of crash And previously completed outputs are not recreated or duplicated (verified by idempotency keys composed of jobId+SKU+sourceFile+variant+channel)
CPU/Memory Guardrails and Concurrency Backpressure
Given worker concurrency is set to 12 When average CPU usage over a 60-second window exceeds 80% or memory usage exceeds 75% of the configured limit Then the system reduces active concurrency within 5 seconds until usage falls below thresholds, but not below 1 worker And worker concurrency never exceeds the configured maximum And no out-of-memory termination occurs during the run And guardrail activations are logged with timestamps and new concurrency level
Chunked Processing and Partial Result Preservation
Given chunk size is configured to 100 items When processing a 1,000-item batch Then outputs are durably written per item as they complete and a checkpoint is recorded every 100 completed items And if processing stops after 450 items, files for 450 items exist on disk while the last checkpoint reflects 400 items And upon resume, processing skips the 450 completed items (no reprocessing) and continues at item 451 And listing and export endpoints return completed items only in deterministic order across chunks
Multi-Destination Export & API Hooks
"As a seller automating my workflow, I want to export QuickSets to my storage or marketplace and trigger runs via API so that I can publish faster with minimal manual steps."
Description

Support exporting packs as local ZIP downloads and direct uploads to common destinations (S3-compatible storage, Google Drive, Dropbox) with stored credentials. Where available, provide lightweight connectors for TikTok Shop and Etsy to prefill listing assets or stage media; otherwise, format exports to align with each marketplace’s bulk uploader templates. Expose a REST endpoint and webhook callbacks to trigger QuickSets programmatically and receive job completion events along with the manifest and download links.

Acceptance Criteria
Local ZIP Export with Channel-Consistent Structure
Given a completed QuickSet pack with variants for one or more channels When the user selects Download ZIP for that pack Then a ZIP is generated within 60 seconds for packs ≤ 500 images and a secure download link is presented And the ZIP contains a root manifest.json and per-channel folders using path {channel}/{variant}/ And each file name matches {sku}_{variant}_{index}.{ext} with variant ∈ {main,square,vertical} And manifest.json enumerates every exported file with relativePath, mimeType, byteSize, sha256, and variant, and the count matches the files in the ZIP And the archive passes integrity verification and can be extracted without errors on Windows and macOS
S3-Compatible Direct Upload with Stored Credentials
Given stored S3-compatible credentials (endpoint, accessKey, secretKey, bucket, region/pseudo-region) validated at save time When the user exports a pack to the configured S3 destination with an optional prefix Then all files are uploaded to s3://{bucket}/{prefix}/{channel}/{variant}/{sku}_{variant}_{index}.{ext} and the operation completes without errors And the API returns per-object locations and ETags; the manifest includes s3 URLs and ETags for each file And uploads retry up to 3 times with exponential backoff on 5xx/timeout errors; failures > 3 attempts are recorded with an errorCode in the manifest And invalid/expired credentials result in a terminal failure with a clear errorCode and no partial files left behind (failed uploads cleaned up when possible)
Google Drive Upload to Specified Folder with Token Refresh
Given a saved Google OAuth credential with a refresh token and a target folderId When the user exports a pack to Google Drive Then the system refreshes the access token if needed and uploads all files under the specified folderId, creating subfolders {channel}/{variant}/ as necessary And filename collisions at the same path overwrite the existing file; the manifest records fileId and webViewLink for each uploaded asset And the export fails fast with a clear errorCode when the folderId is invalid or permission is missing And files > 100 MB are uploaded using resumable upload and complete successfully
Dropbox Upload with Folder Creation and Version Handling
Given a connected Dropbox account with App Folder permissions and a configured base path When the user exports a pack to Dropbox Then the system creates any missing folders and uploads files under {basePath}/{channel}/{variant}/ And name collisions create a new file revision; the manifest records pathDisplay and rev for each file And files > 150 MB are uploaded via chunked upload and complete successfully And revoked/expired tokens cause a clear failure with no orphan partial files
Marketplace Connectors and Fallback Bulk Templates
Given the user has linked TikTok Shop and Etsy accounts and selects those channels in the pack When the export runs Then for TikTok Shop, assets are staged via the connector and the manifest includes tiktok.assetId per image; for Etsy, assets are uploaded/staged and the manifest includes etsy.assetId per image And if a connector is unavailable or not linked for a selected channel, the export produces a channel-specific bundle containing images and a CSV/JSON manifest that matches the marketplace’s bulk uploader template (field names, required columns, and file paths) And each channel’s bundle passes validation by the marketplace sandbox/bulk validator without manual edits
REST Endpoint to Trigger QuickSets Programmatically
Given an API client with a valid API key When it POSTs to /v1/quicksets/jobs with a JSON payload specifying input images, quicksetTemplateId, destinations, and callbackUrl Then the API validates the payload and responds 202 Accepted with a body containing jobId, status=pending, and a job status URL And invalid payloads return 400 with a list of field errors; unauthorized requests return 401 And the endpoint enforces a rate limit of at least 60 requests/min per key and returns 429 with Retry-After when exceeded
Webhook Callback on Job Completion with Manifest and Links
Given a job was created with a callbackUrl and a shared secret When the job completes (success or failure) Then the system POSTs a JSON payload to callbackUrl containing jobId, status, startedAt, completedAt, per-destination statuses, manifestUrl(s), and download links (ZIP or direct URIs) And the request includes an HMAC-SHA256 signature header over the body using the shared secret; deliveries with invalid signatures are not retried And non-2xx responses are retried up to 5 times with exponential backoff and unique deliveryId; duplicate deliveries are marked idempotent via eventId

Auto Split

Set precise A/B/C traffic weights (e.g., 50/30/20) and PixelLift auto-labels files, folders, and CSVs to match. Built‑in guardrails prevent misconfigured splits, and variant quotas are tracked so each version gets fair exposure—eliminating manual math and setup errors.

Requirements

Weighted Split Configurator & Validation
"As a solo e‑commerce seller, I want to set exact traffic weights for my image variants so that each gets the intended exposure without me doing manual math."
Description

Provide a UI and API to define precise A/B/C (and N-way) traffic weights per batch or listing, enforcing that totals equal 100% with configurable precision (e.g., 1% increments). Validate inputs in real time, surface inline errors and warnings, and offer one-click normalization to correct near-100 totals. Persist split presets at workspace and project levels for reuse, with sensible defaults and auditability. Ensure concurrency-safe updates and versioning so in-flight jobs use a locked configuration while edits create a new version. This enables accurate, reproducible splits without manual math, tightly integrated with PixelLift’s batch pipelines and export settings.

Acceptance Criteria
UI Real-Time Validation for N-way Weights
Given a user configures N variants with increment=1% and tolerance=0% When any weight is not a multiple of 1% or the total ≠ 100% Then the Save action is disabled and inline errors show the current total and the specific invalid fields Given all weights are multiples of increment and each weight ≥ increment and the total = 100% When the user clicks Save Then the configuration saves successfully and a success message is displayed Given any weight < increment or any weight > 100% When the user attempts to save Then an inline validation error appears with an error code OUT_OF_RANGE and Save remains disabled
One-Click Normalization Within Tolerance
Given the tolerance is set to 1% and increment=1% When the total weight is 99% or 101% Then the Normalize button is enabled and the Save button is disabled Given the user clicks Normalize When normalization runs Then weights are proportionally adjusted and rounded to the nearest increment so that the total = 100% and no individual weight changes by more than 1% from its pre-normalized value Given normalization succeeds When the user reviews the fields Then the Save button is enabled and a non-blocking info message indicates normalization was applied
API Split Create/Update Validation
Given a POST /splits request with body containing variants[], weights[], increment, tolerance When weights are multiples of increment, each weight ≥ increment, and the sum = 100% Then the API responds 201 with the created split including id, version, scope, and normalized=false Given a POST or PUT request where sum ≠ 100% beyond tolerance When the server validates Then the API responds 422 with code=SUM_NOT_100 and includes fields: providedTotal, requiredTotal=100, increment, tolerance Given any weight not a multiple of increment or any duplicate variant labels When the server validates Then the API responds 422 with code in {NOT_MULTIPLE_OF_INCREMENT, DUPLICATE_VARIANT_LABELS} and identifies offending indices
Scoped Presets with Defaults and Audit Log
Given a user with Manage Presets permission When they save a preset at workspace scope with name, variants, weights, increment, tolerance Then the preset is persisted with version=1 and is visible to all projects in the workspace Given a project-level preset exists When the user sets it as Project Default Then new projects jobs in that project auto-select this preset unless a Workspace Default is enforced to override Given a preset is created/updated/deleted When viewing the audit log Then an entry exists with action, actor, timestamp, scope, presetId, version, before/after snapshots Given a user without Manage Presets permission When attempting to create/update/delete a preset Then the operation is blocked with 403 Forbidden
Versioning and Job Locking for In-Flight Batches
Given a batch job is started with presetId=P and version=1 When a user edits the preset and saves version=2 Then the in-flight job continues using version=1 and new jobs reference version=2 by default Given two users attempt to update the same preset concurrently When the second save is submitted with a stale If-Match version Then the API responds 409 CONFLICT with code=VERSION_MISMATCH and includes the latest version Given a job references preset P v1 When the preset is deleted Then v1 remains readable and executable for audit and reproducibility, and deletion only removes the ability to select P as default for new jobs
Export Auto-Labeling and Quota Allocation
Given a batch of 100 assets and a split of A/B/C = 50/30/20 When the split is applied Then 50 assets are labeled variant=A, 30 as B, and 20 as C using largest-remainder allocation, and the assignment is deterministic across re-runs Given the batch size is smaller than the number of variants When allocation runs Then at least one asset is assigned to each of the top-K variants by weight until assets are exhausted Given export to files/folders/CSV is executed When artifacts are generated Then each asset’s filename and CSV row include fields: variant_label, variant_weight, split_version_id, and the aggregate counts per variant are recorded in the job summary
Auto Labeling for Files, Folders, and CSV
"As a seller, I want exported files and CSVs auto-labeled by variant so I can upload A/B/C versions to marketplaces without manual renaming or folder setup."
Description

Automatically apply consistent variant labels and weight tags across filenames, folder structure, and CSV exports. Support configurable naming templates (e.g., {sku}_{variantId}_{weight} or marketplace-specific patterns), safe character sanitization, and collision avoidance with deterministic suffixing. Ensure labels propagate through bulk exports and mirror the configured split so marketplace imports and listing tools recognize variant groupings. Provide preview of resulting names and a dry-run export to verify structure. Backwards compatible with existing exports and integrated into PixelLift’s export queue and storage paths.

Acceptance Criteria
File Naming Template Application
Given a project with variants A/B/C weighted 50/30/20 and a file naming template "{sku}_{variantId}_{weight}" When exporting 10 processed images for SKU "PL-001" with three variants enabled Then every output filename exactly matches the template for its variant (e.g., "PL-001_A_50", "PL-001_B_30", "PL-001_C_20") And the weight tag in each filename equals the configured weight for that variant And if the template contains no time-based tokens, repeated exports with unchanged inputs produce identical filenames
Folder Structure Labeling Alignment
Given a folder naming template "{marketplace}/{sku}/{variantId}_{weight}" and marketplace profile "amazon" When a batch export is executed for SKUs "PL-001" and "PL-002" Then the export creates directory paths "amazon/PL-001/A_50", "amazon/PL-001/B_30", "amazon/PL-001/C_20", and equivalent for "PL-002" And each directory contains only files for its matching SKU and variant And no additional or missing variant directories are created relative to the configured split
CSV Export Column and Value Labeling
Given CSV export is enabled with columns ["sku","variant_id","weight","variant_group_id"] and a grouping key "{sku}" When exporting three variants A/B/C for SKU "PL-001" weighted 50/30/20 Then the CSV contains exactly three rows for "PL-001" with variant_id values ["A","B","C"] and weight values [50,30,20] And all three rows share the same variant_group_id value for "PL-001" And the CSV header and field formats conform to the selected marketplace profile schema without validation errors
Safe Character Sanitization
Given a naming template that produces names containing spaces, slashes, and non-ASCII characters and the "generic" sanitization profile When generating file and folder names Then all generated names match the regex "^[A-Za-z0-9._-]+$" And leading/trailing spaces and dots are removed, multiple separators are collapsed to a single "-", and names are truncated to a maximum of 128 characters without dropping required tokens And a per-item sanitization log is available indicating original vs. sanitized name when changes occur
Collision Avoidance with Deterministic Suffixing
Given two or more inputs resolve to the same target name after templating and sanitization When the export plan is generated Then the system appends a deterministic suffix of the form "_[a-z0-9]{6}" to colliding names based on stable input attributes to ensure uniqueness And no duplicate file paths or CSV identifiers exist in the plan or the final export And rerunning the same export with identical inputs produces the exact same suffixed names
Preview and Dry-Run Export Validation
Given the user selects "Preview" and "Dry Run" for a batch of 1,000 assets When the dry run completes Then no image files are written to permanent storage and no export queue jobs are enqueued And a downloadable preview (tree view and CSV sample) lists 100% of planned paths and rows with collision/sanitization warnings where applicable And the dry run finishes within 60 seconds and returns a success/fail status with error details if the template contains unknown tokens
Backwards Compatibility and Queue Integration
Given an existing project using legacy export naming and an active export queue When exporting without a custom naming template configured Then the output file paths, folder structure, and CSV schema match the legacy format exactly And previously generated asset URLs remain valid and unchanged And queued exports that use the new labeling show the resolved names in the job detail, and retries/resumes produce the same names without path conflicts
Variant Quota Tracker & Fairness Engine
"As a seller, I want PixelLift to track how often each variant is used so that exposure stays aligned with my target weights across batches and retries."
Description

Implement a quota tracking service that records assignments and exports per variant and enforces proportional distribution over time to match configured weights. Support per-listing, per-batch, and rolling window modes, with options to reset or carry over deficits. Handle variable batch sizes and late-added variants by rebalancing future assignments while keeping past outputs immutable. Provide deterministic, seedable allocation to ensure reproducibility and avoid over-serving a variant during retries. Expose counters and health indicators via API for monitoring and integrate with job scheduler to make serve-time decisions.

Acceptance Criteria
Proportional Distribution Over Variable Batch Sizes
Given weights A:50%, B:30%, C:20% and per-batch mode When processing a single batch of N=10 Then assignments are A=5, B=3, C=2 and sum to N Given the same weights and per-batch mode When processing batches of sizes 3, 7, and 20 (in any order) Then cumulative assignments after all batches differ from round(N_total*weight_v) by ≤1 for each variant v if N_total<100, otherwise each variant’s share is within ±2% of its weight Given any batch size N≥1 When allocating Then the rounding method is deterministic (largest remainder or equivalent), the sum equals N, and no variant deviates by more than 1 from floor(N*weight_v) in that batch
Rolling Window Fairness Enforcement
Given rolling window mode with windowSize=W=500 and weights A:50%, B:30%, C:20% When serving a continuous stream of requests Then in any sliding window of the most recent 500 confirmed exports, each variant’s observed share is within ±3% of its configured weight Given rolling window mode and option resetDeficits=true When windowSize or weights change Then window counters reset within 1s and no carry-over adjustments apply to future allocations Given rolling window mode and option carryOverDeficits=true When a variant is under-served by Δ relative to target in the last W confirmed exports Then the engine biases future allocations so that the deficit decreases monotonically and is ≤1 assignment within the next W confirmed exports (unless the variant weight is 0%)
Late-Added Variant Rebalancing Without Mutating Past Outputs
Given active variants A:60% and B:40% and 100 confirmed exports When variant C is added and weights change to A:50%, B:30%, C:20% effective now Then no prior outputs are modified and historical counters remain immutable Given the change above When processing the next 500 confirmed exports after the change Then the observed shares among A, B, C in those 500 exports are within ±3% of the new weights and no single batch over-allocates any variant by more than +1 versus deterministic rounding for that batch Given a large deficit for a newly added variant due to past periods with no exposure When rebalancing Then the engine does not exceed 2× the expected per-batch count for that variant (based on batch size and weight) to catch up, preserving per-batch fairness
Deterministic, Seedable Allocation for Reproducibility
Given a fixed seed S and identical inputs (weights, mode, batch/listing identifiers, order of requests) When running the allocator twice Then the sequence of assigned variantIds is exactly identical across runs Given no seed provided but identical inputs When running the allocator twice Then the sequence of assigned variantIds is identical (default deterministic behavior) Given two different seeds S1≠S2 with identical inputs When running the allocator Then the sequences differ and neither variant is over- or under-served beyond the fairness bounds defined for the active mode
Retry Handling Without Over-Serving a Variant
Given an assignment was issued but export failed and the job is retried with the same idempotencyKey When requesting allocation again Then the same variant is returned, the assignment is not double-counted, and counters remain unchanged until export confirmation Given an assignment reservation is created When no confirmation arrives within the reservationTTL=15 minutes Then the reservation auto-expires, the tentative count is rolled back, and future allocations proceed without over-serving any variant Given a duplicate export confirmation for the same allocationToken When the confirmation endpoint is called Then the operation is idempotent (HTTP 200) and counters are not incremented twice
API Exposure of Counters and Health Indicators
Given GET /v1/quotas/{scopeId} with a valid scopeId (listing or batch) When called Then respond 200 within p95≤150ms with JSON including configuredWeights, mode, cumulativeAssigned, cumulativeExported, windowAssigned, windowExported, deficitsByVariant, lastResetAt, seed, and health{status∈[healthy,drifting,misconfigured], allocationErrorPctByVariant} Given the API response When health.status=healthy Then each variant’s window allocationErrorPct ≤3% Given the API response When health.status=drifting Then at least one variant’s window allocationErrorPct is >3% and ≤8% Given the API response When health.status=misconfigured Then weights are invalid (sum not within 100%±0.1% or any negative weight) or an active variant has weight 0% Given an invalid scopeId When GET /v1/quotas/{scopeId} is called Then respond 404 Given high read load ≥50 req/s When serving the endpoint Then data freshness (lag from latest confirmed export) is ≤5s
Scheduler Integration for Serve-Time Decisions
Given the scheduler requests an allocation via POST /v1/allocations with {scopeId, modeOverride?, batchId?, idempotencyKey} When processed Then respond within p95≤50ms with {variantId, allocationToken(version, seed, weightsHash)} and the decision honors the configured mode (per-listing, per-batch, or rolling window) Given 100 concurrent scheduler workers over 10,000 allocations When processing Then contention retries due to optimistic locking or conflicts are ≤0.1% and the final distribution satisfies the fairness bounds defined in the relevant mode Given a successful export When POST /v1/allocations/confirm is called with allocationToken Then the reserved assignment transitions to exported within ≤2s and the operation is idempotent (repeat calls do not change counters) Given modeOverride is provided When serving an allocation Then the decision logic uses the override for that request only and does not affect persisted configuration
Preflight Simulation & Guardrails
"As a seller, I want a preflight simulation that flags misconfigured splits and offers one-click fixes so I avoid setup errors before exporting."
Description

Add a preflight step that simulates the distribution given the current assets, variants, and weights, highlighting issues such as totals not equal to 100%, insufficient assets to satisfy quotas, or disabled variants referenced by the split. Provide actionable suggestions: auto-normalize weights, adjust batch sizes, or temporarily exclude under-provisioned variants. Block export on critical errors and allow override with justification on warnings. Show a summarized forecast (per variant counts) and a detailed per-file assignment preview. Integrates with the configurator, quota tracker, and export queue for a seamless review-and-fix workflow.

Acceptance Criteria
Weights Not Equal to 100% — Auto-Normalization Offered
Given a split configuration where variant weights sum is not equal to 100% When the user opens the Preflight Simulation Then the system displays a Warning "Weights do not equal 100%" and highlights the weight input area And an action "Auto-normalize" is available And when the user clicks "Auto-normalize", weights are proportionally scaled to sum to 100% using the largest remainder method with 1% granularity And the before/after weights are displayed inline And the simulation automatically reruns with the normalized weights And the warning is cleared if the sum equals 100%
Insufficient Assets to Satisfy Quotas — Critical Error and Fix Options
Given a batch size B and weights that compute target counts T_v for each variant v And the available eligible assets for variant x is A_x where A_x < T_x When Preflight runs Then a Critical Error is shown: "Insufficient assets for variant x: need T_x, have A_x" And the Export action is disabled And the UI presents fix options with live forecast previews: "Reduce batch size", "Adjust weights proportionally", and "Temporarily exclude variant x" And when the user selects and applies any fix, the simulation reruns, the error is cleared, and Export becomes enabled
Disabled Variant Referenced by Split — Blocking Error
Given one or more variants are disabled in the configurator And the split includes any disabled variant When Preflight runs Then a Critical Error is displayed: "Disabled variant(s) referenced: [list]" And suggested actions "Enable variant(s)" and "Remove from split" are provided And until the issue is resolved via one of the suggestions, the Export button remains disabled And after resolution, the simulation updates and the error clears
Blocking on Critical Errors; Warnings Require Justification to Override
Given the Preflight results contain at least one Critical Error When the user attempts to proceed to export Then the Export action is disabled with a tooltip "Resolve critical errors to proceed" Given the Preflight results contain no Critical Errors but one or more Warnings When the user clicks Export Then a modal prompts for an override justification between 10 and 500 characters And the "Confirm Export" button remains disabled until valid justification is entered And upon confirmation, the justification text and the list of active warnings are audit-logged with timestamp, user ID, and batch ID
Summarized Forecast Matches Batch, Weights, and Rounding Rules
Given a batch size of 101 and weights of 50%, 30%, and 20% for variants A, B, and C When Preflight computes the forecast using the largest remainder method Then the per-variant counts are A=51, B=30, C=20 and the total equals 101 And for any batch size N and weights W_v, the sum of per-variant counts equals N And each variant's count differs from N*W_v by at most 1 due to rounding
Per-File Assignment Preview Is Deterministic and Exportable
Given a fixed input order of assets and a displayed randomization seed When Preflight generates the per-file assignment preview Then each file is assigned to a variant such that per-variant totals match the forecast And re-running Preflight with the same inputs and seed yields identical assignments And the preview table shows columns: file_id/filename, variant_id/name, assigned_label, and reason if unassigned And clicking "Download Plan (CSV)" exports a CSV with all rows present and UTF-8 encoding
Live Integration with Configurator, Quota Tracker, and Export Queue
Given the user adjusts weights, batch size, or variant enablement in the configurator When the user stops typing or changing controls for 500 ms Then the Preflight simulation reruns and updates the forecast and preview without a full page reload And active variant exposure quotas from the quota tracker are applied and, if limiting, surfaced as warnings with the affected counts And when the user confirms Preflight, the forecast and per-file plan are enqueued to the export queue under a single batch ID with status "Ready" And the quota tracker is updated with the planned exposures per variant
Export Schema Mapping for Marketplaces
"As a seller, I want marketplace-aligned export schemas so I can import variant sets without manual column edits or reformatting."
Description

Extend CSV schemas and export metadata to include variant identifiers, group IDs, and traffic weights, with mapping profiles for major marketplaces (e.g., Amazon, eBay, Shopify) and a generic template. Allow users to select a destination profile so headers and value formats match import requirements. Validate required fields per profile, generate companion README/mapping files, and include optional manifests (JSON/YAML) for programmatic pipelines. Ensure schema versioning and backward compatibility with existing PixelLift exports.

Acceptance Criteria
Marketplace Profile Selection Applies Correct Headers and Formats
Given I select a destination profile (Amazon, eBay, Shopify, or Generic) When I export a batch containing variantized products Then the CSV header row exactly matches the header list defined in the selected profile (order, spelling, casing) And each field value is formatted per the profile’s specification (types, enumerations, date/number formats, boolean/percent representations) And no extra or missing columns are present relative to the selected profile
Per-Profile Required Field Validation Blocks Invalid Exports
Given the selected profile defines required fields and constraints When I attempt to export and any row is missing a required field or violates a required constraint (e.g., invalid enum or format) Then the export is aborted with a validation error before any files are written And a machine- and human-readable validation report is provided listing row numbers, field names, and error messages And the export can proceed only after all blocking errors are resolved or a different profile is chosen
Variant and Group Identifiers and Traffic Weights Are Present and Consistent
Given Auto Split is configured for products with multiple variants When I export using any profile Then each row includes populated fields for variant_id, variant_group_id, and traffic_weight And variant_id values are unique within the export And all variants of the same product share the same variant_group_id And traffic_weight values reflect the configured split and are normalized to the selected profile’s required representation And for every variant_group_id the sum of traffic_weight values equals the profile’s expected total (e.g., 100 or 1.0); otherwise the export is blocked with a validation error
Companion Mapping README Generated Alongside CSV
Given an export completes successfully When I inspect the output Then a companion mapping file (README.md or mapping.txt) is generated adjacent to the CSV And it includes: profile name, schema_version, generation timestamp, and contact/support link And it documents each column: header, description, data type, required/optional, constraints/allowed values, example And it includes a sample row illustrating valid values And the README references the CSV filename and matches its headers exactly
Optional JSON/YAML Manifests for Programmatic Pipelines
Given I enable Include manifest in the export options When I export Then manifest.json and manifest.yaml are generated next to the CSV And they contain: profile name, schema_version, column definitions (name, type, required, constraints), CSV metadata (path, size, row_count, checksum), and per-variant-group stats (group count, variant counts) And manifest.json validates against the provided JSON Schema and manifest.yaml parses without errors And the manifests’ column definitions and counts match the actual CSV
Schema Versioning and Backward Compatibility
Given schema versioning is supported for exports When I export without explicitly selecting a version Then the latest schema_version is recorded in the README and manifests (and in a CSV metadata comment row if supported) And when I select a previous supported schema version Then the CSV headers, order, and value formats conform exactly to that version’s contract And when using the latest schema, any new fields are appended and marked optional in the README to avoid breaking existing consumers
Generic Template Export for Unlisted Destinations
Given I select the Generic profile When I export Then the CSV uses neutral, well-typed fields (e.g., sku, title, image_url, variant_id, variant_group_id, traffic_weight, attributes) suitable for downstream mapping And the mapping README describes each field without marketplace-specific naming or constraints And the manifests identify the profile as Generic and omit marketplace-specific restrictions And the export passes internal validation and can be transformed to any supported marketplace via mapping rules
Split Reporting & Audit Log
"As a seller, I want an audit trail and reports of my splits so I can verify fairness, diagnose issues, and share results with my team."
Description

Provide a reporting and audit module showing who configured splits, what changed, and when, along with execution outcomes: per-variant counts, deviations from target weights, retries, and overrides. Include filters by workspace, project, batch, listing, and time range, with CSV/JSON export for analytics. Surface alerts when fairness thresholds drift beyond configurable tolerances. Store immutable audit records for compliance and troubleshooting, integrated with existing activity logs and accessible via UI and API.

Acceptance Criteria
Audit Trail Visibility by Project
Given a user with Project Viewer or higher on Project P When they open Reporting > Audit and select Project P Then the table lists all split configuration events for P in the selected time range including actor name/id, action type (create/update/delete/override), changed fields, old value, new value, timestamp (UTC ISO 8601), source (UI/API), and request ID And entries are ordered by timestamp descending by default and are paginated (default page size 50, user-adjustable) And selecting an entry opens a detail view with a full diff of configuration JSON and linked batch/listing IDs where applicable And a user without sufficient permission receives HTTP 403 with no data leaked via headers or body
Multi-Dimensional Report Filtering
Given audit and outcome data across multiple workspaces When a user applies filters Workspace=W, Project=P, Batch=B, Listing=L, Time=[T1..T2] Then results include only records matching all provided filters (AND semantics) with inclusive bounds on T1 and T2 in UTC And removing any filter immediately broadens the result set accordingly And the UI displays active filter chips and the API echoes the applied filters in response meta And queries return within 3 seconds for up to 100k matching records and within 10 seconds for up to 1M records
Report Export in CSV and JSON
Given a filtered report view When the user requests Export CSV Then a UTF-8 RFC4180-compliant file downloads containing the visible columns plus stable IDs, matching the applied filters and sort order And When the user requests Export JSON Then an NDJSON stream downloads with one JSON object per row containing all API fields And For exports exceeding 100 MB or 1M rows Then an asynchronous export job is created with status (queued/running/failed/completed) and a signed URL valid for 24 hours; the API returns HTTP 202 with job ID And Exported totals (counts, percentages, deviations) equal UI/API totals within 0.01%
Fairness Drift Alerting and Acknowledgement
Given a project with target variant weights and a configured fairness tolerance (default 5 percentage points, configurable per project) When actual exposure deviates beyond tolerance for any variant within a batch or across the selected time window Then an in-app alert appears in Reporting with variant-level details (target%, actual%, deviation%, counts) And the alert is retrievable via GET /alerts with identical details and status (open/resolved/acknowledged) And When a Maintainer or above acknowledges the alert with a note Then the acknowledgement (actor, timestamp, note) is persisted to the immutable audit stream and alert status becomes acknowledged And When exposure returns within tolerance for two consecutive measurement intervals Then the alert auto-resolves and a resolution record is created
Per-Variant Outcome Metrics and Deviations
Given an executed split for Batch B with variants V1..Vn and target weights When the report is generated Then for each variant the UI and API provide: delivered_count, target_weight%, actual_weight% (delivered_count/total_delivered), deviation_pp (actual-target), abs_deviation_pp, retry_count, override_count, and last_retry_reason_code (if any) And batch totals include total_delivered, total_skipped, total_failed, and the sum across variants equals totals (eventual consistency window not to exceed 60 seconds) And metrics update within 60 seconds of new events being ingested And each override event is linked to the actor and reason and is visible in variant detail
Immutable Audit Record Storage and Verification
Given audit records for split configuration and execution When a new record is created Then it is stored append-only with a SHA-256 hash and previous-hash forming a tamper-evident chain And attempts to update or delete an existing record via UI or API return HTTP 403 and are themselves logged as security events And GET /audit/verify returns chain validity (valid/invalid) for a requested range and includes the terminal hash; verification for the last 10,000 records completes within 2 seconds And retention is configurable (e.g., ≥7 years) and deletion of unexpired records is blocked
API and UI Access with Activity Log Integration
Given existing activity logs for configuration changes When a split configuration is created, updated, or deleted Then exactly one audit record is written with a foreign key activity_log_id referencing the activity entry, and the activity entry contains audit_id; referential integrity is enforced (no dangling references) And the UI detail panel shows merged information from activity and audit logs without duplication, including actor, source, and request ID And the API provides paginated endpoints GET /audit and GET /reports/splits supporting sort by timestamp, actor, deviation, and retry_count with stable cursors and rate limits (HTTP 429 with Retry-After) And all endpoints require OAuth2 scopes (read:audit, read:reports); unauthenticated requests receive HTTP 401 and insufficient scopes receive HTTP 403

Winner Auto‑Promote

Define win rules (e.g., highest CTR after 1,000 impressions or best conversion rate over 7 days). When criteria are met, PixelLift prepares the winning image package and pushes it live where integrations allow—logging the change and offering one‑click rollback for safe, fast iteration.

Requirements

Rule Builder & Evaluation Engine
"As a solo e‑commerce seller, I want to define custom winning criteria for my image tests so that the best-performing image is automatically identified without manual analysis."
Description

A configurable rule system and UI for defining win conditions using metrics such as CTR, conversion rate, revenue per impression, and time windows. Supports AND/OR logic, minimum sample sizes, per-marketplace and per-listing scopes, priority order when multiple rules overlap, and scheduled evaluation cadence. Includes rule versioning, previews against historical data, and deterministic evaluation with clear pass/fail outcomes. Integrates with PixelLift’s variant metadata to ensure each decision maps to specific image packages.

Acceptance Criteria
Build and Evaluate CTR Rule with Min Sample and Time Window
Given an active rule with conditions CTR >= 4% AND impressions >= 1000 within the last 7 days for listing L on marketplace M When the evaluation engine runs Then the result for L on M is Pass if both conditions are met, else Fail with reason codes for unmet conditions Given the same inputs across repeated runs When the evaluation engine runs multiple times Then the pass/fail outcome and reported metrics are identical (deterministic) Given an active rule with conditions CTR >= 4% OR conversion rate >= 3% within the last 7 days When the dataset meets at least one condition Then the result is Pass and includes computed metrics, sample size, and evaluation window timestamps
Rule Scope Resolution by Marketplace and Listing
Given a rule scoped to Marketplace = M (all listings) When evaluation runs Then it evaluates only listing-marketplace pairs on M and excludes other marketplaces Given a rule scoped to Listing = L on Marketplace = M When evaluation runs Then only the L–M pair is evaluated and reported Given a listing that does not meet the rule’s minimum sample size When evaluation runs Then the result is Fail with reason "insufficient-sample" and includes the observed sample count
Overlapping Rules and Priority Selection
Given two active rules R1 (priority 10) and R2 (priority 5) both Pass for the same listing–marketplace pair When evaluation runs Then R1 is selected and R2 is not applied, and the decision log records the selected rule ID and priority Given two active rules with equal priority that both Pass for the same target When evaluation runs repeatedly Then the same rule is consistently selected (deterministic tie handling) and a "priority-tie" reason is logged Given one overlapping rule Pass and another Fail for the same target When evaluation runs Then the Pass rule is selected regardless of relative priorities
Scheduled Evaluation Cadence and Idempotency
Given a rule set with evaluation cadence configured to every 6 hours starting at 00:00 UTC When the scheduler is enabled Then evaluations trigger at 00:00, 06:00, 12:00, and 18:00 UTC within ±1 minute and produce a run ID and window bounds Given the system was offline during a scheduled time When it resumes Then a single catch-up evaluation is executed for the most recent missed window and duplicate evaluations for the same window are not produced (idempotent) Given no changes to underlying data within the evaluated window When two evaluations run for the same window Then outcomes, metrics, and logs are identical (deterministic)
Rule Versioning and Historical Preview
Given an active rule version v1 and a draft version v2 with modified conditions When Preview is run against the last 30 days of historical data Then per-target pass/fail outcomes are returned with computed metrics and v1 vs v2 outcome comparison, and no live selections are altered Given v2 is activated When activation completes Then v1 remains stored as a prior version with a unique version ID and timestamp, and v1 can be restored as the active version Given a rule has multiple saved versions When Preview is run for a specific version Then the preview results are reproducible for that version and time window (deterministic)
Variant Metadata Mapping to Image Packages
Given a passing evaluation for a listing–marketplace target where the winning decision references a variant with imagePackageId in PixelLift metadata When the decision is generated Then the output includes the specific imagePackageId, listing ID, and marketplace, establishing a one-to-one mapping for promotion Given the winning decision lacks a resolvable imagePackageId When the decision is generated Then the result is Fail with reason "missing-mapping" and no promotion payload is emitted Given identical inputs across runs When the evaluation re-executes Then the same imagePackageId is produced for the same target (deterministic mapping)
Metrics Ingestion & Variant Attribution
"As a seller, I want accurate performance data tied to each image variation so that auto-promotion decisions are trustworthy."
Description

Reliable ingestion and normalization of impressions, clicks, CTR, add-to-cart events, orders, and conversion rate per image variant from marketplace APIs and tracking pixels. Ensures correct attribution to variants via persistent IDs/UTMs, handles late-arriving data and deduplication, supports backfills and time zone alignment, and provides data quality monitoring with alerts for gaps or anomalies. Exposes a queryable store optimized for rolling windows needed by rule evaluations.

Acceptance Criteria
Metric Normalization per Variant
Given raw events from Marketplace API A and Pixel source B include impressions, clicks, add_to_cart, orders, timestamps, listing_id, variant_id, utm_variant When the ingestion pipeline processes the batch Then the normalized store writes per-variant records with fields impressions, clicks, add_to_cart, orders, ctr = round(clicks/impressions, 4) when impressions > 0 else null, conversion_rate = round(orders/clicks, 4) when clicks > 0 else null And all timestamps are stored in UTC with original source timezone captured in metadata And source field is preserved (marketplace|pixel) and marketplace identifier is normalized to a canonical enum And numeric fields are non-negative integers and schema validation rejects or quarantines invalid rows with error reasons And per-variant metrics roll up correctly to per-listing sums within 0.01% tolerance in test fixtures
Persistent ID/UTM Variant Attribution
Given three image variants for a listing are deployed with persistent variant_id and utm_variant parameters embedded in URLs and the variants catalog is populated When 10,000 known-mapped events across 24h and multiple time zones are ingested Then >= 99.9% of events are attributed to the correct variant_id via direct match or utm_variant fallback And <= 0.1% of events are stored as unattributed with reason code (missing_id|conflict|unknown_variant) And no event is attributed to more than one variant And referential integrity ensures every attributed variant_id exists in the variants table
Late-Arriving Data Reconciliation (7-day SLA)
Given the system is configured to accept late events up to 7 days after event_time When an event for a closed daily window arrives 6 days late Then per-variant daily and rolling 7/14/30-day aggregates are corrected within 5 minutes of ingestion And a correction audit record is written with event_id, affected windows, previous_value, new_value And when an event arrives >7 days late, it is stored but excluded from rule evaluation windows and flagged as late_excluded
Idempotent Deduplication Across Sources
Given a batch of 1,000,000 events containing 5% exact duplicates (same external_event_id, source) and 1% near-duplicates (same external_event_id within 2s timestamp jitter) When the deduplication step runs Then the unique stored event count equals the ground-truth unique count within 0.01% And reprocessing the identical batch yields no change to stored counts (idempotent) And discarded duplicates are logged with dedup_reason and count metrics exported
Time Zone Alignment & DST-Safe Windows
Given a merchant with timezone America/Los_Angeles and a DST transition week When querying per-variant metrics for the last 7 calendar days and for the last 1,000 impressions window Then window boundaries align to merchant local midnight and impression-threshold windows respect event_time ordering in merchant timezone And DST days (23h/25h) do not lose or double-count events; totals match UTC-backed raw events within 0.01% And the rule-eval query returns in <= 300 ms p95 for 10k variants on warmed cache
Data Quality Monitoring & Alerting
Given baseline event rates are computed from the trailing 14 days per connector and listing When ingestion volume drops by >30% for 15 consecutive minutes or a connector reports errors for >5 minutes Then a High severity alert is created within 10 minutes containing connector, marketplace, affected listings, suspected cause, and last-seen timestamp And duplicate alerts are suppressed via a 30-minute dedupe window And the alert auto-resolves when volumes recover to within 10% of baseline for 30 minutes, with resolution note logged
Backfill & Gap Repair
Given a known outage created a data gap from 2025-09-01T00:00Z to 2025-09-03T12:00Z When a backfill job is run for the last 30 days against the marketplace API Then missing windows are filled without double-counting previously ingested events And backfill throughput sustains >= 5,000 events/second and completes within the configured time window And the job is resumable; rerunning the same backfill is idempotent and produces identical aggregates And a backfill report is stored with rows_fetched, rows_deduped, rows_inserted, duration, and any errors
Auto-Promotion Orchestrator
"As a seller, I want the winning image to go live automatically where allowed so that I can iterate faster without manual uploads."
Description

An execution service that, upon rule satisfaction, assembles the winning image package, validates marketplace constraints (dimensions, file size, background requirements), and pushes updates via existing integrations using idempotent, rate-limited operations. Tracks propagation states per marketplace, retries with exponential backoff, and confirms success before marking a promotion complete. Provides per-listing configuration for where auto-promote is allowed.

Acceptance Criteria
Rule-Satisfied Package Assembly and Validation
Given a listing has a winning image variant determined by Winner Auto‑Promote rules and a target marketplace profile When the Auto‑Promotion Orchestrator starts execution Then it assembles a package containing only the chosen images and required metadata for that marketplace And validates each asset against marketplace constraints for dimensions, file size, format, and background requirements And rejects the promotion if any asset fails validation, recording a validation error per failed asset And proceeds to push only if all assets pass validation
Per-Listing Marketplace Allowlist Enforcement
Given a listing has an auto‑promote allowlist specifying target marketplaces When the orchestrator prepares to promote Then it attempts promotion only to marketplaces on the allowlist And skips all other marketplaces without calling their integrations And records a "skipped_not_allowed" status per non‑allowed marketplace
Idempotent, Rate‑Limited Promotion Push
Given a promotion has a unique Promotion ID When the orchestrator retries or receives duplicate trigger events with the same Promotion ID Then the push operation is idempotent and results in at most one live change per marketplace And subsequent identical requests return success with a logged "no_op" outcome without re‑uploading assets And per‑marketplace API calls are rate‑limited to configured thresholds with queuing to avoid 429 errors And if the marketplace returns 429, the orchestrator backs off and reschedules without marking the promotion failed
Per-Marketplace Propagation Tracking and Confirmation
Given promotions are pushed to multiple marketplaces When a push is initiated Then the orchestrator records a propagation state per marketplace: queued, pushing, awaiting_confirmation, confirmed, failed And it polls or listens for completion signals until confirmation or a configurable timeout And the promotion is marked complete only when all allowed marketplaces reach confirmed, or marked partial/failed with reasons when any marketplace fails or times out And timestamps and correlation IDs are stored for each state transition
Exponential Backoff Retry Strategy
Given a transient error occurs (e.g., 5xx, timeout, 429) When retrying the push Then the orchestrator applies exponential backoff with jitter from a base delay up to a max delay, for a maximum of N attempts per marketplace (configurable) And retries are logged with attempt count, next retry time, and error code And on permanent errors (e.g., 4xx validation/authorization), retries stop immediately and the promotion is marked failed for that marketplace
One‑Click Rollback and Audit Logging
Given a promotion was applied to one or more marketplaces When a user clicks Rollback for that promotion Then the orchestrator restores the previous image package on all marketplaces that were changed And the rollback is idempotent; repeated rollbacks do not create additional changes And partial rollback failures trigger retries with the same backoff policy and surface a final status per marketplace And an audit log entry is created containing promotion ID, actor, action (promote/rollback), marketplaces affected, timestamps, and before/after asset version identifiers
One‑Click Rollback
"As a seller, I want to revert a promotion instantly if results dip so that I can minimize revenue risk."
Description

Versioned snapshots of pre-promotion image sets and metadata with a single-action rollback that restores the previous state across all targeted marketplaces. Ensures parity and atomicity of rollback operations, blocks reversal if prior assets now violate marketplace policies, and records the rollback as a new versioned change for traceability. Provides a rollback window configuration and idempotent re-apply.

Acceptance Criteria
Atomic rollback restores prior state across all marketplaces
Given a promotion version v2 created from snapshot v1 is live across N connected marketplaces for M listings and all integrations are healthy When the user triggers One‑Click Rollback within the allowed window Then the system restores all images and listing metadata (titles, tags, alt‑text, marketplace‑specific fields) exactly to snapshot v1 for all M listings on all N marketplaces And the operation is atomic: if any marketplace update fails, no marketplaces are changed and the system reverts any provisional changes before completion And post‑rollback verification confirms asset checksums and metadata hashes match v1 for 100% of listings And a new version v3 is created representing the rollback and linked to v2 And the user receives a success summary with marketplacesUpdated=N, listingsUpdated=M, failures=0, operationId, and completion timestamp
Rollback blocked when prior assets violate current marketplace policies
Given snapshot v1 contains at least one asset or metadata value that breaches a target marketplace’s current policy When the user triggers One‑Click Rollback Then the system validates v1 against each marketplace’s current policies before applying any change And if any violation is found, rollback is blocked for all marketplaces And the user sees a per‑marketplace error list including policy code, offending field/asset, and remediation guidance And no live assets or metadata are modified And an audit entry "Rollback Blocked" is recorded with reason codes and marketplace IDs
Versioning and audit trail recorded for rollback with re‑apply linkage
Given a rollback operation completes successfully When the user opens the Change Log for the affected listing group Then a new version entry exists with type=rollback, fromVersion=v2, toVersion=v1, newVersionId=v3, actor, timestamp, and operationId And the entry contains a field‑level diff and asset hash comparisons between v2 and v1 And a one‑click "Re‑apply v2" action is available When "Re‑apply v2" is executed multiple times or retried with the same idempotency key Then only a single state transition occurs back to v2 and no duplicate versions are created; subsequent attempts return the same operationId and status
Configurable rollback window enforced in UI and API
Given workspace rollbackWindowDays=7 (configurable 1–30 days) When now − promotionTimestamp <= rollbackWindowDays Then the Rollback action is enabled in UI and API When now − promotionTimestamp > rollbackWindowDays Then the Rollback action is disabled in UI with tooltip "Rollback window expired" and the API returns 403 with code=ROLLBACK_WINDOW_EXPIRED And admins can update rollbackWindowDays within 1–30 and the change takes effect on new promotions within 5 minutes
Idempotent rollback execution and concurrency control
Given the UI and API send an Idempotency‑Key per rollback request and a promotion for the same listings may be in flight When duplicate rollback requests with the same Idempotency‑Key are received within 10 minutes Then exactly one rollback is executed and all callers receive the same operationId and final status When a promotion deployment is currently in progress for the targeted listings Then the rollback request returns 409 CONFLICT with code=PROMOTION_IN_PROGRESS and no state changes are applied
Health checks prevent partial rollback when integrations are unhealthy
Given at least one targeted marketplace integration is unhealthy (disconnected token, revoked scope, or active rate limit) When the user triggers One‑Click Rollback Then a pre‑flight health check runs for all target marketplaces And if any marketplace is unhealthy, the rollback is aborted for all marketplaces before applying changes And the user sees a health report per marketplace with status, issue type, and actionable next steps (e.g., re‑authenticate, wait X seconds) And a "Retry Rollback" action is available and succeeds without partial changes once health checks pass
Safety Guards & Statistical Thresholds
"As a seller, I want protections that stop premature or risky changes so that my listings stay compliant and stable."
Description

Built-in safeguards that enforce minimum sample sizes, confidence thresholds, and cooldown periods before and after promotions. Detects metric drift, extreme anomalies, or data outages and pauses evaluations or promotions accordingly. Performs pre-publish compliance checks against marketplace image policies and allows per-listing overrides and global defaults to prevent premature or non-compliant changes.

Acceptance Criteria
Promotion Gated by Sample Size and Confidence
Given a listing has ≥2 image variants and Winner Auto‑Promote is enabled When the configured minimum sample size thresholds are met (e.g., impressions ≥ min_impressions and/or conversions ≥ min_conversions) for the selected primary metric And the chosen statistical test yields confidence ≥ configured confidence_level for the winning variant versus control Then the variant is marked Eligible for Promotion and the candidate, sample sizes, effect size, p/credibility value, and confidence are logged Given thresholds are not met When an evaluation cycle runs Then no variant is marked eligible and no promotion is scheduled Given a variant becomes Eligible for Promotion and integration supports publishing When the next scheduler run occurs Then promotion executes at most once per cycle and writes an immutable audit entry with listing ID, variant IDs, metric used, thresholds applied, timestamp, and actor=system
Pre‑ and Post‑Promotion Cooldowns Enforced
Given global defaults define pre_promotion_cooldown and post_promotion_cooldown (in hours) and a listing may override them When a test starts for a listing Then no promotion may occur until now − test_start_time ≥ pre_promotion_cooldown Given a promotion has occurred When now − last_promotion_time < post_promotion_cooldown Then further promotions for that listing are blocked and evaluations may run but cannot schedule a promotion Given a manual rollback occurs When cooldowns are active Then rollback is allowed but triggers/reset cooldown rules as configured (e.g., post_promotion_cooldown restarts) and all actions are logged
Metric Drift and Anomaly Pause
Given drift detection is enabled with thresholds (e.g., z_score_threshold, jump_percent_threshold, min_window_hours) When CTR or conversion rate for any variant deviates from its 7‑day baseline by z‑score ≥ z_score_threshold OR changes by ≥ jump_percent_threshold within min_window_hours Then the system sets evaluation_status=Paused_Drift and blocks promotions for the affected listing(s) Given drift pause is active When metrics return within thresholds for a sustained period ≥ recovery_window_hours Then evaluations auto‑resume and the pause/resume events are logged with diagnostics (baseline window, observed z, observed % change)
Data Freshness and Outage Safeguard
Given data ingestion provides per‑listing freshness timestamps and completeness ratios When last_ingest_age > max_staleness OR completeness < min_completeness Then evaluations for affected listings are skipped and promotions are blocked with status=Paused_Outage Given an outage resolves When backfill completes and freshness/completeness return to within thresholds for ≥ stabilization_window Then evaluations resume and a recovery audit event is written Given evaluations are paused due to outage When a user attempts manual promote Then the UI/API returns 409 Conflict with reason=Data_Outage unless user has Force permission and acknowledges override (which is logged)
Marketplace Compliance Pre‑Publish Gate
Given marketplace‑specific image policy checks are configured (dimensions, background, watermark/text overlays, file size/type, prohibited content flags) When a variant is Eligible for Promotion Then PixelLift runs compliance checks for the target marketplace(s) before publish Given any Hard‑Fail policy is violated (e.g., min dimension, prohibited overlays) When checks run Then promotion is blocked, the failing rules are displayed via API/UI, and an audit entry captures rule IDs and evidence Given only Soft‑Fail warnings are present (e.g., recommended aspect ratio) When a user with permission chooses Override per listing Then publish proceeds and the override scope, user, and expiry are recorded
Global Defaults and Per‑Listing Overrides
Given global defaults exist for min_impressions, min_conversions, confidence_level, cooldowns, and detection thresholds When a listing defines overrides within allowed ranges Then the effective rule set for that listing uses overrides; otherwise it inherits globals Given an override is outside allowed bounds or internally inconsistent (e.g., min_impressions < 1) When the rule is saved Then validation fails with actionable errors and no change is applied Given evaluations run When computing eligibility Then the system records the effective rule values used in the audit log for traceability
One‑Click Rollback with Guardrails and Audit
Given a promotion has changed the live image package When a user triggers One‑Click Rollback within retention_window Then the previous state is restored within SLA ≤ 60s, a post‑rollback cooldown is enforced, and auto‑promote is suspended for the listing until cooldown expires (unless overridden) Given a rollback occurs When audits are queried Then the system shows linked entries for the promotion and rollback with correlation IDs, actor, timestamps, and before/after assets and rules
Change Logging & Audit Trail
"As a seller, I want a clear record of what changed and why so that I can audit performance and resolve issues quickly."
Description

Comprehensive, immutable logging of evaluations, decisions, promotions, and rollbacks including timestamps, rule versions, metric snapshots, before/after image hashes, initiating user or system actor, and integration API responses. Provides searchable history, export in CSV/JSON, and outbound webhooks for BI pipelines. Implements append-only storage with retention policies and PII-safe redaction.

Acceptance Criteria
Evaluation and Promotion Decision Logged with Full Context
Given a Winner Auto‑Promote rule is evaluated for a listing When the evaluation completes, regardless of outcome Then the system writes an immutable audit record with: eventType in ['evaluation','promotion' if a change is applied], decisionId (UUIDv4), timestamp (ISO 8601 UTC), tenantId, listingId, ruleId, ruleVersion, metricSnapshot {impressions, ctr, conversions, conversionRate, windowStart, windowEnd}, previousWinnerVariantId, evaluatedVariantIds, selectedVariantId (nullable), beforeImageHash (SHA-256), afterImageHash (SHA-256, equals beforeImageHash when no change), actor {type: 'system'|'user', id}, correlationId And the record includes integration details only when a promotion is executed: {name, endpoint, requestId, httpStatus, responseTimeMs, errorMessage?} And the record is assigned a monotonically increasing sequenceNumber and a tamper-detection checksum And the evaluation (and any promotion) is committed atomically with the audit write; on failure, no state change occurs and an 'error' audit record is emitted referencing the failed decisionId And the record is durably persisted within 1s at p95 and is retrievable by decisionId and sequenceNumber
Rollback Logged and Linked to Original Decision
Given a user with rollback permission initiates one‑click rollback for a specific promotion decision When the rollback is executed Then the system writes an immutable audit record with: eventType='rollback', rollbackId (UUIDv4), timestamp (ISO 8601 UTC), tenantId, listingId, actor.userId, reason, originalDecisionId, restoredVariantId, supersededVariantId, integration {name, endpoint, requestId, httpStatus, responseTimeMs, errorMessage?}, outcome in ['success','failure'], correlationId And the rollback state change and audit write are atomic And the rollback record is discoverable from the original promotion via originalDecisionId linkage And idempotency ensures repeated rollback requests with the same originalDecisionId do not create duplicate actions or records
Searchable History with Filters, Sorting, and Pagination
Given a tenant has >= 1,000,000 audit records in the last 13 months When a user searches using any combination of filters: date range, eventType, listingId, ruleId, ruleVersion, actor.id, integration.name, outcome, correlationId, imageHash, decisionId Then the first page (<= 200 records) returns within 3s p95 And results are sorted by timestamp desc by default, with optional asc And pagination is cursor-based and stable across concurrent writes, providing next/prev cursors And keyword search across errorMessage and changeReason supports case-insensitive exact phrase matching And users can only see records for their tenant
CSV/JSON Export of Log History
Given a user requests export of the current filtered audit results When the result set size N <= 100,000 Then a CSV or JSON file is generated synchronously within 60s and begins download And when N > 100,000 Then the export runs asynchronously and a signed URL is provided via notification upon completion And exported files include all fields shown in UI, ISO 8601 timestamps, UTF-8 encoding, applied redactions, and an accompanying checksum file And a maximum export size of 2 GB is enforced with a clear error if exceeded And exports preserve filter criteria and sort order
Outbound Webhooks for Audit Events with Delivery Guarantees
Given a tenant has configured a webhook endpoint and shared secret When a 'promotion' or 'rollback' audit record is created Then the system POSTs a JSON payload containing the audit record, eventId, eventType, createdAt, tenantId, with an HMAC-SHA256 signature in 'X-PixelLift-Signature' And deliveries are idempotent using 'Idempotency-Key' == eventId And on 2xx responses, delivery is marked successful; on 4xx, no retries; on 5xx or network errors, retry with exponential backoff and jitter up to 10 attempts within 24h, else send to dead-letter queue And events for the same listing are delivered in order And p95 delivery latency for first attempt is <= 5s
PII‑Safe Redaction in Logs, Exports, and Webhooks
Given audit data may include PII from integrations or users When audit records are stored, displayed, exported, or sent via webhooks Then PII fields (email, phone, full name, street address, IP) are redacted or tokenized before persistence and cannot be reconstructed And non-PII identifiers (tenantId, userId, requestId) are preserved And free-text payloads are scanned and masked for PII patterns prior to storage And search does not allow querying by raw PII values And each record stores redactionVersion to indicate the policy applied
Append‑Only Storage and Retention Policy Enforcement
Given audit storage is append‑only with a tenant‑configurable retention period (default 13 months) When any attempt is made to update or delete an existing audit record via APIs, UI, or direct storage access Then the operation is rejected and a 'mutation_attempt' audit record is written with actor and reason And only the retention job may purge records older than the retention threshold, creating a 'purge' audit record with counts and date range And searches and exports exclude purged records after completion And read-time checksum verification detects tampering; on mismatch, the record is not returned and an 'integrity_violation' audit event is written
Notifications & Approval Workflow
"As a seller, I want to be notified and optionally approve changes so that I stay in control of critical listings."
Description

Configurable notifications via in-app alerts, email, Slack, and webhooks for winner detection, pending approvals, successful promotions, and rollbacks. Supports optional manual approval gates per listing or marketplace with SLA reminders, role-based permissions, and deep links to change diffs and metrics snapshots. Captures approver identity and rationale for the audit trail.

Acceptance Criteria
Winner Detected — Multi-Channel Notification Delivery
Given winner rules are configured for a listing and notification channel preferences are set When a variant meets a configured win rule and the system marks it as winner_detected Then the system sends notifications to in-app, email, Slack, and POSTs a webhook to subscribed endpoints, respecting channel opt-in settings And each notification payload includes: event_type, tenant_id, listing_id, marketplace, winning_variant_id, rule_matched, metric_values, detection_window, detected_at (UTC), deep_link_diff_url, deep_link_metrics_url, correlation_id And deliveries occur within 2 minutes of detection for in-app, email, Slack, and the first webhook attempt And duplicate notifications with the same correlation_id are suppressed within a 5-minute window And a notification log is recorded per channel with delivery status and response metadata (for webhooks)
Manual Approval Gate — Hold and Approve Promotion
Given manual approval is enabled for the listing or marketplace When a winner is detected Then the listing remains in Pending Approval and no auto-promotion occurs And an approval panel is accessible via deep links in the notifications And the panel displays change diff (before/after assets), metrics snapshot, rule matched, and estimated impact And an authorized user can Approve or Reject; Approve triggers promotion; Reject archives the candidate And on Approve/Reject, the system captures approver user_id, role, timestamp (UTC), action, and rationale (required, 10–500 chars) in the audit trail And watchers receive a decision confirmation via configured channels
SLA Reminders — Pending Approval Escalation
Given an item is Pending Approval with an SLA duration configured (e.g., 24 hours) and designated approver group When the SLA duration elapses without a decision Then the system sends SLA reminder notifications across configured channels to the designated approvers And if still unresolved after the escalation threshold (e.g., +24 hours), the system escalates to the fallback approver group And reminders stop immediately upon Approve or Reject And all reminders and escalations are logged with timestamps and recipient lists
Role-Based Permissions — Approval Access Control
Given role permissions are defined so that Owner and Approver can approve/reject and Viewer cannot When a user without permission attempts to approve or reject via UI or API Then the action is blocked with 403 Forbidden and a non-destructive UI message; the item state remains unchanged And the attempt is recorded in the audit log with user_id, timestamp (UTC), attempted action, and source (UI/API) And users with appropriate roles can successfully approve/reject, with their actions similarly logged
Promotion Outcomes and One-Click Rollback Notifications
Given a promotion is initiated by auto-promotion or manual approval When promotion completes successfully via marketplace integration(s) Then a success notification is sent across channels including marketplace confirmation IDs, go_live_at (UTC), and a deep link to the live listing And the audit trail records the promotion actor (system or user), request/response payloads, and integration IDs When promotion fails Then a failure notification is sent including error codes/messages and a retry action link And a one-click rollback control is available from the notification deep link and audit UI When rollback is executed Then the original image package is restored, listing status is reverted, a rollback notification is sent, and rationale (required) plus actor are captured in the audit trail
Webhooks — Security, Reliability, and Idempotency
Given a tenant has configured a webhook endpoint and HMAC secret When events are emitted (winner_detected, pending_approval, promotion_success, promotion_failure, rollback_success) Then each webhook request includes X-Signature (HMAC-SHA256 over the body), X-Timestamp, event_id, and idempotency_key headers/fields And deliveries use at-least-once semantics with exponential backoff for up to 10 attempts or 24 hours until a 2xx is received And duplicate deliveries reuse the same idempotency_key to allow receiver de-duplication And the dashboard displays per-event delivery status (success, retrying, failed) and last_error And if all retries fail, an in-app/email alert is sent to tenant admins

Sample Guard

Get instant sample‑size and duration estimates per SKU based on your traffic and chosen metrics. Live progress bars flag underpowered tests and recommend extensions or split tweaks, so you stop calling winners too early and avoid wasting time on inconclusive experiments.

Requirements

Per-SKU Experiment Setup & Metric Selection
"As an e-commerce seller, I want to configure the goal metric and assumptions for each SKU test so that estimates match my traffic and business context."
Description

Provide a setup flow to create experiments per SKU that links PixelLift-generated image variants to marketplace listings, select a primary success metric (e.g., CTR, conversion rate), define baseline rate (auto-suggested from historical data), minimum detectable effect, desired power, significance level, number of variants, and initial traffic split. Validate inputs, surface historical metric ranges for guidance, and allow saved presets per marketplace. Persist experiment configurations with versioning and permission controls so teams can reuse and audit settings across SKUs.

Acceptance Criteria
Link PixelLift Variants to SKU Listing
Given a user selects a SKU and PixelLift-generated image variants exist When the user starts an experiment and maps variants to the SKU Then the system shall require at least 2 variants and at most 10 And the system shall validate each variant meets marketplace image requirements (dimensions, format, size) And the system shall display a preview of each mapped variant with the target listing And the mapping shall persist and be retrievable on experiment reload And if a variant is missing or invalid, the system shall block progression with inline errors
Primary Metric Selection and Baseline Auto-Suggest
Given a user selects a marketplace and SKU in experiment setup When the user chooses a primary metric from the supported set (CTR, Conversion Rate) Then the system shall auto-suggest a baseline rate using the last 90 days of SKU data (fallback to 60/30 if insufficient) And the baseline value shall be displayed with source window and sample size And the user may override the baseline within 0–100% (or 0–1) with format validation and unit consistency And the system shall store the chosen metric and baseline with the experiment
MDE, Power, Alpha, Variants Validation and Feasibility Check
Given a user inputs Minimum Detectable Effect, desired power, significance level, and number of variants When the user submits the configuration Then the system shall validate ranges: MDE 1–50% (or absolute if metric <1%), power 0.7–0.99, alpha 0.001–0.1, variants 2–10 And the system shall compute required sample size per variant and estimated duration using recent traffic for the SKU And if estimated duration exceeds a configurable threshold (e.g., 8 weeks), the system shall flag underpowered with recommendations (increase duration, reduce variants, increase MDE) And blocking errors prevent save on invalid ranges; warnings allow save with acknowledgment
Initial Traffic Split Configuration
Given number of variants N is set When the user configures initial traffic allocation Then the system shall enforce allocations sum to 100% And each variant shall have a minimum allocation of 5% unless excluded And the system shall provide a one-click equalize option to distribute traffic 100/N And if variants change, the system shall prompt to re-balance or auto-normalize existing allocations And save is blocked until allocations are valid
Surface Historical Metric Ranges for Guidance
Given a user has selected the primary metric and marketplace When the guidance panel is opened in setup Then the system shall display SKU-level metric ranges for 30/60/90 days (min, median, max, sample size) And data freshness timestamp and source shall be shown And if data is insufficient (<200 impressions for CTR or <50 conversions for Conversion Rate), the panel shall indicate Insufficient Data and suggest using marketplace-level benchmarks And the user can copy a suggested baseline value from the panel with one click
Saved Presets per Marketplace
Given a user with create permissions is in setup When the user saves current configuration as a preset with a unique name at the marketplace scope Then the preset shall capture: metric, baseline mode (auto/manual), MDE, power, alpha, default number of variants, default traffic split, and guidance settings And the preset shall be available for all SKUs in that marketplace for users with access And applying a preset to a SKU shall populate fields instantly and log the preset version used And preset names shall be validated (3–50 chars, unique per marketplace) And editing a preset shall create a new preset version; existing experiments retain the original version
Persist Configuration with Versioning, Permissions, and Reuse
Given a user saves an experiment configuration When the save completes Then the system shall create an immutable version (v1, v2, …) with timestamp, author, and diff from prior version And only users with Editor or Admin role may create new versions; Viewers have read-only access And the audit log shall record who, what fields changed, and when for each version And a user may clone a saved configuration to another SKU within the same marketplace, preserving references and creating a new version context And attempts to edit without permission shall be blocked with an authorization error and no version change
Sample Size & Duration Calculator
"As a seller, I want instant sample size and time estimates so that I can plan how long to run my image test before calling a winner."
Description

Implement a calculation service that derives required per-variant sample sizes and estimated calendar duration using the configured metric, baseline, minimum detectable effect, power, and alpha. Support proportion tests (CTR/CR) via standard fixed-horizon formulas with continuity correction and guardrails for low base rates, and compute duration from rolling 7-day average traffic per SKU and channel. Output per-variant sample targets, daily progress targets, and confidence bounds. Expose results via UI and API, cache for performance, and recalc on parameter or traffic changes.

Acceptance Criteria
CTR/CR Proportion Sample Size (Fixed-Horizon, CC)
Given metric=proportion, baseline=0.05, MDE.absolute=0.01, alpha=0.05 (two-sided), power=0.80, continuityCorrection=true, allocation=50/50, variants=2 When the calculator computes per-variant sample size Then perVariantSampleTarget equals the result of R power.prop.test(correct=TRUE) within ±1% tolerance and is rounded up to the next integer And if MDE.relative is provided (e.g., 20%), it is converted to absolute before calculation and yields the same perVariantSampleTarget as providing the equivalent absolute MDE And the response echoes parameters {baseline, MDE.absolute, alpha, power, continuityCorrectionApplied=true}
Duration Estimate from 7-Day Avg Traffic
Given rolling7DayAvgTrafficPerSKUChannel=2000 sessions/day, allocation=50/50, perVariantDailyExposures=1000, perVariantSampleTarget=n When the calculator estimates duration Then durationDaysEstimate equals ceil(n / 1000) And changing allocation to 60/40 updates perVariantDailyExposures to 1200/800 and the recomputed durations equal ceil(n / 1200) and ceil(n / 800) respectively And the estimate updates within 60 seconds when the rolling7DayAvgTraffic changes by ≥1%
Low Base Rate Guardrails and Underpowered Flag
Given metric=proportion and (baseline < 0.005 or expectedDailyEventsPerVariant < 5) When the calculator runs Then guardrail.triggered=true with guardrail.code in {LOW_BASE_RATE, LOW_EVENT_VOLUME} And underpowered=true if durationDaysEstimate > maxTestDurationDays (default 60) based on current traffic and allocation And the response includes recommendedAdjustments with at least one of {increaseMDERelative, extendDuration, rebalanceSplit}, each with a concrete suggested value
Output Fields: Sample Targets, Daily Targets, Confidence Bounds
Given a valid request When the calculator returns results Then the API JSON includes fields {perVariantSampleTarget:int, dailyPerVariantTarget:int, durationDaysEstimate:int, durationDaysCI:{low:int, high:int}, confidenceLevel:float, channel:string, sku:string} And dailyPerVariantTarget equals ceil(perVariantSampleTarget / durationDaysEstimate) And durationDaysCI.low/high are derived from the empirical 7-day traffic distribution percentiles (P90 for low, P10 for high); if daily traffic is constant across 7 days, durationDaysCI.low == durationDaysCI.high == durationDaysEstimate
UI and API Consistency
Given the API returns calculation results for SKU S and channel C When the UI renders the calculator panel for the same inputs Then values for per-variant sample size, estimated duration, daily per-variant targets, and confidence bounds match the API within display rounding rules (sample size: integer up, duration: integer days up, daily target: integer up) And any guardrail/underpowered flags and recommended adjustments are displayed with the same codes and suggested values
Caching and Recalculation on Input/Traffic Changes
Given a request with identical inputs repeated within 10 minutes and no change in rolling7DayAvgTraffic When the calculator is invoked Then the response has cacheStatus="HIT", computedAt unchanged, and p95 latency ≤ 100 ms And when any of {baseline, MDE, alpha, power, allocation, rolling7DayAvgTraffic} changes Then the next response has cacheStatus="MISS", a new computedAt timestamp, and reflects updated values within 60 seconds of the change being recorded
Live Progress Tracker & Power Meter
"As a seller, I want to see live progress and power for my running tests so that I know whether I’m close to statistically valid results."
Description

Build real-time progress bars per SKU and variant comparing observed events (impressions, clicks, conversions) to required sample targets, with computed achieved power and projected time-to-completion. Include color-coded states (on track, at risk, underpowered), last-updated timestamps, and per-channel breakdowns. Handle multi-variant tests, missing data, and delayed reporting from marketplaces. Auto-refresh at configurable intervals, and maintain a resilient state store for partial updates and backfills.

Acceptance Criteria
Real-Time Progress Bars by SKU & Variant
Given a SKU with 2–12 active variants and required sample targets for impressions, clicks, and conversions per variant, when new event counts are ingested or a refresh occurs, then each variant renders a progress bar per metric showing Observed/Required and percentage (0.0–100.0%) rounded to one decimal place. Given Observed >= Required for a metric, when rendering the progress bar, then the bar caps at 100% and a "Target met" pill is shown for that metric. Given a variant is added to an ongoing test, when the next refresh cycle completes, then the variant appears with zeroed progress and begins updating without affecting historical totals of existing variants. Given a variant is paused or removed, when the next refresh cycle completes, then it is excluded from aggregate ETA and power projections but remains visible with a "Paused" label and its final counts locked. When a manual refresh is initiated, then progress bars update within 5 seconds.
Achieved Power & Color-Coded State Logic
Given target power is configured and projections are available, when computing status at the SKU and variant level, then: - If projected power at planned end time >= target power AND projected ETA <= planned end time, status = On Track (green). - If projected power at planned end time is within [target power − 5 pp, target power) OR projected ETA exceeds planned end time by ≤ 20%, status = At Risk (amber). - If projected power at planned end time < target power − 5 pp OR projected ETA exceeds planned end time by > 20% OR data gap > 24 hours, status = Underpowered (red). Achieved power is displayed as a percentage with one decimal place and updates each refresh cycle; values must be numeric (never NaN/blank). Status badges are consistent between list and detail views for the same SKU/variant within a refresh cycle.
Projected Time-to-Completion (ETA) Calculation & Display
Given remaining required samples and observed event rates, when computing ETA, then ETA = remaining_required / expected_rate and is displayed as both a duration (e.g., "18h") and a target timestamp in the user's timezone. Given last-24h event rate < 50 impressions for a channel, when computing ETA, then the system falls back to the last-7d average rate; if both are insufficient (< 50), ETA displays "Cannot project" and contributes 'Underpowered' to status evaluation. When new data arrives or a refresh occurs, then ETA recalculates within the next refresh cycle and never increases by more than 25% without a corresponding decline in observed rate. Channels flagged as delayed are excluded from ETA until data resumes or backfills arrive.
Per-Channel Breakdown, Aggregation, and Delayed Reporting
For each active channel, when rendering breakdown, then impressions, clicks, and conversions counts and progress bars are shown per channel and per variant. When computing aggregate counts for a SKU/variant, then aggregate Observed = sum of channel Observed and aggregate Required = sum of channel Required; aggregate percentage is recomputed from aggregates (not averaged). Given a channel's last-updated timestamp exceeds 15 minutes, when rendering, then the channel shows a 'Delayed' badge and grey-hatched bar and is excluded from ETA and power projections until new data or backfill arrives. When delayed backfill data arrives out of order, then per-channel and aggregate counts reconcile to the correct totals within two refresh cycles.
Last-Updated Timestamps & Staleness Indicators
Each SKU/variant/channel tile displays a "Last updated" timestamp in ISO 8601 format using the user's timezone. When no updates have been processed for a tile for > 15 minutes, then the tile shows a 'Data delayed' tooltip and a staleness icon; when updates resume, the indicators are removed on the next refresh. Timestamps are monotonic non-decreasing per tile and reflect processing time; client display uses server-supplied timestamps and handles clock skew without showing negative ages.
Configurable Auto-Refresh & Manual Refresh Behavior
Given a global refresh interval of N seconds and a per-SKU override of M seconds, when enabled, then the system refreshes data for that SKU every M ± 5 seconds and all others every N ± 5 seconds. When auto-refresh is toggled off, then no background refreshes occur; manual refresh remains available. When a manual refresh is triggered, then visible tiles update within 5 seconds without resetting filters, sort order, scroll position, or expanded rows. Concurrent refreshes do not produce duplicate rows or flicker; partial responses render progressively and finalize within one additional refresh cycle.
Resilient State Store: Partial Updates, Backfills, and Idempotency
Given duplicate event payloads for the same channel/metric/time window, when processed, then stored counts remain unchanged (idempotent). Given partial updates (e.g., impressions only) are received, when rendering aggregate metrics, then unaffected metrics retain their prior values and a 'Partial' badge is shown until all metrics are present. Given out-of-order backfills for prior dates, when processed, then aggregates recalculate and UI reflects corrected counts, power, status, and ETA within two refresh cycles. After a process restart, the system restores state and reconciles with the source so that counts match the source-of-truth within two refresh cycles with no data loss or double counting.
Recommendation Engine for Extensions & Split Tweaks
"As a seller, I want clear, data-backed recommendations on extending or tweaking my test so that I don’t waste time on inconclusive experiments."
Description

Create a rules-and-model-driven component that evaluates current variance, allocation, and traffic to recommend extending duration, adjusting traffic splits, or increasing the minimum detectable effect when tests risk being inconclusive. Provide explainable reasons, impact estimates (added days to reach target power), and one-click actions to apply new splits where supported or generate step-by-step instructions otherwise. Log all recommendations and actions for auditability and learning.

Acceptance Criteria
Underpowered Test: Extension Recommendation with Impact Estimate
Given an active A/B test with target power and MDE configured And accumulated samples and per-variant daily eligible traffic are known And current computed power is below the target power When the engine evaluates the test Then it recommends "Extend Duration" and displays current power, target power, required samples per variant, accumulated samples, and an addedDays estimate = ceil((requiredSamples - accumulatedSamples) / dailyEligibleTraffic) And the recommendation includes a plain-language reason referencing variance, allocation, and traffic And the addedDays estimate is rounded up to whole days and never negative
Allocation Inefficiency: Split Tweak Proposal with Time Saved
Given an active test where current allocation deviates from the time-to-power optimal allocation under current variance estimates Or any variant is below the minimum exposure threshold When the engine evaluates the test Then it recommends "Adjust Split" with proposed percentages that sum to 100% and each >= the configured minimum exposure And it displays projected time-to-power before vs after and expected days saved (>= 0) And the proposed split respects platform allocation granularity limits (e.g., nearest 1%)
Excessive Duration Forecast: Suggest Increasing MDE
Given a test whose projected time to reach target power at the current MDE exceeds the user-defined maximum duration or target end date When the engine evaluates the test Then it recommends "Increase MDE" with the smallest MDE that fits within the maximum duration window And it displays the trade-offs: new MDE, new projected duration, and the difference from the current plan And it requires explicit user confirmation acknowledging the sensitivity trade-off before applying
Actionability: One-Click Apply or Step-by-Step Instructions
Given a recommendation with an actionable split tweak When the user clicks "Apply" Then if a connected allocator supports programmatic split updates, the new split is applied within 60 seconds and the UI confirms with effective timestamp and new percentages Else the system generates step-by-step instructions listing exact fields and values to change, including current and target splits, and provides copy/export options And any failure surfaces a clear error and performs no partial updates; the prior allocation remains unchanged
Explainability: Reasons, Inputs, and Method Disclosure
Given any recommendation When it is rendered in the UI or returned via API Then it includes an explanation payload with: decision type, top drivers (at least two) with quantitative values (e.g., current power %, variance ratio, daily traffic), method name used for projections, and the input parameters And the human-readable summary is <= 240 characters and avoids unexplained jargon And the explanation is consistent with the displayed numeric estimates
Auditability: Recommendation and Action Logging with Export
Given any recommendation generation or action application When the event occurs Then a log entry is persisted with: UTC timestamp, test/SKU ID, user ID (or system), recommendation type, inputs (traffic, allocation, variance, MDE, target power), proposed change, user response (accepted/ignored/applied), outcome status, and impact estimates (e.g., addedDays or daysSaved) And logs are immutable and filterable by date range, SKU, and recommendation type And logs are exportable to CSV and retrievable via API with pagination
Traffic & Event Data Integration
"As a seller, I want Sample Guard to pull accurate traffic and outcome events per variant so that estimates and progress reflect reality."
Description

Integrate with marketplace analytics APIs and PixelLift’s tracking to ingest per-SKU, per-variant impressions, clicks, and conversions, mapping events to PixelLift variant IDs. Support OAuth where required, schedule periodic pulls, backfill up to 30 days, and reconcile late-arriving data. Provide CSV import for sellers without API access. Implement deduplication, timezone normalization, and monitoring with alerts for data gaps or API failures. Expose a clean event schema used by calculators and dashboards.

Acceptance Criteria
OAuth Authentication & Token Refresh
Given a seller connects a marketplace that requires OAuth, When the seller completes the OAuth consent flow, Then the system stores access and refresh tokens encrypted at rest and associates them with the seller and marketplace. Given a valid refresh token, When the access token expires or has <=5 minutes remaining, Then the system automatically refreshes the token without user intervention and logs the event. Given the refresh attempt fails due to invalid_grant or revoked consent, When the next scheduled pull runs, Then the job is paused for that marketplace, the seller is notified within 5 minutes, and the integration status is set to "Action Required". Given multiple marketplaces are connected, When tokens are refreshed concurrently, Then no marketplace’s refresh blocks another and each refresh result is tracked separately.
Scheduled Pulls with Late-Arrival Reconciliation & Retries
Given an active integration, When the scheduler runs hourly by default, Then the system pulls events incrementally using a per-source watermark and processes them within 15 minutes of job start. Given API rate limit (HTTP 429) or transient network errors occur, When a pull fails, Then the system retries up to 3 times with exponential backoff (1m, 2m, 4m) and marks the attempt outcome. Given events arrive late for any of the last 72 hours, When they are received, Then the system upserts them, recomputes aggregates, and propagates deltas to calculators within 15 minutes. Given a marketplace supports webhooks for updates, When a webhook is received, Then the watermark is advanced appropriately and duplicate processing is avoided.
30-Day Backfill on Connection
Given a new marketplace connection is established, When backfill starts, Then the system requests and ingests up to the last 30 days of events per API limits and shows progress percentage. Given the marketplace restricts lookback to less than 30 days, When backfill executes, Then the system fetches the maximum allowable range and records the effective window used. Given a backfill job is interrupted, When it restarts, Then it resumes from the last successful watermark without re-importing previously ingested events. Given backfill completes, When calculators run, Then totals for the backfill window match the source within ±1% per metric or discrepancies are flagged with details.
Event Ingestion, Normalization to UTC, and Schema Exposure
Given events are pulled or imported, When processing, Then each event is validated to include source_marketplace, sku_id, pixelLift_variant_id, event_type ∈ {impression, click, conversion}, event_time_utc, source_event_id (nullable), and quantity/value fields as applicable. Given a source event timestamp and timezone offset, When storing, Then event_time_utc is computed and stored in ISO 8601 UTC, and source_timezone is preserved. Given calculators and dashboards query the event store, When accessing the events_v1 view, Then the documented schema is returned consistently with field names, types, and constraints and passes schema contract tests. Given an event fails validation, When processing, Then it is rejected, logged with a reason, and surfaced in an import error report without blocking valid events.
Deduplication & Idempotency Across API and CSV Sources
Given the same source_event_id is received more than once, When processing, Then exactly one event record exists downstream (idempotent upsert). Given a source does not provide a unique ID, When processing, Then a deterministic hash key is constructed from marketplace, sku_id, pixelLift_variant_id, event_type, event_time_utc (to the second), and value fields to prevent duplicates. Given an identical CSV row is uploaded after an API pull already ingested it, When processing, Then the CSV row is recognized as duplicate and skipped, with the skip counted in metrics. Given deduplication occurs, When reporting, Then dedupe rate metrics are emitted per source daily.
CSV Import Validation & Mapping
Given a seller lacks API access, When they upload a CSV using the provided template, Then the system validates headers, data types, and required fields and shows a preview with row-level errors. Given valid CSV rows contain seller_sku and variant mapping, When processing, Then rows are mapped to internal sku_id and pixelLift_variant_id using the seller’s mapping table or explicit columns. Given a CSV includes a timezone column or selected timezone, When processing, Then timestamps are converted to event_time_utc consistently. Given a CSV exceeds 200k rows, When uploaded, Then it is chunked and processed in parallel without exceeding resource limits and provides progress feedback.
Monitoring & Alerts for Data Gaps and API Failures
Given the system operates, When a scheduled pull fails or is more than 2 hours overdue per marketplace, Then an alert is sent to the seller’s notification channel and internal on-call within 5 minutes. Given daily expected volume baselines are established, When a data gap exceeds 30% below baseline over a rolling 6-hour window for any SKU/variant, Then a warning is raised with investigation guidance. Given an API returns sustained 4xx/5xx errors for 3 consecutive attempts, When detected, Then the integration status is set to Degraded and a status banner is shown in-app. Given monitoring is active, When viewing the data health dashboard, Then last pull time, lag in minutes, success/failure counts, dedupe counts, and gap alerts are displayed per source and downloadable as CSV.
Guardrails & Early Stop Prevention
"As a seller, I want protections against calling a winner too early so that my decisions remain statistically sound."
Description

Enforce configurable thresholds for minimum runtime, minimum sample counts per variant, and minimum achieved power before enabling the ‘Declare Winner’ action. Display blocking warnings when criteria are unmet, and support admin override with reason capture and audit logging. Provide per-account defaults and per-experiment overrides, ensuring consistent, statistically sound decision-making across teams and SKUs.

Acceptance Criteria
Minimum Runtime Guardrail
Given an experiment with startTime T0 and active status, and minRuntimeDays=7 is the active threshold When a non-admin user views the experiment before T0+7 days or attempts to click "Declare Winner" Then the "Declare Winner" action is disabled and a blocking warning states "Minimum runtime not met (X/Y days)" And the UI displays a runtime progress bar with current elapsed days and required days And a "blocked_winner_attempt" event is recorded on click with userId, experimentId, and unmetGuardrail="minRuntime" When the elapsed time reaches >= 7 days and all other active guardrails are satisfied Then the "Declare Winner" action becomes enabled
Per-Variant Sample Size Guardrail
Given active threshold minSamplesPerVariant=100 for the selected primary metric And variants included in analysis each have observed primary-metric sample counts {n1..nk} When any included variant has n < 100 Then the "Declare Winner" action is disabled and a blocking warning lists each under-threshold variant with "n/100" And the sample-size progress bars show per-variant progress percentages When all included variants have n >= 100 Then this guardrail passes and its warning is cleared
Power Threshold Guardrail
Given active requiredPower=0.80 and alpha configured for the experiment And the system computes achieved power for the observed effect on the chosen primary metric using the configured test method When achieved power < 0.80 Then the "Declare Winner" action is disabled and a blocking warning states "Underpowered (achieved X%, required 80%)" And the progress bar shows current power and an estimated additional samples or runtime needed to reach 80% When achieved power >= 0.80 and all other active guardrails are satisfied Then this guardrail passes and the action can proceed
Blocking Warnings UX & Summary
Given one or more guardrails are unmet When a user views the experiment details Then a single blocking banner appears summarizing unmet guardrails with current vs required values And each unmet guardrail links to details with progress bars and recommended next steps (extend runtime or adjust split) And the banner persists across sessions until all guardrails pass or an authorized override occurs
Admin Override With Reason & Audit Log
Given one or more guardrails are unmet and the current user has Admin permission When the user clicks "Override and Declare Winner" Then a modal requires a reason (minimum 10 characters) and an explicit acknowledgment checkbox And submitting records an immutable audit log with timestamp, userId, experimentId, guardrailStates, reason, and previous CTA state And the winner is declared and warnings are archived with an "overridden" tag When a non-admin attempts to declare a winner while guardrails are unmet Then override is not available and the action remains blocked
Account Defaults and Experiment Overrides
Given account-level defaults for minRuntimeDays, minSamplesPerVariant, and requiredPower exist When a new experiment is created Then those defaults populate the experiment’s guardrail settings When an authorized user sets per-experiment overrides Then the experiment uses the overrides immediately for gating and the changes are versioned and audit logged And a tooltip indicates which values are defaults versus overrides When overrides are removed Then the experiment reverts to account defaults without data loss
API and Bulk Operations Enforcement
Given a client triggers "Declare Winner" via API or a bulk operation When any active guardrail is unmet and no override flag is provided Then the request fails with HTTP 409 and a machine-readable list of unmet guardrails with current vs required values When an Admin includes override=true and provides a reason (minimum 10 characters) Then the request succeeds and an audit log entry is created with the same fields as UI overrides And the same validations apply to bulk operations, with per-experiment results returned

Results Sync

Pull performance data from supported marketplaces/ad dashboards or import a CSV. PixelLift auto‑maps metrics to variants via labels, consolidates CTR, impressions, and conversions, and updates the experiment log—producing clear, apples‑to‑apples comparisons without spreadsheet wrangling.

Requirements

Multi-Source Data Connectors
"As an e-commerce seller, I want to connect my marketplaces and ad accounts to PixelLift so that performance metrics are pulled automatically without manual exports."
Description

Implement secure, OAuth-based connectors for supported marketplaces and ad dashboards (e.g., Amazon, Etsy, eBay, Shopify, Walmart, Google Ads, Meta Ads) to ingest metrics including impressions, clicks, CTR, conversions, spend, revenue, listing/SKU/ASIN IDs, creative/ad IDs, campaign/group, and date. Normalize incoming data into a unified schema with UTC timestamps and standard field names, handle pagination and rate limits, support token refresh and credential rotation, and store credentials in an encrypted vault scoped to workspace. Provide selectable date ranges and filters, support incremental sync via cursors (since-last-sync), and ensure idempotent ingestion with duplicate protection. Include robust error handling, retries with backoff, partial batch handling, and detailed connector logs for observability.

Acceptance Criteria
OAuth setup and workspace-scoped credential vault
- Given a workspace admin in Workspace A initiates OAuth for Shopify When consent is granted Then access and refresh tokens are stored encrypted at rest in the credential vault scoped to Workspace A only. - Given a member of Workspace B When viewing connected accounts Then no credentials or metadata from Workspace A are visible or accessible. - Given any connector token will expire within 7 days When background token refresh runs Then tokens are refreshed successfully without user action and the previous token is revoked within 60 seconds. - Given an admin triggers credential rotation for a connector When rotation completes Then all subsequent syncs use the new credentials and logs record a rotation event with secrets redacted. - Given the OAuth flow fails or insufficient scopes are returned When the callback is processed Then no credentials are stored and the user sees a descriptive error including provider code and remediation.
Normalized metrics schema with UTC timestamps
- Given records ingested from Amazon, Google Ads, and Shopify for the same date When normalized Then each record contains fields: source, workspace_id, listing_id, sku, asin, creative_id, campaign_id, ad_group_id, date_utc (YYYY-MM-DD), ts_utc (ISO 8601), impressions, clicks, ctr, conversions, spend, spend_currency, revenue, revenue_currency. - Given source timestamps in varying timezones When processed Then date_utc and ts_utc are converted to UTC with no off-by-one-day errors at boundaries. - Given a metric is missing from a source (e.g., revenue) When normalized Then the field is set to null and marked missing in the record metadata; no zero is substituted. - Given impressions and clicks are present When normalized Then ctr equals clicks/impressions rounded to 4 decimal places. - Given validation runs on each record When a record fails schema Then it is rejected to a quarantine store with a reason code; valid records proceed.
Pagination, rate limits, and complete retrieval for date range
- Given a date range that yields more than one page per provider API When a sync runs Then the connector fetches all pages until completion and the total normalized records equal the provider-reported total or, if unavailable, are stable across two repeated pulls within ±0.5%. - Given HTTP 429 or rate limit headers are returned When a sync runs Then the connector applies backoff per provider hints or exponential backoff (starting 1s, doubling up to 60s) and resumes without data loss. - Given provider page size limits When syncing Then requests use provider-compliant page sizes to avoid server rejection. - Given transient network errors (5xx, timeouts) occur When syncing Then the connector retries up to 5 times with jitter and logs each retry attempt; on final failure, the run is marked failed with an actionable message.
Incremental sync via cursor since last sync
- Given a successful full sync completed on 2025-09-20T00:00Z When an incremental sync runs on 2025-09-26 Then only records with updated_at greater than last_cursor within a 48-hour lookback window are fetched. - Given a connector and workspace When a sync completes Then the last successful cursor is stored atomically and used on the next run. - Given a user chooses “Resync since date” for 2025-09-10 When the job runs Then the connector backfills from 2025-09-10 inclusive and advances the cursor to the latest completed page. - Given provider results are unordered or repeated across pages When normalizing Then deduplication uses the natural key {source, workspace_id, listing_id|sku|asin, creative_id, campaign_id, ad_group_id, date_utc} before upsert.
Idempotent ingestion and duplicate protection
- Given the same provider page is replayed three times due to retries When records are ingested Then the total unique records stored equals the unique inputs with zero duplicates. - Given a record with the same natural key arrives with updated metrics When ingested Then the latest record supersedes the prior one using provider updated_at or ts_utc ordering; a changelog captures the previous values. - Given a manual re-run of the same date range for a connector When completed Then no duplicate records are created and the run report shows dedup counts. - Given two connectors for the same provider in different workspaces When both ingest identical records Then no cross-workspace duplication or leakage occurs.
Error handling, retries with backoff, and partial batch continuation
- Given a batch of 10,000 records contains 200 schema errors When the sync completes Then 9,800 valid records are committed, 200 are quarantined with reason codes, and the run status is “Completed with warnings”. - Given max retry attempts are exhausted on a page When the sync stops Then the run status is “Failed”, a correlation ID is provided, and resume-from-checkpoint is available. - Given any exception is logged When reviewing logs Then entries include timestamp, source, workspace_id, request_id, page_token/cursor, and redacted credentials; no PII or secrets are logged. - Given a failed run is retried within 15 minutes When resumed Then it continues from the last committed checkpoint without reprocessing completed pages.
Selectable date ranges and source-level filters
- Given a user selects 2025-09-01 through 2025-09-15 and campaign “BackToSchool” When the sync runs Then only records within the inclusive date range and matching the campaign are ingested. - Given a user selects timezone America/Los_Angeles When the sync runs Then the selected range is converted to UTC correctly for ingestion, with boundaries honored. - Given no server-side filtering is supported by a provider When the sync runs Then the connector fetches all records for the range, applies client-side filters post-fetch, and marks the run as client-filtered in logs. - Given the user clears all filters and runs the sync again for the same dates When completed Then the record count is greater than or equal to the filtered run and matches provider totals within ±0.5% if totals are exposed.
CSV Import with Smart Column & Variant Auto-Mapping
"As a seller, I want to upload a CSV and have PixelLift auto-map the columns to the right metrics and variants so that I can import results quickly even from unsupported sources."
Description

Allow users to upload CSV/XLSX files and auto-detect delimiter, encoding, and headers. Provide an interactive mapping UI that suggests column-to-field mappings (impressions, clicks, CTR, conversions, spend, revenue, date, listing ID, campaign/ad ID, variant label) using heuristics and synonyms; enable manual override and saving of mapping presets per source/workspace. Validate required fields, detect data types and timezones, preview sample rows with validation warnings, and block import on critical errors. De-duplicate rows using a composite key (source, listing/ad ID, date, metric granularity), support large files via chunked streaming, and record provenance for each imported row. Auto-extract variant labels from common locations (filename, UTM parameters, campaign names) when present.

Acceptance Criteria
Auto-Detection of Delimiter, Encoding, and Headers
Given CSV/XLSX files using comma, tab, semicolon, or pipe delimiters When a file is uploaded Then the detected delimiter produces a consistent column count for the first 1000 data rows and is displayed to the user Given files encoded in UTF-8, UTF-8-BOM, ISO-8859-1, or Windows-1252 containing non-ASCII characters When a file is uploaded Then the preview shows 0 replacement characters (�/U+FFFD) in the first 100 rows and characters render correctly Given a file whose first row is a header When the file is uploaded Then the header row is recognized and prefilled as column names Given a file with no header row When the file is uploaded Then the system defaults to “No header” and assigns Column 1..N and allows the user to toggle header on/off with immediate remap in the preview
Column Mapping Suggestions and Manual Override with Presets
Given a file with columns ["impr","clk","ctr %","orders","cost","rev","date","asin","campaign id","variant"] When the mapping UI loads Then suggested mappings are [Impressions, Clicks, CTR, Conversions, Spend, Revenue, Date, Listing ID, Campaign/Ad ID, Variant Label] and are visually indicated with confidence labels When the user manually changes any suggested mapping Then the preview updates and validation re-runs within 1 second When the user saves the mapping as a preset named "Amazon Ads" for the current workspace Then a subsequent upload from the same source/workspace auto-applies the preset and displays "Preset: Amazon Ads" Given an existing preset When the user edits or deletes it Then the change is persisted and reflected on the next upload
Required Fields, Type/Timezone Validation, Preview Warnings, and Blocking on Critical Errors
Rule: Required mappings are Date AND at least one identifier (Listing ID or Campaign/Ad ID) AND at least one metric among {Impressions, Clicks, CTR, Conversions, Spend, Revenue} Given required mappings are missing When the user clicks Import Then the import is blocked and the UI lists the missing mappings Given date values in formats YYYY-MM-DD, MM/DD/YYYY, or DD-MM-YYYY with optional timezone indicators (e.g., Z, ±HHMM) When uploaded Then the system detects the format/timezone and shows normalized UTC in the preview tooltip Given type-mismatched cells (e.g., non-numeric in numeric fields, invalid dates) When uploaded Then warnings list row numbers and columns; if >5% of rows have critical errors in required fields, Import is blocked; otherwise user may proceed with the error count displayed Given mixed timezone indicators within the same file When detected Then Import is blocked until a single timezone override is selected
De-duplication by Composite Key
Rule: Composite key is {Source, Listing/Campaign/Ad ID, Date (day), Metric Granularity} Given multiple rows within the same upload share the same composite key When importing Then the first occurrence is retained, subsequent duplicates are skipped, and a summary reports the duplicate count and sample keys Given existing rows in the database share the same composite key as incoming rows When importing Then incoming duplicates are skipped (no overwrite), and the summary reports the count of skipped existing rows Then the final imported row count equals parsed rows minus duplicates minus invalid rows
Large File Import via Chunked Streaming with Progress and Cancellation
Given a 1,000,000-row CSV (~1 GB) over a stable connection When imported Then ingestion proceeds via chunked streaming with a progress indicator updating at least every 2 seconds and completes within 30 minutes Then importer process memory usage does not exceed 512 MB and the mapping UI remains responsive (no browser "Page Unresponsive" dialog) When the user clicks Cancel during import Then the operation stops within 10 seconds and the partial import is rolled back (0 rows committed)
Row-Level Provenance Recording
Given a successful import Then each stored row includes provenance: source, original filename, upload ID, importing user ID, workspace ID, mapping preset ID (if applied), imported_at timestamp, and original header names hash Given an imported row When queried via audit view/API by upload ID Then all provenance fields are retrievable and match the import context Given identical filenames imported on different dates When queried Then rows are distinguishable by unique upload IDs and timestamps
Variant Label Auto-Extraction from Filename, UTM, and Campaign Name
Given a filename "SKU1234__VAR=Blue__img1.jpg" and no mapped Variant Label column When imported Then Variant Label is set to "Blue" Given a URL with UTM parameters containing "utm_content=Variant%3AB" When imported Then Variant Label is set to "B" Given a campaign name "[VAR:Hero-B]" When imported Then Variant Label is set to "Hero-B" Given a mapped "Variant Label" column exists When imported Then the mapped value is used and auto-extraction does not overwrite it Then rows sharing the same Listing/Campaign ID and Variant Label are consistently labeled across the batch (case-insensitive, trimmed)
Variant Label Matching Engine
"As a seller, I want PixelLift to accurately match imported metrics to my image variants using the labels I use in filenames and campaigns so that comparisons are correct."
Description

Create a configurable matching engine that links external performance rows to PixelLift experiment variants using labels. Support rule-based parsing (regex, token patterns) for filenames, campaign/ad names, and UTM parameters; allow multiple strategies with priority order and confidence scoring. Provide fuzzy matching with configurable thresholds, tie-break rules, and fallbacks to explicit experiment/variant IDs when present. Surface unmatched or ambiguous rows for manual resolution and allow users to create and persist mapping rules directly from the resolution UI. Maintain an audit trail of mappings, version rules over time, and reapply rules on re-syncs.

Acceptance Criteria
Regex and Token Parsing Maps Variants from Filenames, Campaign Names, and UTM Parameters
Given parsing strategies are configured with: (1) filename regex ^(?<product>.+)_v(?<variant>[A-Z0-9-]+)\.(jpg|png)$, (2) UTM token variant=<label>, (3) campaign/ad name regex \[Var:(?<variant>[A-Z0-9-]+)\] And the strategy priority is UTM token > Filename regex > Campaign regex And a CSV import contains rows with filename, campaign_name, and utm_content fields When Results Sync is executed Then rows matching any strategy are mapped to the extracted variant label And exact regex/token matches receive confidence >= 0.90 And at least 95% of rows containing a matching pattern are mapped on first pass And each mapped row stores the applied strategy, extracted label, and confidence And rows with no matches remain Unmatched for resolution
Strategy Priority Order and Deterministic Tie-Breaking
Given multiple strategies produce conflicting labels for the same row And the configured priority order is [Explicit IDs, UTM token, Filename regex, Campaign regex] And confidence scores are computed per match When the matching engine selects a label Then it chooses the label from the highest-priority strategy And if two candidates are from the same strategy, it selects the one with higher confidence And if confidence differs by ≤ 0.01, it applies tie-breakers in order: longest exact token match > most specific regex (fewest wildcards) > lexicographically smallest label And the same input produces the same decision across repeated runs
Fuzzy Matching with Configurable Thresholds
Given fuzzy matching is enabled with minimum similarity threshold = 0.82 and auto-apply threshold = 0.90 And candidate labels are the set of existing experiment variant labels When no exact or token/regex match is found Then the engine proposes the nearest variant with similarity ≥ 0.82 And it auto-maps only when similarity ≥ 0.90 And rows with 0.82 ≤ similarity < 0.90 are flagged Ambiguous with top 3 suggestions and scores And rows with similarity < 0.82 remain Unmatched And all fuzzy decisions record similarity score and algorithm used
Explicit Experiment/Variant IDs Override All Strategies
Given a row contains experiment_id and variant_id that exist and are Active in PixelLift When mapping runs Then the row is mapped directly to that variant with confidence = 1.00 And lower-priority strategies are skipped And if the IDs are unknown, archived, or mismatched to the experiment, the row is marked Unmatched with reason = Invalid IDs And the override decision is recorded in the audit log with source = Explicit IDs
Manual Resolution Queue with In-UI Rule Creation and Persistence
Given the Resolution Queue lists Unmatched and Ambiguous rows with their candidate suggestions When a user assigns a row to a variant and selects "Create rule from this decision" Then a new persisted rule is created with captured pattern, rule type, scope (global/experiment), and position in priority order And the user can preview and confirm the rule before saving And upon save, the queue reprocesses immediately, applying the new rule to remaining items And the resolved row status changes to Resolved with reference to the rule ID/version And the new rule appears in the Rules list and is applied on subsequent syncs
Audit Trail and Rule Versioning for All Mappings
Given any mapping occurs (automatic, fuzzy, explicit ID, or manual) When the decision is finalized Then an immutable audit record is written including: UTC timestamp, row ID, source dataset, selected variant, strategy, confidence/similarity, rule ID/version (if applicable), user ID (for manual), and decision reason And audit records are queryable and exportable to CSV filtered by date range, experiment, strategy, and outcome And when a rule is edited, reordered, enabled/disabled, or deleted, a new rule version is created with change metadata (who, when, what) And past audit entries retain their original rule version reference
Re-Sync Reapplication, Supersession Handling, and Idempotency
Given rules exist and a dataset has previously been synced with some Unmatched or Ambiguous rows When a re-sync is triggered for the same dataset Then all rules (including new versions) are reapplied to all rows And previously mapped rows remain mapped unless superseded by a higher-priority rule version, in which case a new audit entry is appended with reason = Superseded by rule vX And repeated re-syncs with unchanged data and rules produce no changes and no duplicate audit entries And the post-sync summary displays counts for Mapped, Ambiguous, Unmatched that reconcile with the audit log
Experiment Metrics Aggregation & Comparison Engine
"As a seller, I want PixelLift to consolidate metrics per variant and compute comparable CTR and conversions over the same time window so that I can trust apples-to-apples results."
Description

Aggregate synced data into experiments and variants, aligning records by overlapping time windows and marketplaces to ensure apples-to-apples comparisons. Compute totals and derived KPIs (CTR, CVR, CPC, CPA, ROAS) with options for per-source breakdowns and overall rollups. Enforce inclusion criteria (e.g., minimum impressions/clicks) and flag underpowered results. Update the experiment log with a linked data snapshot, calculation metadata, and versioning to allow reproducibility and re-computation after new syncs. Provide optional basic significance testing for two-variant comparisons with guardrails (minimum sample sizes, continuity corrections) and clearly label non-significant outcomes. Support export of consolidated results for A/B documentation and bulk reporting.

Acceptance Criteria
Overlap-Window Alignment by Marketplace
Given an experiment with two or more variants containing events from multiple marketplaces and non-identical date ranges And alignment mode is set to overlap-per-marketplace When the engine aggregates metrics Then for each marketplace, only events within the intersection of all included variants' date ranges are counted And events outside the overlap window are excluded from totals and derived KPIs And if a marketplace has no overlap between variants, it is excluded from rollup with reason "No overlap" And the aggregation log includes per-marketplace overlap start_date and end_date used And rollup totals equal the sum of included marketplaces' totals within an absolute tolerance of 1e-6
KPI Computation, Precision, and Zero-Division Handling
Given aggregated totals per variant with fields: impressions, clicks, conversions, spend, revenue When the engine computes KPIs Then CTR = clicks / impressions, CVR = conversions / clicks, CPC = spend / clicks, CPA = spend / conversions, ROAS = revenue / spend And if a denominator is zero, the corresponding KPI is null and flagged "insufficient_denominator" And rate KPIs (CTR, CVR, ROAS) are rounded to 4 decimal places; monetary KPIs (CPC, CPA) to 2 decimal places And per-source KPIs are produced for each marketplace present and an overall rollup KPIs are computed from rollup totals And rollup totals equal the sum of per-source totals within 1e-6 and rollup KPIs recomputed from totals match displayed values within 1e-4
Inclusion Thresholds and Underpowered Flagging
Given inclusion thresholds are configured as min_impressions >= 100 and min_clicks >= 20 per variant (rollup level) When a variant's rollup totals fail any threshold Then the variant is labeled "Underpowered" and excluded from significance testing and winner designation And the variant's metrics remain visible with an "Underpowered" badge and a note of which thresholds failed And if all variants in an experiment are underpowered, the experiment status is marked "Underpowered" in the log And the log records the thresholds applied and the variants that failed them
Snapshotting, Metadata, and Versioned Experiment Log
Given an aggregation run completes When the engine writes to the experiment log Then it creates a new version entry with a monotonically increasing version number And attaches an immutable data snapshot_id with source dataset references and content hash And stores calculation metadata: inclusion thresholds, marketplaces included, per-market overlap windows, KPI formulas and rounding, significance settings (alpha, tail, correction), breakdown_mode, and initiator And re-running aggregation with the same snapshot_id and settings produces identical outputs within tolerances and does not create a new version (updates last_verified_at) And after a new sync that changes the input data, re-running creates a new version linked to the new snapshot_id while preserving prior versions
Two-Variant Significance Testing with Guardrails
Given a two-variant experiment with significance testing enabled (alpha = 0.05, two-tailed) and a selected primary KPI (CTR or CVR) And guardrails are satisfied: for CTR test each variant has impressions >= 1000; for CVR test each variant has clicks >= 100 When the engine performs significance testing Then it uses a two-proportion z-test with continuity correction (Yates) on the chosen KPI And outputs p_value (4 decimals), absolute_difference, relative_difference, and a label "Significant" if p_value < 0.05 else "Not significant" And if guardrails are not met, the test is skipped and labeled "Not enough data" with p_value null And if more than two variants are present, significance testing is not run and labeled "N/A" And the log captures method, alpha, sample sizes, and guardrail evaluation
Consolidated Results Export (Rollup and Per-Source)
Given an aggregated experiment with per-source breakdowns, rollup totals, and (optionally) significance results When the user exports results as CSV Then the file includes a header row and rows for each variant per marketplace plus one rollup row per variant And columns include: experiment_id, version, variant_label, marketplace, date_start, date_end, impressions, clicks, conversions, spend, revenue, CTR, CVR, CPC, CPA, ROAS, underpowered_flag, p_value, effect_abs, effect_rel, significance_label, snapshot_id, generated_at And numeric values match in-app metrics within 1e-6 for totals and 1e-4 for rates; dates are ISO 8601; encoding is UTF-8; delimiter is comma with RFC 4180 quoting And when significance is disabled, p_value/effect columns are present with nulls
Per-Source Breakdown Modes and Rollup Consistency
Given the aggregation parameter breakdown_mode in {per_source_only, rollup_only, both} When running aggregation with breakdown_mode = per_source_only Then only per-marketplace rows are returned and no rollup rows are present When running aggregation with breakdown_mode = rollup_only Then only rollup rows per variant are returned and no per-marketplace rows are present When running aggregation with breakdown_mode = both Then both per-marketplace rows and rollup rows are returned And in both/both modes, rollup totals equal the sum of per-source totals within 1e-6 and KPIs recomputed from rollup totals match displayed values within 1e-4 And the log records the breakdown_mode used
Scheduled & On-Demand Sync Orchestration
"As a seller, I want syncs to run automatically on a schedule and retry on failures so that my experiment log stays up to date without my intervention."
Description

Enable users to trigger manual syncs and configure per-connector schedules (e.g., hourly, daily) with time window settings and blackout periods. Run syncs as idempotent background jobs with concurrency controls, exponential backoff, and resumable cursors. Support webhooks/notifications from sources where available to initiate delta syncs. Record per-run telemetry (duration, rows processed, errors, last cursor), expose a run history, and allow safe cancellation and retry. Ensure that aggregation and experiment-log updates are executed as transactional steps to prevent partial state on failures.

Acceptance Criteria
Manual On-Demand Sync Trigger
Given a connected marketplace connector with valid credentials and a prior successful run When the user clicks "Sync Now" Then a background job is enqueued within 2 seconds and appears in Run History with status "Queued" Given the job starts without a "Full Re-sync" override When fetching from the source Then only records newer than the last stored cursor are requested and processed Given the user clicks "Sync Now" twice within 10 seconds for the same connector When jobs are scheduled Then only one job runs at a time (connector concurrency = 1) and the second job starts only after the first completes Given the first job completes successfully When the same delta is re-run manually Then no duplicate aggregations or experiment-log entries are produced (idempotent result: counts unchanged) Given a prior failed manual run with a stored cursor When the user clicks "Retry" Then processing resumes from the last stored cursor and already committed rows are excluded from processing
Per-Connector Scheduling with Time Windows and Blackouts
Given a connector schedule set to "hourly at :15" in timezone America/Los_Angeles When local system time reaches 10:15 Then a job is enqueued within 60 seconds and marked with trigger source "Schedule" Given a blackout window of 22:00–06:00 local time When a scheduled time falls inside the blackout Then the run is skipped and the next eligible run occurs at the first window after 06:00 (e.g., 06:15) Given a time window filter "last 24 hours" When the scheduled job runs Then API requests are constrained to that range and rows older than 24 hours are not fetched Given a user edits and saves the schedule settings When the service restarts Then the updated schedule persists and takes effect on the next occurrence Given multiple connectors have overlapping schedules When jobs are triggered Then each connector runs on its own cadence without cross-connector blocking beyond global worker limits
Idempotent Jobs with Concurrency Controls and Exponential Backoff
Given the same delta payload is delivered twice by the source When the sync processes both deliveries Then aggregated metrics and experiment-log entries remain unchanged after the second processing (no duplicates) Given per-connector concurrency = 1 and global worker pool size = 10 When 5 connectors have due runs Then at most 1 active run per connector and at most 10 active runs overall are observed Given the source responds with HTTP 429 or a Retry-After header When retrying requests Then retries follow exponential backoff (e.g., ~1s, 2s, 4s, 8s) respecting Retry-After up to a maximum of 5 attempts Given max retry attempts are exceeded for a step When the run terminates Then the run status is "Failed" and the last error code/message are captured in telemetry with no partial writes committed Given a transient network failure during a page fetch When the page is retried Then the page is processed exactly once end-to-end (no duplicate commits)
Resumable Cursors for Partial and Failed Runs
Given a run fails after committing cursor "t=2025-09-26T10:00:00Z,page=3" When the user triggers a retry Then processing resumes from page 3 (or the next unprocessed offset) and continues forward without reprocessing already committed rows Given the connector re-delivers records from the last committed page When resuming Then deduplication prevents double-counting and no duplicate experiment-log entries are created Given connectors use heterogeneous cursors (timestamps, numeric IDs, opaque tokens) When storing state Then the connector-specific cursor value is persisted durably and displayed in run telemetry Given a full re-sync is requested by the user When the job starts Then the stored cursor is reset and historical data is fetched with progress checkpoints at least every 1,000 records
Webhook-Initiated Delta Sync with Debounce and Signature Validation
Given a valid signed webhook event indicating new marketplace results When the event is received Then a delta sync job is enqueued within 5 seconds with trigger source "Webhook" Given a webhook with an invalid or missing signature When processed Then the request is rejected with 401/403 and no sync job is created Given multiple valid webhooks arrive within 30 seconds for the same connector When scheduling syncs Then events are debounced into a single run using the newest cursor available Given webhook delivery is unavailable for a connector When scheduled polling occurs Then pending changes are still captured on the next schedule without data loss
Per-Run Telemetry and Run History Visibility
Given a run completes (success, failure, or canceled) When telemetry is recorded Then start_time, end_time, duration_ms, rows_fetched, rows_processed, rows_deduped, errors_count, last_cursor, trigger_source, and final status are stored Given a user opens Run History When viewing the list Then runs from the last 90 days are visible with filters for connector, status, trigger_source, and date range, and the list can be exported to CSV Given a run is in progress When viewing its detail Then status transitions (Queued → Running → Succeeded/Failed/Canceled) and progress percentage update with ≤5 seconds latency Given an error occurs in any step When inspecting the run Then the failing step name and error code/message are displayed in telemetry
Safe Cancellation, Retry, and Transactional Aggregation/Experiment-Log Updates
Given a running sync When the user clicks "Cancel" Then the job stops within 30 seconds after the current atomic step and the run status becomes "Canceled" Given a failure or cancellation occurs during aggregation or experiment-log update When the transaction completes Then either both aggregation and experiment-log updates are committed or neither is (no partial state visible to readers) Given the user retries a failed or canceled run When processing resumes Then it continues from the last committed cursor and re-applies aggregation and log updates idempotently Given concurrent reads of metrics occur during a write When isolation is enforced Then readers observe only a fully committed pre-state or post-state (no mixed/inconsistent views) Given a run fails 3 consecutive times and is dead-lettered When viewing actions Then automatic retries stop and a manual "Retry" control is available with the failure reason shown
Sync Dashboard & Mapping Review UI
"As a seller, I want a dashboard to see sync status and fix mapping issues so that I can resolve data problems fast."
Description

Provide a dashboard showing connector health, last/next sync times, data freshness indicators, recent run outcomes, and quick actions (reauthorize, resync, edit schedule). Include a mapping review workspace to preview imported rows, visualize field mappings and variant assignments, highlight unmatched/ambiguous records, and allow inline corrections with bulk actions. Offer filters by source, experiment, date range, and status; surface validation warnings and suggested fixes. Support commit/revert workflows so users can approve mappings before they affect the experiment log. Allow export of sync and mapping logs for audit or support.

Acceptance Criteria
Connector Health Overview and Quick Actions
- Given at least one connected source, when the Sync Dashboard loads, then each connector card shows: connector name, status (Healthy/Warning/Error/Syncing), last sync timestamp (UTC), next scheduled sync timestamp (UTC), and a freshness pill with age in hours:minutes and a tooltip containing the source-provided timestamp. - Given a connector with expired or revoked authorization, when status is Error due to auth, then a Reauthorize action is visible and enabled, and clicking it opens the authorization modal within 1 second of click. - Given any connector, when a user clicks Resync, then a sync job is queued, the UI reflects status Syncing within 2 seconds, and upon completion the Recent Runs list adds an entry with start time, end time, duration, records processed, and outcome (Success/Partial/Failed). - Given a connector with a defined schedule, when a user edits and saves the schedule, then the next scheduled sync timestamp updates immediately and persists after page reload.
Mapping Review Preview and Inline Corrections
- Given an imported dataset (API pull or CSV), when the Mapping Review workspace opens, then a paginated preview renders at least 100 rows per page showing source fields, mapped target fields, and variant assignment per row. - Given rows with no variant match, when displayed, then they are highlighted as Unmatched and can be filtered via a Status filter. - Given rows with multiple variant candidates, when displayed, then they are flagged as Ambiguous with a badge showing the number of candidates and a dropdown to select the correct variant. - Given a user edits a mapping inline for a single row and saves, then the row updates immediately in the staged set and the Unmatched/Ambiguous counts update accordingly; given N selected rows, when a bulk action is applied, then all N rows update within 2 seconds and the action is undoable until commit.
Auto-Mapping Rules, Validation, and Suggestions
- Given a row label exactly matches a variant ID or SKU (case-insensitive, trimmed), when auto-mapping runs, then the variant is assigned automatically and marked Auto with no warning. - Given no exact match but one candidate has string similarity ≥ 0.85 to the row label, when auto-mapping runs, then a Suggested match is displayed with its confidence score and an Apply action. - Given multiple candidates within 0.05 similarity of each other above 0.75, when auto-mapping runs, then the row is marked Ambiguous and is not auto-assigned. - Given a row missing any required metrics (CTR, impressions, conversions), when validation runs, then a blocking warning lists the missing fields and the row is excluded from commit until resolved.
Filters by Source, Experiment, Date Range, and Status
- Given the user selects one or more Sources and Experiments, when filters are applied, then the grid updates within 500 ms for datasets up to 10,000 staged rows and the applied filters are reflected as chips. - Given a Date Range (start and end) is set, when applied, then only rows with timestamps within the inclusive range are shown and the total/filtered counts update correctly. - Given a Status filter (All, Auto, Manual, Unmatched, Ambiguous, Error, Ready) is selected, when toggled, then the grid immediately reflects the subset and the selection persists after navigation away and back within the same session. - Given filters are active, when the user commits or exports, then only the filtered subset is included in the operation and the operation summary shows the active filters.
Commit and Revert Workflow with Guardrails
- Given staged changes with zero blocking validation errors, when the user clicks Commit, then a confirmation modal displays rows to commit, affected experiments, and date range, and requires explicit confirmation before proceeding. - Given the commit is confirmed, when it completes, then a commit ID is generated, an audit log entry records user, timestamp, and row counts, and the experiment log reflects the changes within 60 seconds. - Given staged changes contain any Unmatched, Ambiguous, or Error rows, when the user attempts to Commit, then the action is blocked and a list of blocking issues is displayed with deep links to fix. - Given a prior commit exists, when the user selects Revert for a specific commit, then the changes from that commit are rolled back, a new audit log entry records the revert with a link to the original commit ID, and the UI confirms success.
Sync and Mapping Logs Export
- Given sync and mapping logs exist, when the user requests Export, then a downloadable file is generated within 10 seconds for up to 100,000 rows in both CSV and JSON formats. - Given an export is generated, then it contains: connector, source, run ID, user, start time, end time, duration, status, records processed, errors (message and row IDs), mapping changes (before/after), and commit IDs where applicable. - Given filters are active, when exporting, then only the filtered rows are included and the export metadata includes the active filters and export timestamp. - Given the file is ready, when downloaded, then the filename follows the pattern PixelLift_{logType}_{YYYYMMDDTHHMMSSZ}.{csv|json}.
Data Freshness Indicator Logic
- Given the latest source data timestamp age is ≤ 2 hours, when displaying freshness, then the pill shows Fresh (green) with the age in hh:mm and a tooltip with last marketplace timestamp and last sync completion time. - Given the age is > 2 hours and ≤ 24 hours, when displaying freshness, then the pill shows Stale (amber) with the age in hh:mm. - Given the age is > 24 hours or the last sync Failed, when displaying freshness, then the pill shows Out of date (red) and the Recent Runs list shows the latest failure reason. - Given the user hovers the freshness pill, when the tooltip opens, then it includes last source timestamp, last successful sync time, and the skew between them in minutes.
Sync Health, Validation & Alerts
"As a seller, I want to be notified when syncs fail or data looks incomplete so that I can take action before making decisions."
Description

Implement schema and business-rule validation (e.g., non-negative metrics, CTR <= 100%, consistent timezones), duplicate detection, and gap analysis for missing days or sources. Perform basic anomaly checks (sudden spikes/drops) and mark suspect data for review. Generate in-app and email/Slack alerts for connector failures, stale data, mapping ambiguity, and low sample size against configured thresholds. Expose a health summary per connector and experiment, with remediation guidance. Maintain an audit trail of data changes and experiment-log updates, and enforce role-based access to alerts and data actions.

Acceptance Criteria
Schema and Business-Rule Validation on Ingest
Given a CSV import or connector fetch includes fields date, variant_label, marketplace, source, timezone, impressions, clicks, conversions, CTR When the dataset is validated Then rows with missing required columns cause the import to fail with a clear error code and list of missing columns And rows with non-numeric or negative impressions, clicks, conversions, or CTR are rejected and counted with reason codes And CTR values must be between 0 and 100 inclusive; if CTR is provided and differs from clicks/impressions by more than 1 percentage point, the row is marked suspect with a mismatch flag And dates must parse successfully and not be in the future relative to the workspace timezone; invalid or future dates are rejected with reason codes And timezone values must be valid IANA identifiers or match the workspace default; invalid timezones are rejected and counted And a validation summary shows total rows processed, rows rejected by rule, rows marked suspect, and is persisted to the audit trail
Duplicate Detection and De-duplication During Sync
Given incoming records may contain duplicates defined by the composite key (date, marketplace, source, variant_label) When duplicates are detected within a single sync batch or across overlapping sync windows Then duplicates are resolved using the configured rule (latest ingestion_timestamp wins, else source priority order) and only one canonical record is stored And metrics from discarded duplicates are not double-counted in experiment aggregates And the number of duplicates detected, kept, and discarded is displayed in the sync report and written to the audit trail And a de-duplication warning alert is created if duplicates exceed the configured threshold percentage of the batch
Gap Analysis for Missing Days and Sources
Given an experiment has an expected reporting cadence per connector and source over a defined date range When one or more expected days or sources have no records and no explicit zero-report from the source Then the missing dates/sources are listed as gaps with counts per variant_label And gaps older than the configured freshness threshold trigger an in-app alert and a Slack/email notification And gap items contain remediation actions (re-run sync, reconnect, backfill) with deep links And gap days are excluded from experiment CTR and sample size calculations until data arrives or the user marks them as intentionally missing And the Health Summary displays total gap days and their current remediation status
Anomaly Detection and Suspect Data Flagging
Given historical baselines exist for impressions, CTR, and conversions per variant_label and source When a new day’s metric deviates beyond the configured anomaly rule (e.g., z-score >= 3 or absolute change >= 50% vs. 7-day median) and sample size >= minimum impressions Then the record is marked suspect with an anomaly type (spike or drop) and contributing metric And suspect records are excluded from rolled-up comparisons and winner calculations unless an Admin or Editor explicitly includes them And an alert is generated if anomalies persist for 2 consecutive days for the same variant_label And all auto-flagging and user override actions are captured in the audit trail
Connector Failure, Stale Data, Mapping Ambiguity, and Low Sample Size Alerts
Given a connector has a scheduled sync window and channel notifications are configured When a sync fails or last successful sync exceeds the staleness threshold (e.g., > 24 hours) Then an in-app alert is created with severity, error detail, and remediation steps, and notifications are sent via email and Slack to subscribed users And users with Editor or Admin roles see a Retry Sync action on the alert; Viewers do not When auto-mapping detects ambiguous variant_label mapping (many-to-one or unmapped labels) Then the system blocks result publication, prompts for manual resolution, and creates an alert with a resolution link When an active experiment’s impressions per variant fall below the configured minimum sample size over the analysis window Then a low-sample-size alert is raised and winner computation is withheld until the threshold is met And alerts are de-duplicated within a 60-minute window and can be acknowledged to suppress further notifications while remaining visible in history
Health Summary per Connector and Experiment
Given a user opens the Health view for a connector or experiment When the health data loads Then the view displays last sync time, data freshness, counts of validation errors, duplicates, gaps, anomalies, and open alerts And each failed check includes remediation guidance and a deep link to take action (reconnect, re-run sync, resolve mapping, backfill) And the displayed counts and timestamps match the latest backend logs for the selected date range And the user can export a Health Report (CSV/PDF) that includes all checks, counts, and timestamps And loading and empty states are shown appropriately with retry controls
Audit Trail and Role-Based Access Enforcement
Given any data change, mapping decision, alert acknowledgment, sync retry, threshold edit, or experiment-log update occurs When the action is performed by a user or system process Then an immutable audit record is created capturing actor identity, role, timestamp (UTC), action type, entity IDs, old_value/new_value, and source (UI/API) And audit entries are filterable by date range, user, action, and entity, and exportable to CSV And only Admins can edit thresholds, resolve mapping ambiguities, or delete/restore records; Editors can re-run syncs and acknowledge alerts; Viewers have read-only access And unauthorized attempts are blocked with a 403 response and recorded in the audit trail And audit records are tamper-evident via hash chaining or signature, and integrity verification can be run on demand with a pass/fail result

Variant Tags

Attach structured tags to each variant (angle, crop tightness, background, shadow style). Tags appear in filenames and CSVs and roll up across tests so you can spot patterns—like “tight crop + pure white” consistently lifting CTR in Home & Kitchen—informing smarter future shoots.

Requirements

Tag Schema & Governance
"As a workspace admin, I want standardized tag definitions so that tagging stays consistent and comparable across teams and tests."
Description

Implement a structured, versioned tag schema with governed categories (e.g., angle, crop tightness, background, shadow style) and controlled vocabularies. Provide validation rules, required/optional flags, default values, and per-workspace or per-marketplace overrides. Include an admin UI to create, edit, deprecate, and version tag definitions with migration tools to backfill or transform existing variant tags. Ensure backward compatibility so historical exports and analytics remain intact, and maintain audit logs for schema and value changes.

Acceptance Criteria
Create and Version Tag Categories with Controlled Vocabulary
Given I am an admin in the Tag Schema UI When I create category "background" with allowed values ["pure_white","light_gray","lifestyle_soft"], required=false, default="pure_white" Then the schema saves as version "1.0", is marked Active, and is visible to all workspaces within 2 seconds Given schema version "1.0" exists When I add a new allowed value "studio_gradient" to "background" and publish Then a new schema version "1.1" is created, "1.0" remains immutable, and "1.1" becomes Active Given an attempt to add a duplicate value or a value not matching slug pattern ^[a-z0-9_]{2,32}$ When I click Save Then the UI blocks save and displays field-level errors listing the offending values and the allowed pattern; no new version is created
Validate Required/Optional Tags and Defaults on Variant Creation
Given Active schema v1.1 where angle.required=true and shadow_style.required=false and shadow_style.default="soft_drop" When I POST /variants with tags { angle: null, shadow_style: null } Then the API responds 422 with errors[angle].code="TAG_REQUIRED" and no variant is created Given Active schema v1.1 as above When I POST /variants with tags { angle: "front", shadow_style: null } Then the variant is created; stored tags include shadow_style="soft_drop" Given Active schema v1.1 where background.allowed=["pure_white","light_gray"] When I POST /variants with tags { background: "off_white" } Then the API responds 422 with errors[background].code="TAG_VALUE_INVALID" and errors[background].allowed=["pure_white","light_gray"]
Per-Workspace and Per-Marketplace Overrides Enforcement
Given Workspace "A" has a marketplace override for category "crop_tightness" allowed=["tight","medium","loose"], default="tight" When I create a variant assigned to marketplace "Home_Kitchen" in Workspace "A" with crop_tightness missing Then the saved variant uses crop_tightness="tight" and validation only permits ["tight","medium","loose"] Given Workspace "B" has no override for "crop_tightness" When a variant is created in Workspace "B" Then the base schema vocabulary and default apply Given an override is scheduled with effective_at timestamp T in Workspace "A" When a variant is created before T Then the previous override/base schema applies; when created at or after T, the new override applies
Deprecate Tag Values and Migrate Existing Variants
Given category "background" has value "off_white" in use on 1,250 variants When I deprecate "off_white" and map it to "pure_white" via the migration tool in Dry Run Then the tool reports will_update=1250, unmapped=0, and estimated_duration<=120s Given I confirm Apply on the migration When the migration completes Then 1,250 variants are updated to background="pure_white"; "off_white" is marked Deprecated and blocked from future use; a migration record with id and counts is created Given unmapped deprecated values exist When I attempt to publish a schema without providing mappings Then the system prevents publish with error code "MIGRATION_MAPPING_REQUIRED" listing the unmapped values
Backward Compatibility for Historical Exports and Analytics
Given schema upgraded from v1.0 to v1.2 at timestamp T When I download an export generated before T Then filenames and CSVs contain original tag strings from v1.0 with no alteration Given I regenerate an export for a batch created at timestamp T-1h When I request export_mode="historical" Then the system uses schema v1.0 to render filenames/CSVs; when I request export_mode="current" it uses Active v1.2 Given analytics dashboards aggregate CTR by tags across versions When viewing a report spanning v1.0–v1.2 Then tag rollups use stable tag_category_id and tag_value_id; deprecations are mapped according to migration rules; metrics remain continuous
Audit Logging for Schema and Tag Value Changes
Given any schema or override change is made (create/edit/deprecate/version/migration) When I open Admin > Audit Log and filter by "Tag Schema" Then each entry shows actor, timestamp, action, scope (global/workspace/marketplace), before/after diff, affected counts, and reference IDs Given I attempt to delete an audit log entry via UI or API When I send the request Then the action is forbidden with HTTP 403 and no changes occur Given I export audit logs for date range D1–D2 When I click "Export CSV" Then a CSV downloads with columns: id, timestamp, actor, action, scope, entity_type, entity_id, before, after, affected_count
Bulk Tagging UI & Shortcuts
"As a seller managing many listings, I want to apply tags to multiple variants at once so that I can prepare large A/B sets quickly without repetitive steps."
Description

Add a batch-friendly tagging interface in the variant grid/editor that displays current tags, supports multi-select and bulk-apply, and offers quick-pick presets for common combinations. Provide inline validation, visual conflict resolution when merging different tag sets, keyboard shortcuts, and undo/redo. Ensure responsive performance on large batches and accessibility compliance. Persist recent tags and presets per user for faster workflows.

Acceptance Criteria
Multi-Select Bulk Tag Apply
- Given the variant grid with at least two variants selected, when the user applies tags from the Bulk Tag panel, then all selected variants receive the chosen tags without creating duplicates. - Given selected variants have existing tags in the same dimension, when bulk applying a new tag value, then the previous value is replaced only if the user selects Override in the confirmation step; otherwise existing values are retained. - Given the bulk apply action is confirmed, then a completion toast lists counts of updated, unchanged, and failed variants and provides a link to retry failures. - Given up to 1,000 variants are selected, when applying tags, then the operation completes in 5 seconds or less.
Display Current Tags in Grid/Editor
- Given a variant tile is visible, then its current tags for angle, crop tightness, background, and shadow style are displayed as labeled chips with tooltips. - Given a variant has more tags than fit in the tile, then a “+N more” chip is shown that expands on click. - Given the user clicks a tag chip, when the inline editor opens and a value is changed, then the change is saved and reflected in the tile within 200 ms without page reload.
Quick-Pick Presets and Recents Persistence
- Given the Quick-Pick Presets menu is opened, when the user selects a preset, then all corresponding tag dimensions are populated in one action and previewed before confirmation. - Given the user applies presets or tags, then the last 20 unique tags and last 10 presets are stored per user and appear in Recents on next session and device. - Given the Recents list is populated, then items are ordered by most-recently-used and each entry includes a one-click apply action. - Given the user chooses Clear Recents, then all stored recent tags and presets are removed and no longer appear.
Inline Validation and Conflict Detection
- Given the tag editor is open, when the user selects more than one value for a single tag dimension, then the save action is disabled and an inline message states “Only one value allowed for [dimension]”. - Given the user selects mutually exclusive values, then the conflicting chips are highlighted and a conflict badge appears in the header. - Given an inline validation error is shown, then the error message is announced via aria-live within 500 ms and remains visible until resolved.
Visual Conflict Resolution and Preview Summary
- Given multiple variants with mixed existing values are selected, when opening the Bulk Tag panel, then each tag dimension with mixed values displays a conflict banner with options: Keep existing, Override with [value], Skip. - Given the user adjusts conflict options, then a live preview shows before/after counts per dimension for the selected set. - Given the user confirms, then the result matches the selected conflict options and the summary modal lists counts per outcome and any failures with reasons.
Keyboard Shortcuts and Undo/Redo
- Given focus is in the grid, when the user presses Ctrl/Cmd+A, then all visible variants are selected; Arrow keys move focus; Space toggles selection on focused tile; Shift+Click extends selection. - Given the user presses Ctrl/Cmd+B, then the Bulk Tag panel opens; pressing 1–9 applies preset slots 1–9; Enter confirms; Esc cancels without changes. - Given a tagging action completes, when the user presses Ctrl/Cmd+Z, then the last tagging change is undone across all affected variants; Ctrl/Cmd+Shift+Z redoes; at least 20 steps are maintained per session. - Given shortcuts are used, then no browser-default shortcut is overridden without providing an alternative and all actions are available via keyboard-only interaction.
Performance and Accessibility Compliance on Large Batches
- Given 1,000 variants are selected, when opening the Bulk Tag panel, then it renders in 1 second or less and input latency remains under 100 ms during interactions; for 5,000 variants, render time is 2 seconds or less and apply completes in 20 seconds or less with a progress indicator and cancel. - Given the UI is used with a screen reader and keyboard only, then all controls are reachable in logical order, have programmatic names, visible focus indicators, and no keyboard traps are present. - Given text and icons are displayed, then text contrast ratios are at least 4.5:1 and non-text UI components are at least 3:1, and interactive targets are at least 44x44 CSS pixels. - Given toasts and validation events occur, then they are announced via aria-live regions and the selection count changes are announced when selection changes.
Auto-Tag Suggestions
"As a time-constrained user, I want the system to suggest tags automatically so that I spend less time manually labeling variants."
Description

Leverage image analysis to infer likely values for angle, crop tightness, background type, and shadow style, returning suggestions with confidence scores. Surface one-click "apply all" or selective acceptance, and allow users to correct suggestions to improve future recommendations via a feedback loop. Run suggestions asynchronously during batch processing with clear status indicators and fallbacks to schema defaults. Provide workspace-level controls to enable/disable and configure which categories use auto-tagging.

Acceptance Criteria
Async Suggestions and Status Indicators in Batch
Given a batch upload of 100 product images, When processing begins, Then the UI remains interactive and other edits are available while auto-tagging runs asynchronously. Given an image is queued for analysis, When the worker has not started, Then the per-image status badge displays "Pending" within 2 seconds of queueing. Given an image analysis has started, When the worker picks up the job, Then the badge displays "Analyzing" within 2 seconds of start. Given analysis completes successfully, When suggestions are generated, Then the badge displays "Ready" and the UI shows suggested values for angle, crop_tightness, background_type, and shadow_style each with a confidence score. Given analysis fails, When an error occurs, Then the badge displays "Failed" with an error tooltip and a visible "Retry" control. Given normal system load, When 100 images are processed, Then 95% reach "Ready" within 3 minutes of upload.
Confidence Scores Display and Threshold Handling
Given suggestions are available, When displayed, Then each suggested tag shows a confidence score from 0.0% to 100.0% with one decimal precision and a tooltip explaining the score meaning. Given the workspace default confidence threshold is 70%, When a suggestion's confidence is below threshold, Then it is visually flagged and excluded from auto-apply actions. Given an admin updates the confidence threshold to X% in workspace settings, When new suggestions are generated, Then the new threshold is used to determine auto-apply eligibility. Given a suggestion's confidence is ≥90%, When rendered, Then it is marked with a "High confidence" indicator. Given no confidence score is available due to service degradation, When suggestions are shown, Then the UI displays "N/A" for confidence and disables auto-apply by default.
One-Click Apply All and Selective Acceptance
Given suggestions are ready for a batch, When the user clicks "Apply all", Then all suggestions at or above the configured threshold are applied to their respective images and fields. Given some suggestions are below threshold, When "Apply all" is clicked, Then those suggestions are skipped and a summary indicates the number skipped per field. Given a user selects specific images and fields, When they click "Apply selected", Then only the chosen suggestions are applied. Given suggestions have been applied, When the user clicks "Undo", Then the last apply operation is reverted for all affected images and fields. Given suggestions are applied, When the project is reloaded, Then the applied tag values persist in variant metadata.
Corrections Feedback Loop and Audit Trail
Given a suggestion is incorrect, When the user edits the tag to the correct value and submits, Then a feedback event is recorded with image ID, field, suggested value, corrected value, confidence, timestamp, and user ID. Given a correction is submitted, When the feedback pipeline processes events, Then the event is available in the feedback store within 5 minutes and visible in an admin report grouped by field and outcome. Given feedback collection is enabled, When a correction is recorded, Then a learning job is queued within 15 minutes with a reference to the feedback batch. Given the workspace disables "Use corrections to improve", When new corrections are submitted, Then they are stored for audit but excluded from the training dataset and marked as opted-out.
Fallback to Schema Defaults on Low Confidence or Failure
Given auto-tagging is enabled, When a required field's suggestion is missing or below the threshold, Then the workspace schema default for that field is assigned and labeled "Default applied" in the UI. Given image analysis fails, When exporting tags, Then schema defaults are used for missing fields and a warning entry is written to the export log for that image. Given a user later applies or corrects tags, When defaults exist, Then the user-specified values replace defaults and the default label is removed.
Workspace-Level Controls and Category Configuration
Given the workspace settings page, When an Admin toggles Auto-Tag Suggestions off, Then no new analyses are queued for subsequent uploads and "Apply all" controls are hidden or disabled. Given category-specific auto-tagging is configured for selected categories, When an upload is assigned to a non-enabled category, Then auto-tagging is not triggered and schema defaults are used. Given a non-admin user accesses auto-tag settings, When they attempt to change a setting, Then the change is blocked and an explanatory message is shown. Given settings are changed and saved, When new uploads occur, Then the new configuration takes effect within 1 minute and an audit log entry is created with actor, timestamp, and diff.
Export and Filename Integration After Applying Tags
Given tags have been applied, When images are exported as files, Then filenames include tag tokens per the workspace naming template reflecting angle, crop_tightness, background_type, and shadow_style values. Given tags have been applied, When a CSV is exported, Then the CSV includes columns angle, crop_tightness, background_type, shadow_style and corresponding confidence columns with the values used at apply time. Given defaults were used due to low confidence or failures, When exporting filenames or CSV, Then default values are output and confidence columns show "N/A" for those fields.
Tagged Filename & CSV Export
"As a marketplace seller, I want tags included in filenames and CSVs so that downstream tools and partners can reliably track performance by variant attributes."
Description

Enable configurable templates to embed tag key/value pairs into exported filenames and include tags as dedicated columns in CSV exports. Support per-marketplace presets and constraints (sanitization, length limits, separators), collision-safe disambiguation, and preview before export. Ensure tags remain mapped to stable variant IDs across ZIP, direct download, and S3 exports. Provide options to select which tag categories are exported and in what order.

Acceptance Criteria
Filename Template Embeds Tags (Per-Marketplace Preset)
Given a marketplace preset "Amazon" with constraints: allowedChars [A–Z,a–z,0–9,_,-], spaceReplacement "_", keyValueSep "-", tagPairSep "__", maxFilenameLength 80, preserveExtension true And a filename template "{sku}__{variantId}__{tag:background}-{tag:crop}-{tag:angle}" And a variant with id V123, sku "HK-123", and tags: background "pure white", crop "tight", angle "45" When the user exports images via ZIP and via direct download Then the exported filename for this variant is "HK-123__V123__pure_white-tight-45.jpg" on both channels And no disallowed characters appear; spaces are replaced per preset; the extension is preserved And the filename length is <= 80 characters And tags not referenced by the template do not appear in the filename
CSV Tag Columns and Order Selection
Given the user selects tag categories [background, crop, shadow_style] and orders them [crop, background, shadow_style] When exporting the CSV Then the CSV header begins with ["variantId","filename","crop","background","shadow_style"] And each row’s tag values appear under the correct columns in the chosen order And missing tag values are exported as empty cells And the CSV is RFC 4180 compliant (values with commas/quotes are properly quoted) And the number of rows equals the number of exported variants
Sanitization, Separators, and Length Limits per Preset
Given a marketplace preset "Etsy" with constraints: keyValueSep "=", tagPairSep "_", spaceReplacement "-", maxFilenameLength 60, disallowedChars [/:*?"<>|] And a variant whose tag values contain spaces, unicode, and disallowed characters (e.g., background "pure/white", crop "über tight") When generating filenames Then disallowed characters are removed or replaced per preset; spaces become "-"; unicode is normalized to NFC And the configured separators are applied exactly as set And if the filename would exceed 60 characters, it is truncated before the extension while preserving the extension And the resulting filename conforms to all preset constraints
Collision-Safe Filename Disambiguation
Given two or more distinct variants produce the same filename after applying the template and sanitization When preparing the export Then the system appends a deterministic suffix "-{variantId}" to each colliding basename And if the suffix would exceed the max length, the basename is truncated to accommodate the suffix and extension And all filenames in the export are unique with zero overwrites And the preview displays the count of auto-disambiguated files
Accurate Preview Before Export
Given the current filename template, selected tag categories, and marketplace preset When the user opens the export preview Then at least the first 50 variants (or all, if fewer) display their final filenames and CSV row snippets exactly as they will export And any truncations, replacements, or disambiguations are highlighted with non-blocking warnings And proceeding to export produces outputs that exactly match the preview for the sampled items
Stable Variant ID Mapping Across ZIP, Direct, and S3
Given variants with stable IDs and identical export settings When exporting the same selection via ZIP, direct download, and S3 Then the filenames and CSV rows are identical across channels (excluding storage-location fields) And each CSV row maps the same variantId to the same filename in all channels And if {variantId} is omitted from the filename template, a manifest CSV still maps variantId to filename consistently across channels And S3 exports write one object per filename with keys derived from the final filename; the object count equals the CSV row count
Independent Tag Selection for Filenames vs CSV
Given the user selects filename tag categories [background, crop] with order [crop, background] And selects CSV tag categories [angle, shadow_style] with order [shadow_style, angle] When exporting Then filenames include only crop and background in the specified order And the CSV contains only shadow_style and angle columns in the specified order (in addition to required columns like variantId and filename) And no unselected tag categories appear in either output
Cross-Test Tag Rollup Analytics
"As a growth marketer, I want rollup insights by tag across tests so that I can identify visual patterns that consistently improve CTR and apply them to future shoots."
Description

Aggregate performance metrics (e.g., CTR, CVR, impressions) by tag values and combinations across projects, time ranges, marketplaces, and categories. Provide filters, segment comparisons (e.g., tight crop + pure white vs. medium crop + lifestyle), and statistical significance indicators with sample size thresholds. Offer charting, pivot tables, and CSV exports. Implement caching and incremental refresh to scale on large datasets while respecting workspace permissions and data privacy.

Acceptance Criteria
Filtered Tag Rollup Across Projects, Time, Marketplaces, and Categories
Given I am an Analyst in workspace W with access to Projects P1…Pn And tag values exist for angle, crop_tightness, background, shadow_style And metrics are defined as CTR = clicks/impressions and CVR = conversions/clicks When I open Analytics > Tag Rollup and apply filters: time range [start,end], Projects [P1,P2], Marketplaces [M1,M2], Categories [C1,C2] Then the aggregates by tag value reflect only filtered data and match source counts within ±0.1% relative error (or absolute difference ≤1 for counts <1000) And CTR and CVR are computed using filtered counts and displayed to 2 decimal places And groups with missing tag values are labeled "Unlabeled" And results are deterministically sorted by impressions descending by default
Multi-Tag Segment Comparison with Statistical Significance
Given two to five segments defined by tag combinations (e.g., crop_tightness=tight AND background=pure_white) And global filters for time range, marketplace, category, and project are applied When I run a comparison on CTR and CVR Then each segment displays impressions, clicks, CTR, conversions, CVR And lift versus the selected baseline is shown with 95% CI and p-value And significance is marked "Significant" when p<0.05 via two-proportion z-test and sample thresholds are met (CTR: impressions≥1000 per segment; CVR: clicks≥200 per segment), otherwise "Insufficient sample" And results are consistent for identical filters and segments across repeated runs
Pivot Table for Tag Dimensions and Metrics
Given I open the Pivot view When I add dimensions angle, crop_tightness, background, shadow_style, marketplace, category, project, date_month to rows/columns And I select metrics impressions, clicks, CTR, conversions, CVR Then the pivot renders with totals and subtotals whose grand totals equal the flat aggregate within ±0.1% And I can sort by any metric ascending/descending And up to 50,000 visible rows are supported with virtual scrolling; larger results prompt CSV export
Time-Series Charts with Segment Overlays
Given I choose a metric (CTR, CVR, impressions) and a time grain (day, week, month) with workspace timezone TZ And I optionally add up to 3 segments by tag combinations When I render the chart Then the series honor all global filters and timezone TZ And missing periods are represented as zero values, not gaps And tooltips show date, segment (if any), and exact metric values And toggling segments on/off updates the chart without page reload
CSV Export of Tag Rollup and Comparisons
Given a current analysis state (filters, pivot or comparison selection) When I click Export CSV Then the CSV includes columns: angle, crop_tightness, background, shadow_style, marketplace, category, project, date_grain, impressions, clicks, CTR, conversions, CVR, and when applicable lift, p_value, ci_low, ci_high, significance And rows and totals match the on-screen results under the same filters And numbers use dot decimal separator, no thousand separators, UTF-8 encoding And exports are capped at 1,000,000 rows; if exceeded, I am prompted to refine filters or schedule a background export
Caching and Incremental Refresh for Large Datasets
Given workspace W contains ≥50M event rows across projects And cache TTL is 15 minutes and incremental refresh window is the last 24 hours When I execute a query with a warm cache entry Then results return within 3 seconds P95 When I execute a cold query on ≤100M rows Then results return within 12 seconds P95 When new data lands within the last 24 hours Then incremental aggregates update within 10 minutes without full recomputation And a "Last updated" timestamp and data freshness badge are shown on analytics views
Workspace Permissions and Data Privacy Enforcement
Given I am a user in workspace W without access to workspace W2 When I access Tag Rollup Analytics and perform exports Then only data from workspace W is visible and exportable And attempts to access data from other workspaces return 403 with no data leakage And analytics outputs include only aggregated metrics and tag fields (no user-level identifiers or PII) And exports and manual refresh actions are recorded in an audit log with user, timestamp, and scope
Tag-Based Search & Saved Filters
"As a content manager, I want to filter and save views by tags so that I can quickly locate and reuse specific variant sets."
Description

Add global search and advanced filtering on the asset library and test results by tag values with AND/OR/NOT operators and multi-select support. Show result counts and pill-style badges, and allow saving, naming, and sharing filters within a workspace. Provide deep links to prefiltered views and ensure fast response via indexed storage optimized for tag queries. Persist last-used filters per user session and profile.

Acceptance Criteria
Global Tag Search with Boolean Operators and Multi-Select
- Given a user is on the Asset Library or Test Results view with variants tagged (e.g., angle, crop tightness, background, shadow style), when they construct a filter using AND/OR/NOT and parentheses, then the query evaluates with precedence NOT > AND > OR and respects explicit parentheses. - Given multiple values are selected within the same tag key, when the user chooses "Match any" then values are combined with OR; when they choose "Match all" then values are combined with AND. - Given values are selected across different tag keys, when no override is set then the groups are combined with AND by default. - Given the user includes a NOT condition for a value, when the query is applied then any variant containing that value is excluded from the results set. - Given the user enters an invalid or incomplete expression, when attempting to apply the filter then the Apply action is disabled and an inline error describes the issue until corrected. - Given tag values are structured, when matching string values then matching is case-insensitive and exact on the stored value (no substring matches) unless the user explicitly chooses a contains operator. - Given identical queries are applied on Asset Library and Test Results for the same workspace, when executed then both surfaces return consistent sets for overlapping items (subject to each surface’s scope).
Pill Badges and Live Result Counts
- Given one or more filters are applied, when the query is executed then a pill badge is rendered for each clause showing key, operator, and value(s) (e.g., Background = Pure White, NOT Shadow = Drop). - Given a pill’s remove action is clicked, when it is removed then the query re-runs and the results and total count update accordingly. - Given any filter change (add/remove/toggle operator), when the new query is applied then the total results count updates in the header to the exact number returned by the backend. - Given pagination is enabled, when navigating pages under an active filter then the total count remains stable and page counts are accurate for the filtered set. - Given the filter yields zero results, when the results load then an empty state appears with the count set to 0 and a visible Clear All action restores the unfiltered state. - Given at least one filter is active, when Clear All is clicked then all pills are removed and the unfiltered total count is shown.
Save, Rename, Delete, and Share Filters
- Given a user has an active query, when they click Save Filter and enter a name 1–60 characters using letters, numbers, spaces, underscores, or dashes then the filter saves successfully. - Given a saved filter name must be unique per workspace (case-insensitive), when the user attempts to save a duplicate name then an error is shown and the save is blocked. - Given a saved filter, when the owner renames or edits its definition then the changes are persisted and visible to users with access. - Given sharing controls, when the owner sets visibility to Workspace then all members can discover and apply the filter; only the owner and workspace admins can edit or delete it. - Given a user with viewer permissions, when they access a shared filter then they can apply it but cannot modify its definition or name. - Given a saved filter is applied, when compared to an ad-hoc application of the same definition then the results set is identical. - Given a saved filter is deleted, when another user attempts to open it from history then a not-found message appears and no filter is applied.
Deep Links to Prefiltered Views
- Given a user has an active filter, when they copy a deep link to the current view then the link encodes the full query definition and target surface (Asset Library or Test Results). - Given the deep link is opened by an authenticated user with workspace access, when the page loads then the exact filter is applied and the results and counts match the sharer’s view (modulo data changes). - Given the deep link is opened by a user without access to the workspace, when the page loads then a 403/permission error is shown and no data is leaked in the URL beyond opaque identifiers. - Given a saved filter is later renamed, when using a previously generated deep link then the link still reconstructs the encoded query (not the old name) and yields the same filtering behavior. - Given an invalid or tampered deep link, when opened then the app shows an invalid filter message and defaults to an unfiltered view upon dismissal.
Query Performance and Scale SLAs
- Given indexed storage is available, when executing tag-based queries with up to 5 clauses on a workspace containing up to 2,000,000 variants then p95 server response time is ≤ 500 ms and p99 is ≤ 1200 ms under a sustained concurrency of 25 queries/second. - Given pagination and sorting are applied to a filtered result, when requesting subsequent pages then response times meet the same p95/p99 targets. - Given cold start or cache miss conditions, when a query is executed then the first response still meets p99 ≤ 1200 ms. - Given the system is under load, when SLAs cannot be met then requests are rate-limited with a 429 and retriable-after header rather than timing out; timeouts (≥ 10 s) occur in ≤ 0.1% of requests.
Index Freshness After Tag Updates
- Given a user adds, updates, or removes tag values on variants, when re-querying then changes are reflected in results within 10 seconds at p95 and 30 seconds at p99. - Given a bulk operation updates tags on 10,000 variants, when the operation completes then all affected queries reflect the new values within 2 minutes at p99. - Given a variant is deleted or archived, when filtering on any of its tag values then it no longer appears in results after index refresh within the stated SLAs. - Given index refresh is in progress, when a query is executed then results are consistent (no duplicate or partial items) and eventual completeness converges within the freshness window.
Persist Last-Used Filters per User and Profile
- Given a user applies a filter on the Asset Library, when they return to the Asset Library in the same session then the last-used filter is auto-restored. - Given cross-session persistence, when the same user returns on another device or a new session then the last-used filter is restored based on their profile for that surface within 30 days of last use. - Given a user switches workspaces, when returning to a surface then the last-used filter restored is specific to that workspace and surface. - Given the user clicks Reset Filters, when the page reloads then no filter is restored for that surface until a new filter is applied. - Given a last-used filter references tag values that no longer exist, when restored then the app warns of missing criteria and applies only the valid portions of the filter.
Tag API & Webhooks
"As a developer, I want programmatic access to tags and events so that I can integrate PixelLift with our PIM and reporting pipelines."
Description

Provide authenticated REST/GraphQL endpoints to create, read, update, and delete tags on variants, retrieve the tag schema, and perform bulk operations. Ensure idempotency, pagination, and rate limiting, with OAuth scopes restricting schema administration versus tag application. Emit webhooks for tag changes, export events, and schema version updates. Publish API documentation and lightweight SDKs to accelerate integration with PIM and analytics systems.

Acceptance Criteria
Idempotent Tag CRUD on Variants (REST & GraphQL)
Given a valid OAuth token with scope tags.write and an existing variant ID When the client creates or updates a tag via REST (POST/PUT/PATCH /variants/{id}/tags) or GraphQL mutation and supplies an Idempotency-Key K, then repeats the exact same request with K within 24 hours Then the first request returns 201 Created (create) or 200 OK (update), the replay returns 200 OK with an identical response body, and the variant’s tag state is unchanged Given a request to delete a tag via REST (DELETE /variants/{id}/tags/{tagKey}) or GraphQL mutation When the operation succeeds Then the API returns 204 No Content and a subsequent GET shows the tag is absent (404 for tag key) Given a tag payload that violates the active schema (unknown key, wrong type, or invalid enum) When the client attempts create/update Then the API returns 422 Unprocessable Entity with machine-readable error codes and field paths Given an unauthenticated request or a token lacking tags.write When attempting tag create/update/delete Then the API returns 401 (unauthenticated) or 403 (insufficient scope) without altering state
Versioned Tag Schema Retrieval & Admin Scopes
Given a token with scope schema.read When the client requests GET /tag-schema or the GraphQL schema query Then the API returns 200 with schema name, semantic version, field definitions (keys, types, enums, required flags), and an ETag header Given a request with If-None-Match matching the current ETag When the schema has not changed Then the API returns 304 Not Modified Given a token without schema.read When requesting the schema Then the API returns 403 INSUFFICIENT_SCOPE Given a token with scope schema.admin When the client creates/updates the tag schema via REST/GraphQL Then the change is persisted, the schema version increases by one, and the response echoes the new version and updated fields Given a token without schema.admin When attempting to mutate the schema Then the API returns 403 INSUFFICIENT_SCOPE and no changes are applied
Bulk Tag Operations with Partial Success and Idempotency
Given a bulk request containing multiple variant-tag assignments and a client-supplied idempotency key J When the client submits POST /bulk/tag-apply (or equivalent GraphQL) with J Then the API returns 202 Accepted with a job resource URL; resubmitting the same payload with the same J returns 200/202 with the same job reference and does not create a duplicate job Given a bulk job with a mix of valid and invalid items When the job completes Then GET /bulk/jobs/{jobId} returns status=completed, per-item results (success or failed), error codes for failures, and only valid items are applied; within a single variant, updates are atomic (no partial tag writes) Given an invalid bulk payload (malformed JSON or missing required fields) When submitted Then the API returns 400 with validation errors and no job is created
Cursor-based Pagination for Listing Tags and Variants
Given a request to list tags or tagged variants with a limit parameter L When GET /tags?limit=L or equivalent listing is called Then the response includes at most L items, a deterministic ascending sort (created_at, id), and a next_cursor if more items remain Given a follow-up request with next_cursor When the client requests the next page Then the API returns the next non-overlapping page; when no further data exists, next_cursor is absent Given an invalid or expired cursor When used in a request Then the API returns 400 INVALID_CURSOR and no data is returned
Rate Limiting with Standard Headers and Safe Retries
Given a client making requests within the allowed rate When calling any Tag API endpoint Then responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers with accurate values Given a client exceeds the allowed rate When additional requests are made Then the API returns 429 Too Many Requests with a Retry-After header; after waiting the indicated duration, the same idempotent request succeeds Given a burst followed by cooldown When the window resets Then X-RateLimit-Remaining reflects the refreshed quota
Signed Webhooks for Tag Changes, Exports, and Schema Updates
Given webhooks are configured with a shared secret When a tag is created, updated, or deleted on a variant Then a tag.changed event is delivered once per change with JSON payload containing event_id, event_type, occurred_at, tenant_id, variant_id, tag_key, and before/after values, and an X-PixelLift-Signature header containing an HMAC-SHA256 signature computed over the raw body Given the receiver returns a non-2xx status When delivering any webhook (tag.changed, export.started/completed/failed, schema.version.updated) Then delivery is retried at least 5 times with exponential backoff; a final failure is recorded and visible via a deliveries endpoint with timestamps and last_status Given an export job starts or finishes When events are emitted Then export.started and export.completed (or export.failed) webhooks include export_id, status, and artifact URLs (where applicable) Given a tag schema version is published When the version changes Then a schema.version.updated webhook is emitted with old_version and new_version; duplicate deliveries carry the same event_id for idempotent handling
API Documentation and SDKs (JS/TS, Python) Enable Integration
Given access to the developer portal When viewing the API documentation Then an OpenAPI 3.1 spec (REST) and GraphQL SDL are downloadable; docs include authentication (OAuth2), scopes, idempotency usage (Idempotency-Key), pagination (cursor), rate limiting headers, standardized error codes, and webhook signature verification examples Given installation of the official SDKs When using the JavaScript/TypeScript (npm) and Python (PyPI) packages Then each SDK exposes typed methods for tag CRUD, bulk operations, schema retrieval/updates, and webhook signature verification; provided Quickstart examples run successfully against a public sandbox Given the Quickstart guide When a developer follows the steps from a clean environment Then they can create a tag on a sample variant, list tags with pagination, and verify a webhook payload end-to-end without modifying SDK source code

Clean Compare

Review variants side‑by‑side with instant zoom, flicker toggle, and metric overlays. Add notes, approve a winner, and export a shareable summary (CSV + PDF) for stakeholders—speeding consensus and reducing back‑and‑forth on what to ship next.

Requirements

Side-by-Side Variant Viewer
"As a solo e-commerce seller, I want to compare multiple optimized variants side by side with instant zoom and pan so that I can quickly spot subtle differences and pick the best-performing image."
Description

Provide a responsive, high-performance viewer that displays 2–6 image variants in a synchronized, side-by-side grid. Support instant zoom (hover/click) up to 400% with smooth pan, maintaining image sharpness via high-DPI tiling and client caching. Ensure synchronized zoom/pan across all visible variants, keyboard navigation (←/→ to change selection, Z to toggle zoom), and lazy-loading for rapid initial render (<200ms TTI on broadband, <1s on 3G). Handle large source images (up to 12k px longest side) with progressive loading, memory caps, and fallback to downscaled layers. Provide responsive layouts (desktop, tablet, mobile) with pinch-zoom on touch, and preserve color accuracy (sRGB conversion) and ICC profile awareness. Include clear empty/error states, loading indicators, and a compact metadata bar (filename, dimension, size). Integrate with PixelLift’s asset store and variant generator so that the viewer auto-populates after a batch run and remembers last session state per project.

Acceptance Criteria
Responsive Side-by-Side Grid (2–6 Variants) with Metadata and States
- When 2–6 variants are available, the viewer renders the same number of tiles in a side-by-side grid without horizontal scroll on desktop (≥1280px) and with consistent 16px±1px gutters. - Layout adapts by breakpoint: desktop (≥1280px) up to 3 columns per row, tablet (768–1279px) 2 columns, mobile (≤767px) 1–2 columns with horizontal swipe. - Each tile displays a metadata bar showing filename, pixel dimensions (WxH), and file size; dimensions exactly match asset metadata and file size within ±1%. - Empty state appears when no variants are present with message "No variants to compare" and a primary action "Generate variants". - Per-tile error state shows placeholder, short error code/message, and a Retry control that refetches the asset; a successful retry replaces the error state with the image. - Per-tile loading indicator appears within 100ms of fetch start and persists until the first image preview is painted.
Zoom/Pan Fidelity and Synchronization
- Zoom supports 100%, 200%, and 400% levels via hover/click (desktop) and pinch/double-tap (touch); zoom interaction begins within 50ms of input. - Panning at any zoom level maintains ≥60 FPS on a mid-tier desktop (i5-class CPU, integrated GPU) and ≥30 FPS on mid-tier mobile (throttled 4x) under lab conditions. - High-DPI tiled rendering ensures displayed tiles meet or exceed device pixel ratio (no visible pixelation at DPR 2.0 at 200%). - Previously fetched tiles are cached client-side; revisiting the same viewport does not issue additional network requests for identical tiles. - Zoom and pan are synchronized across all visible variants; viewport alignment error is <1% of image dimensions or <4px, whichever is greater. - Toggling synchronization off (if control is provided) isolates navigation to the active tile; default is synchronized on.
Keyboard and Touch Interaction
- Left/Right Arrow changes the active selection to the previous/next variant and wraps around at ends; the active tile shows a visible focus indicator. - Pressing Z toggles between fit-to-screen and the last used zoom level, centered on the cursor (desktop) or tile center (touch). - Click-and-drag (desktop) or one-finger drag (touch) pans the image when zoomed; ESC resets the active tile to fit. - Pinch-zoom on touch scales smoothly up to 400% with inertial deceleration; double-tap toggles zoom within 100ms. - Input events are debounced to prevent duplicate actions per keydown/tap; holding an arrow key repeats navigation at 5–10 Hz. - All controls function when the viewer is embedded within a scrollable page without causing unintended page scroll at zoom >100%.
Performance and Lazy Loading Benchmarks
- Time to Interactive (TTI) for initial viewer render with 2 variants is <200ms on broadband (25 Mbps, 20ms RTT) and <1000ms on simulated 3G Fast (1.6 Mbps, 300ms RTT), measured in Lighthouse/ WebPageTest. - Offscreen tiles and higher-resolution tiles are lazy-loaded; initial paint uses a low-res preview (≤64 KB per tile) within 150ms on broadband. - Concurrent tile requests are capped at 6 per host; request prioritization favors visible tiles first. - First Contentful Paint of at least one low-res preview per visible tile occurs in <300ms on broadband and <800ms on 3G Fast. - Smooth scroll/zoom interactions maintain main-thread long tasks under 50ms during user input windows.
Large Image Robustness and Memory Management (Up to 12k px)
- The viewer successfully loads and displays images up to 12,000 px on the longest side using progressive, multi-LOD tiling without crashes or unhandled exceptions. - With 6 variants at 12k px, total image memory for decoded tiles remains ≤350 MB; upon reaching the cap, older/offscreen tiles are evicted and downscaled layers are used. - Progressive loading shows a preview within 300ms on broadband, with higher LOD tiles filling in without visible layout shift. - During pan at 200% zoom on 12k assets, frame rate does not drop below 30 FPS on mid-tier desktop and interactions remain responsive (input latency <100ms). - If device memory is constrained (lowered via PerformanceObserver or UA hints), the viewer automatically reduces LOD target to fit within available memory and logs a non-fatal warning.
Color Accuracy and ICC Profile Handling
- All images are rendered in sRGB; if an embedded ICC profile is detected, a single conversion to sRGB is applied before display. - Color validation against a reference conversion yields ΔE00 average <2.0 and max <4.0 across a standard patch set. - No double-conversion occurs for assets already in sRGB; colors match a baseline sRGB render within ΔE00 max <1.0. - On wide-gamut displays, the viewer correctly maps to sRGB without oversaturation or clipping (verified via known test images). - Metadata bar indicates when a non-sRGB profile was converted (e.g., tag “ICC converted”).
Asset Store Integration and Session Persistence
- After a variant generation batch completes, opening the associated project automatically populates the viewer with the latest 2–6 variants without manual import. - The viewer persists per-project state (active selection, zoom level, sync toggle, layout density, sort order) and restores it within 150ms after project load and authentication. - If no prior state exists, defaults are applied: synchronized on, fit-to-screen, grid density auto. - When assets are updated in the store, the viewer refreshes the affected tiles within 2 seconds and invalidates only the changed cache entries. - State persistence survives browser reloads and cross-device sessions for the same user account within the last 30 days.
Flicker Toggle with Pixel-Accurate Alignment
"As a reviewer, I want to flicker between two variants at the same zoom and position so that I can perceive micro-differences that are hard to see side by side."
Description

Enable a flicker (A/B) toggle that rapidly alternates between two selected variants within the same viewport while preserving zoom level and pan position. Provide blink and crossfade modes with adjustable interval (0.25–2.0s) and keyboard shortcut (F). Implement auto-alignment to minimize parallax and framing differences via feature matching and smart cropping bounds; expose a manual nudge control when auto-align confidence is low. Preload the next variant to avoid frame drops, achieving a stable 60fps on modern hardware. Maintain accessibility with ARIA labels and reduced-motion settings. Persist user preferences per project and log selection context for analytics. Integrate with the Side-by-Side Viewer selection model and respect the same caching and error-handling paths.

Acceptance Criteria
Flicker Toggle Preserves Viewport (Zoom and Pan)
Given exactly two variants are selected in the Side-by-Side Viewer and the user has set a zoom level and pan position, When the user activates the flicker toggle, Then both variants render within the same viewport with the current zoom and pan preserved across alternations. Given flicker is running, When the user adjusts zoom or pan, Then subsequent alternations reflect the updated zoom and pan with no jump or reset. Given the user exits and re-enters flicker within the same session, When flicker is reactivated, Then the last applied viewport zoom and pan are retained. Given fewer than or more than two variants are selected, When the user attempts to activate flicker, Then the flicker control is disabled and an explanatory tooltip is shown.
Blink and Crossfade Modes with Adjustable Interval
Given flicker is active in Blink mode, When the interval is set to any value between 0.25s and 2.0s (inclusive), Then alternation cadence matches the configured interval within ±5% and updates within 100ms of change. Given flicker is active in Crossfade mode, When the interval is adjusted between 0.25s and 2.0s (inclusive), Then the crossfade animation runs smoothly at 60fps without visible stutter and total cycle time matches the configured interval within ±5%. Given the user attempts to set an interval outside the allowed range, When input is applied, Then the value is clamped to the nearest boundary (0.25s or 2.0s) and the UI reflects the clamped value. Given flicker is running, When the user switches between Blink and Crossfade, Then the mode switch takes effect within 200ms and no blank frame is displayed.
Keyboard Shortcut F Toggles Flicker
Given the Side-by-Side Viewer has focus and exactly two variants are selected, When the user presses the F key, Then flicker toggles on if off, and toggles off if on. Given the user is typing in a text input or textarea within the app, When the F key is pressed, Then flicker is not toggled. Given a key is held down, When key repeat events fire, Then only the initial keydown toggles flicker and repeat events are ignored. Given flicker is toggled via keyboard, When focus is on the Flicker control, Then the control updates its pressed/aria-pressed state accordingly.
Auto-Alignment with Manual Nudge on Low Confidence
Given two variants with minor framing differences, When auto-alignment executes on flicker activation, Then residual misalignment at salient features is ≤1px median error across the central 80% of the image for the provided reference test set. Given the auto-alignment confidence for a pair falls below the low-confidence threshold, When flicker is activated, Then a manual nudge control is shown within 200ms indicating low confidence. Given the manual nudge control is visible, When the user nudges horizontally or vertically, Then the alignment shifts in increments of 1px with a minimum range of ±50px on both axes and updates immediately in the next frame. Given manual nudge has been applied, When the user clicks Reset Alignment, Then alignment reverts to the auto-aligned state. Given smart cropping is applied to achieve alignment, When flicker runs, Then no empty pixels or black bars are exposed and image bounds are respected at all times.
Preload and Performance at 60fps
Given two selected variants and flicker interval set within 0.25–2.0s, When flicker is activated, Then the next frame is preloaded and the first alternation begins within 50ms without a visible loading placeholder. Given flicker runs for 10 seconds on baseline modern hardware, When measuring frame times, Then average FPS is ≥60 and the 95th percentile frame time is ≤22ms with no more than 1 dropped frame per second. Given a cacheable variant was previously viewed in the Side-by-Side Viewer, When used in flicker, Then it is served from the same cache path with no additional network request. Given the second variant fails to load due to a network error, When flicker attempts to alternate, Then the standard Viewer error state is shown and flicker stops gracefully without freezing the UI.
Accessibility and Reduced Motion Compliance
Given assistive technologies are used, When navigating to the Flicker toggle and mode/interval controls, Then all controls are reachable via keyboard (Tab/Shift+Tab), expose appropriate roles and ARIA labels, and show a visible focus indicator. Given a user has OS or browser reduced-motion enabled, When Crossfade is selected, Then animations are replaced with an instant blink (no opacity animation) and interval timing is preserved. Given the user toggles flicker via keyboard or click, When a screen reader is active, Then an aria-live region announces "Flicker on" or "Flicker off" and announces the current mode and interval on change. Given accessibility features are enabled, When the user inspects the controls, Then the tooltip/help text for disabled states is programmatically associated and readable by screen readers.
Per-Project Preference Persistence and Analytics Logging
Given a project, When the user changes flicker mode or interval, Then the preference is saved per project within 200ms and persists across app reloads and logins. Given the user reopens the same project, When the Flicker panel loads, Then the last saved mode and interval for that project are restored before first render of the control state. Given the user starts and stops flicker, When analytics is enabled, Then events are logged including project_id, variant_ids, mode, interval, duration_ms, alignment_confidence, and manual_nudge_used, with no image pixels or PII captured. Given analytics is disabled by account or workspace policy, When flicker is used, Then no analytics events are sent and none are queued. Given a transient analytics transport failure occurs, When events are generated, Then they are queued and retried using the standard analytics retry policy shared with the Side-by-Side Viewer.
Metric Overlays and Sorting
"As a seller deciding which image to ship, I want to see key metrics overlaid on each variant and sort by them so that I can make a data-informed choice quickly."
Description

Overlay configurable performance and quality metrics directly on each variant thumbnail and full-view tile. Support metrics such as CTR (imported), conversion rate (imported), file size, compression ratio, background removal confidence, and color compliance flags. Allow users to toggle overlays, choose placement (corner badges vs. footer bar), and set color thresholds for quick scanning. Provide sorting and filtering by any metric (e.g., highest CTR, smallest size) and indicate metric provenance with tooltips linking to data sources. Handle missing data gracefully with placeholders and data-staleness indicators. Support metric ingestion via CSV upload and API connectors (marketplaces/analytics), with schema validation and mapping UI. Cache metric snapshots per comparison session to ensure consistent decision context and include them in exports. Integrate with permissions so only authorized users can see sensitive performance data.

Acceptance Criteria
Overlay Toggle and Placement Selection
Given I am in a Clean Compare session viewing variant thumbnails and full-view tiles, When I toggle "Show metric overlays" on, Then overlays render for all visible variants within 300ms per tile and remain synchronized during scroll and zoom. Given overlays are shown, When I select "Corner badges" placement, Then each metric appears as a badge in the chosen corner without covering more than 10% of the image area and remains legible at 100% and 200% zoom. Given overlays are shown, When I select "Footer bar" placement, Then a footer bar is displayed below each image with metrics truncated to fit the container and full values accessible via tooltip. Given I change overlay placement or toggle state, When I navigate within the session, Then my selections persist for the duration of the session and reset on starting a new session.
Supported Metrics Display and Formatting
Given overlays are enabled, When supported metrics exist (CTR, Conversion Rate, File Size, Compression Ratio, Background Removal Confidence, Color Compliance), Then each metric displays with label, value, and units as follows: CTR and Conversion Rate as percentages with 1 decimal; File Size in KB (0 decimals) or MB (1 decimal) with auto-unit selection; Compression Ratio as N:1 with 1 decimal; Background Removal Confidence as 0–100% (0 decimals); Color Compliance as Pass/Fail with icon and label. Given color thresholds are configured for one or more metrics, When values meet or breach thresholds, Then badges/bars render using the configured colors and a legend is available in overlay settings. Given I update color thresholds and save, When overlays re-render, Then updated color coding applies within 1 second to all visible variants and associated exports reflect the new thresholds.
Sorting and Filtering by Any Metric
Given multiple variants are present, When I sort by any metric (e.g., CTR, File Size) and choose direction, Then the list orders by that metric accordingly and ties break by variant name ascending. Given some variants have null values for the sort metric, When sorting, Then nulls appear last regardless of sort direction. Given I set filters (e.g., File Size <= 200 KB AND Background Removal Confidence >= 95%), When applied, Then only variants matching all conditions are shown and the result count is displayed. Given a sort and filters are active, When I clear them, Then the view returns to default order within 500ms. Given I export while a sort or filters are active, When the export is generated, Then the current sort and filters are applied to the exported data.
Provenance, Missing, and Stale Data Handling
Given a metric is displayed, When I hover its info icon, Then a tooltip shows metric name, source system, connector name, mapped field, and last updated timestamp in ISO 8601, plus an "Open source" link when a source URL is available. Given the tooltip shows a source URL, When I click it, Then the URL opens in a new browser tab. Given a metric value is missing for a variant, When overlays render, Then a placeholder "—" is shown with tooltip "No data" and the metric is excluded from filters and treated as lowest priority in sorts unless "Include missing" is enabled. Given a metric's last updated exceeds the configured staleness threshold, When overlays render, Then a yellow staleness badge appears with tooltip indicating the threshold and exact timestamp. Given a metric is computed locally (e.g., File Size), When the tooltip is opened, Then the source displays "PixelLift" and includes the computation method name.
Metrics Ingestion via CSV Upload and API Connectors with Mapping
Given I upload a CSV containing a unique variant or image identifier and metric columns, When validation runs, Then invalid headers, data types, and rows are reported with counts and line numbers and the import is blocked until issues are resolved. Given the mapping UI, When I map CSV columns to supported metrics and save, Then a preview shows the first 50 mapped rows with inferred types and unit normalization and unmapped columns are highlighted. Given a valid CSV, When I confirm import, Then a summary shows total rows processed, rows ingested, rows rejected with reasons, and provides a downloadable error CSV for rejected rows. Given an API connector is configured, When I trigger or schedule a sync, Then the job reports status (queued/running/success/failure), duration, records upserted, and next run time, and failures include actionable messages. Given metrics have been ingested, When I view overlays, Then new values are available in new sessions immediately and in current sessions only after clicking "Refresh metrics".
Session Snapshot Consistency and Export Inclusion
Given I start a comparison session, When metrics are first loaded, Then a snapshot timestamp is captured and all overlays, sorts, and filters use snapshot values for the entire session. Given new metrics arrive during an active session, When I do not refresh, Then overlay values and rankings remain unchanged. Given I click "Refresh metrics" and confirm, When the UI updates, Then a new snapshot replaces the old one and the view re-renders using the new snapshot values. Given I export CSV or PDF from the session, When the export is generated, Then it includes metric values, applied color threshold states, staleness indicators, and provenance details, and embeds the snapshot timestamp for reproducibility.
Permissions for Sensitive Performance Data
Given my role lacks "View performance metrics" permission, When overlays render, Then imported CTR and Conversion Rate values are masked as "Restricted" with a lock icon and are excluded from sort and filter options. Given my role has the permission, When overlays render, Then performance metrics are visible and available for sort and filter. Given I generate an export, When I lack permission, Then restricted metrics are omitted or masked in CSV and PDF; when I have permission, Then they are included. Given a share link is opened by an unauthorized viewer, When the summary loads, Then restricted metrics remain masked regardless of link possession.
Pinned Notes and Mentions
"As a stakeholder, I want to pin notes on specific parts of an image and mention teammates so that we can capture feedback precisely and resolve it faster."
Description

Provide lightweight annotation tools for adding pinned, location-aware notes on images and global notes per comparison session. Support rich-text (limited Markdown), variant-specific and cross-variant notes, and @mentions to notify collaborators via email/in-app alerts. Auto-save drafts, show author/timestamp, and allow edit/delete with permission checks. Display note indicators without obscuring critical content (smart placement and opacity). Include a filter to show/hide notes and a compact sidebar for threaded discussion. Ensure notes are included in exports and are searchable. Store notes with audit trails and link them to asset and project IDs for traceability. Respect privacy by redacting private notes from public share links when configured.

Acceptance Criteria
Pinned Location-Aware Notes on Variant Images
Given a Clean Compare session with multiple variants loaded and the user has Comment permission When the user selects Add Note and clicks a coordinate on Variant A Then a pin is created at that location, the note editor opens anchored to the pin, and the pin persists on Variant A after save Given a pin would overlap the detected foreground subject mask by more than 5% of its area or would render within 8px of image edges When the note editor opens Then the callout auto-offsets by at least 12px to avoid obstruction and the pin indicator renders at reduced opacity while idle Given the user marks the note as Cross-variant When the note is saved Then identical pins are shown on all variants at the same relative coordinates and the note is labeled Cross-variant Given the user marks the note as Variant-specific When the note is saved Then the pin and thread appear only on the selected variant Given a note is saved When it appears in the sidebar and on hover over the pin Then the author display name and creation timestamp (UTC) are shown
Global Session Notes (Non-Pinned)
Given a Clean Compare session is open When the user adds a Global Note from the sidebar Then a note with no pin is created, visible across the session, and not bound to any variant coordinates Given a Global Note is saved When it is displayed Then the author name and timestamp (UTC) are shown and it is tagged as Global Given exports are generated When notes are included Then Global Notes are included with Scope set to Global
Limited Markdown Formatting and Sanitization
Given the note editor supports limited Markdown (bold, italics, strikethrough, inline code, ordered/unordered lists, and links) When the user enters content using these syntaxes Then the rendered note displays the formatting consistently in preview, canvas tooltip, sidebar, and exports Given the user enters HTML tags, scripts, or unsupported Markdown When the note is previewed or saved Then content is sanitized, disallowed elements are stripped, and no executable code is stored or rendered Given the user adds a hyperlink When the note is saved Then the link renders with target opening in a new tab and rel attributes set to prevent opener access
@Mentions and Notifications
Given the user types @ followed by at least two characters in the note editor When collaborator suggestions are available for the current project Then an autocomplete list shows only project collaborators eligible to be mentioned Given the user mentions @alice and saves the note or reply When @alice has project access and notifications enabled Then @alice receives an in-app notification within 5 seconds and an email within 1 minute containing a deep link to the note Given the user edits a note without adding or removing mentions When the edit is saved Then no duplicate notifications are sent for existing mentions Given the user mentions a non-collaborator When the user attempts to save Then a validation error blocks save until the mention is removed
Auto-Save Drafts and Recovery
Given the user is typing a new note or reply When there is 2 seconds of inactivity or 5 seconds of continuous typing Then the current content auto-saves as a draft and a Saved indicator appears Given the browser/tab is closed, reloaded, or the network drops When the user returns to the same session within 7 days Then the last draft content is restored in the editor with a Restored Draft label Given a draft exists When the user posts or deletes the note Then the associated draft is cleared from storage Given an auto-save attempt fails due to connectivity When connectivity is restored Then the draft is retried within 10 seconds and the user is notified only if all retries fail
Edit/Delete Permissions and Audit Trail with Traceability
Given role permissions where Authors can edit/delete their own notes and Project Admins can edit/delete any note, and Viewers cannot When a user attempts to edit or delete a note Then the action is allowed only if permitted; otherwise a permission error is shown and no change occurs Given a note is created, edited, or deleted When the action is committed Then an immutable audit entry records action type, timestamp (UTC), actor ID, previous and new content (for edits), and client/device identifier Given a note exists When it is retrieved via API, UI, or export Then it includes projectId, comparisonSessionId, assetId for variant-specific notes (or null for global), and noteId for traceability Given a note is deleted When the deletion completes Then the note is soft-deleted (excluded from default views and exports) while the audit trail remains accessible to Admins
Search, Filter, Sidebar Threads, Export, and Privacy Redaction
Given notes exist across variants and global scope When the user opens the Notes sidebar Then a compact threaded view appears with each root note showing reply count, author, and timestamp; replies are nested one level; the sidebar can be resized between 240–320 px and toggled collapsed Given the user clicks a note in the sidebar When the canvas is focused Then the view scrolls/zooms to the pinned location (if present) and highlights the related pin without obscuring the underlying content Given the user applies filters (Show Pins On/Off; Scope: All/Variant/Global; Author; Mentioned User; Date Range; Has Unread) When filters are applied Then both canvas and sidebar reflect the filtered set and the selection persists for the session Given a user enters a keyword in the search field When search is executed Then notes are filtered by full-text match on content and author, with matches highlighted in results Given the user exports the comparison to CSV or PDF When export is generated Then all notes matching current filters are included with fields: content (rendered), author, timestamps, scope, coordinates (if any), mentions, and IDs (projectId, comparisonSessionId, assetId, noteId) Given a public share link is generated with Redact Private Notes enabled When the CSV or PDF is accessed via that link Then notes marked Private are omitted and other notes are included per viewer permissions
Winner Selection and Approval Workflow
"As a product owner, I want to approve a winner with rationale and clear status tracking so that our team can move to publishing without ambiguity."
Description

Allow users to mark a single variant as the winner, capture rationale, and set status per variant (Approved, Rejected, Needs Work). Support optional multi-approver rules (e.g., 1-of-N or all-of-N) with clear progress indicators and notifications. Lock the winner to prevent accidental edits, while allowing authorized users to revert with an audit log. Trigger downstream actions: tag the winner for export pipelines, update project status, and emit webhooks for integrations (e.g., PIM/DAM, marketplace listing prep). Provide safeguards against stale decisions by warning when source metrics have changed since the last review. Surface a comparison summary banner showing decision date, approvers, and key metrics at decision time.

Acceptance Criteria
Select Single Winner with Rationale
Given a review session with two or more variants and no winner finalized When the user clicks "Mark as Winner" on a variant and enters a rationale Then the system finalizes that variant as the single winner And the rationale, decider, and decision timestamp are recorded And the winner state is visible to collaborators within 5 seconds
Per-Variant Status Management
Given a review session with multiple variants When a user sets a variant's status Then the allowed values are only Approved, Rejected, or Needs Work And the selected status is saved immediately and shown to all collaborators And the change is logged with user, timestamp, and old/new values
Multi-Approver Rules and Progress Indicators
Given a project configured with a multi-approver rule (1-of-N or all-of-N) and a defined approver list When approvers submit approvals Then the progress indicator displays current approvals vs required (e.g., 1/3) And notifications are sent to pending approvers on request creation and to all approvers on completion And for 1-of-N, the first approval finalizes the decision and disables further approvals unless reverted And for all-of-N, the decision finalizes only when all required approvers have approved
Winner Lock and Authorized Revert with Audit Log
Given a winner has been finalized When a non-authorized user attempts to edit the winner or change the decision Then edit and decision controls are disabled with a lock message When an authorized user initiates Revert Winner and provides a reason Then the winner is unset, edits are re-enabled, and an audit entry captures who, when, reason, and prior winner and rationale
Downstream Actions and Webhooks on Finalization
Given a winner is finalized Then the winner is tagged "winner" for export pipelines And the project status updates to Decision: Winner Selected And a webhook event "winner.finalized" is emitted within 5 seconds with project_id, variant_id, rationale, approvers[], decision_timestamp, and metrics_snapshot And the webhook is emitted exactly once per finalization and appears in the integration log with delivery status
Stale Metrics Warning and Confirmation
Given source performance metrics have changed since the last review snapshot When a user attempts to finalize a winner or export based on the prior snapshot Then the system displays a warning indicating metrics changed since the last review, with last-reviewed timestamp And the user must either refresh metrics or confirm proceed And if proceeding, the decision records the new metrics snapshot and a note that changes were acknowledged
Decision Summary Banner
Given a winner exists for the comparison set When any collaborator opens the comparison view Then a summary banner displays decision date/time, approvers, rationale, and key metrics snapshot from decision time And the same banner data appears on shareable summary views (CSV/PDF)
Shareable Summary Export (CSV + PDF)
"As a seller sharing results with stakeholders, I want a one-click export to PDF and CSV so that everyone can review the decision and data without needing access to the app."
Description

Generate a shareable comparison summary in PDF and CSV that includes thumbnails, variant metadata, selected metrics, pinned notes, and the winner with rationale and approvers. Offer basic branding (logo, header), page layout options, and redaction of private notes or sensitive metrics. Provide a tokenized, view-only link with optional password and expiry, plus downloadable files. Run export generation as an asynchronous job with progress feedback, retries, and email notification on completion. Store artifacts in secure object storage with retention policies and access logs. Ensure accurate localization of dates, numbers, and paper size. Validate CSV schemas to support re-import into analytics tools. Integrate with the approval workflow and metric overlays so exports reflect the exact state at decision time.

Acceptance Criteria
Export Content Fidelity (Decision Snapshot)
Given a finalized Clean Compare decision with selected metrics, a chosen winner, rationale, approvers, pinned notes, variant thumbnails, and metadata When the user triggers "Export Summary" with default options Then the generated PDF and CSV include, for each variant, the same metadata fields and metric values that were visible at decision time, with no differences And the PDF displays the selected metric overlays and pinned notes content; the CSV contains columns for each selected metric and a notes column And the winner is clearly marked in both files along with rationale text and the list of approvers and the decision timestamp And any changes made after the decision timestamp do not alter the contents of the generated artifacts And thumbnails correspond to the correct variants with preserved aspect ratios and are not missing
Branding and Page Layout Options
Given a user configures branding (logo, header text) and page layout (paper size, orientation, variants-per-row) When generating the PDF Then the selected logo appears in the header and header text is displayed on each page without clipping And the PDF uses the chosen paper size and orientation; if none is chosen, the default matches the user's locale (US Letter for en-US; A4 otherwise) And variants are arranged per the selected layout (e.g., 2, 3, or 4 per row) with consistent margins and automatic pagination And branding is applied only to the PDF; the CSV remains unbranded And the PDF metadata includes the paper size and orientation used
Selective Redaction of Private Data
Given private notes and sensitive metrics exist in a comparison And the user enables redaction for private notes and/or selects specific sensitive metrics to exclude When the export is generated Then redacted fields are omitted from both PDF and CSV outputs And the PDF header includes a visible "Redacted" notice; the CSV includes a metadata row indicating which categories were redacted And the shareable link serves only the redacted artifacts; viewers cannot toggle redaction And no redacted content appears in access logs, filenames, or link metadata
Secure Shareable View-Only Link with Expiry
Given an export completes and the user requests a shareable view-only link with optional password and expiry When the link is created Then a unique, high-entropy token is generated and appended to the URL; the underlying files are not publicly readable And, if a password is set, the link requires the correct password and rate-limits failed attempts And the link expires at the configured date/time; subsequent requests return an expired response and are logged And the link page allows downloading the PDF and CSV with correct MIME types and Content-Disposition And the owner can revoke the link at any time; revocation immediately invalidates access And all accesses (success and failure) are recorded with timestamp, actor IP, and user agent
Asynchronous Export Job with Progress and Notifications
Given a user starts an export When the job is created Then the UI shows an asynchronous job with states (Queued, Processing, Rendering PDF, Rendering CSV, Uploading, Complete, Failed) and a progress indicator And transient failures are retried automatically with backoff up to a limit; upon final failure, the job shows Failed with an actionable error code And on completion or failure, an email notification is sent to the requester including job status and the shareable link (if successful) And repeated export requests with identical parameters reuse existing artifacts when still valid (idempotent) And the user can cancel a running job; cancellation stops processing and no partial artifacts are exposed
Secure Storage and Retention Compliance
Given export artifacts are produced When storing files Then they are written to object storage with server-side encryption enabled and private ACLs And object keys are non-guessable and do not contain sensitive data And retention policies are enforced to purge artifacts upon link expiry or at the configured retention interval, whichever comes first And access to artifacts occurs only via time-bound signed URLs generated by the shareable link service And storage access logs record create, read, and delete events with object key, time, and actor; logs are queryable by admins And file integrity is verified by checksum on upload and on retrieval
CSV Schema Validation and Localization
Given a CSV export is requested with a specific locale and schema version When the CSV is generated Then the first row declares the schema version and locale And headers match the documented schema exactly; data types per column are validated And dates/times format consistently per locale (or ISO 8601 if "Machine-friendly" option is selected); numeric metrics use the correct decimal separator for the locale And text fields are RFC 4180 compliant (proper quoting and escaping); no unescaped line breaks are present And the CSV passes re-import validation into supported analytics tools and PixelLift without schema errors And metric units are included either in column headers or a metadata row and match those shown in the PDF

LightLock Match

Automatically matches scene lighting (direction, intensity, and color temperature) to your product photo so shadows and highlights feel native. Delivers instant photorealism across batches, cutting manual relighting and reducing the uncanny “pasted” look that hurts trust and CTR.

Requirements

Light Direction & Intensity Estimation
"As a solo e-commerce seller, I want the lighting direction and intensity to match the background automatically so that my product looks naturally placed without manual relighting."
Description

Automatically infer the dominant light direction (azimuth/elevation), relative intensity (key/fill ratio), and softness from the target scene/background to build a per-image lighting model. Outputs a normalized lighting descriptor with confidence scores that downstream modules (relighting, shadow synthesis) consume. Includes fallbacks to a studio default profile when confidence is low, ensuring robust results across varied scenes. Integrates into the existing background-removal and composition pipeline, running on-device GPU or server-side accelerators for batch throughput. Expected outcome: consistent, accurate lighting parameters that drive photorealistic relighting with minimal manual intervention.

Acceptance Criteria
Accurate Light Direction on Labeled Scenes
Given a validation set of ≥500 composited product images with ground-truth dominant light azimuth/elevation, When the estimator runs, Then median spherical angular error ≤ 10° and 90th percentile error ≤ 18° per image. Given images where confidence_direction ≥ 0.8, When compared to ground truth, Then absolute angular error ≤ 8° for at least 80% of such images. Given the defined angular error metric (great-circle distance from predicted vs ground-truth azimuth/elevation on the unit sphere), When calculated, Then reported errors use this metric consistently across reports and dashboards.
Key/Fill Ratio and Softness Estimation Quality
Given a test set with photometric ground truth for key/fill ratio and softness index, When the estimator runs, Then key_fill_ratio MAPE ≤ 15% with p90 absolute percentage error ≤ 30%. Given the same set, When evaluating softness_index ∈ [0,1], Then MAE ≤ 0.12 and Spearman correlation with ground truth ≥ 0.70. Given images where confidence_intensity ≥ 0.8, When measuring key_fill_ratio, Then MAPE ≤ 10% on that subset.
Low-Confidence Fallback to Studio Profile
Given any image where confidence_direction < 0.4 OR confidence_intensity < 0.4 OR confidence_softness < 0.4, When estimation completes, Then descriptor.source = "fallback/studio-v1", fallback_applied = true, and all parameters are set to the studio default profile with confidences preserved. Given a batch of 1,000 images with at least 10% triggering fallback, When processed, Then the job completes with 0 unhandled errors and p95 per-image latency increase ≤ 50% versus non-fallback images. Given fallback-applied descriptors, When consumed by relighting and shadow synthesis, Then downstream modules complete without schema or runtime errors and produce outputs for 100% of those images.
Descriptor Schema, Normalization, and Versioning
Given any completed estimation, When emitting the descriptor, Then JSON includes: version, image_id, timestamp, source ∈ {"model","fallback"}, azimuth_deg, elevation_deg, key_fill_ratio, softness_index, confidence_direction, confidence_intensity, confidence_softness. Given emitted values, When validated, Then ranges hold: azimuth_deg ∈ [-180,180], elevation_deg ∈ [-90,90], key_fill_ratio ∈ [1.0,20.0], softness_index ∈ [0.0,1.0], confidences ∈ [0.0,1.0]. Given serialization, When measured, Then descriptor size ≤ 512 bytes and version == "lightdesc/1.0". Given a 10,000-image contract test, When parsed by relighting and shadow synthesis, Then 100% of descriptors validate against the schema with 0 parsing errors.
Batch Throughput and Latency on GPU/Server and On‑Device
Given a server with NVIDIA T4 GPU and 2MP images, When processing batch size 16, Then throughput ≥ 300 images/min and p95 latency ≤ 250 ms per image with peak GPU memory ≤ 2 GB. Given an Apple M2 laptop on-device GPU path, When processing 2MP images, Then throughput ≥ 30 images/min and p95 latency ≤ 800 ms per image. Given automatic device selection, When GPU is unavailable, Then CPU path executes with correctness parity: |Δazimuth| ≤ 1°, |Δelevation| ≤ 1°, |Δkey_fill_ratio| ≤ 0.05, |Δsoftness_index| ≤ 0.02 for ≥ 99% of images. Given a 10,000-image batch, When completed, Then estimation error/failure rate (non-recoverable) < 0.5%.
Pipeline Integration and Deterministic Output
Given the background-removal and composition pipeline, When run end-to-end, Then Light Direction & Intensity Estimation executes after background segmentation and before relighting, and writes the descriptor to the artifact store keyed by image_id. Given downstream relighting/shadow synthesis, When consuming descriptors, Then > 99.9% of images process without schema or contract errors. Given identical inputs and model version, When rerun twice, Then descriptors are deterministic within tolerances: |Δazimuth| ≤ 0.1°, |Δelevation| ≤ 0.1°, |Δkey_fill_ratio| ≤ 0.02, |Δsoftness_index| ≤ 0.01. Given the feature flag light_estimation_enabled toggled off, When pipeline runs, Then module is skipped without breaking downstream steps; toggled on, Then module runs and descriptors are produced for 100% of images.
Color Temperature & Tint Matching
"As a seller, I want the product’s color temperature to match the scene so that colors feel consistent and trustworthy across marketplaces."
Description

Estimate scene white balance (correlated color temperature in Kelvin) and green–magenta tint, then apply those parameters to the product layer during relighting while preserving brand-critical colors. The system performs color-managed adjustments (sRGB/AdobeRGB) with gamut-safe transforms and clamp ranges to avoid hue shifts on known brand colors. Integrates with the lighting descriptor from the estimation engine and provides batch-level uniformity options. Expected outcome: product tones harmonize with the scene’s light color, eliminating the mismatched white-balance look.

Acceptance Criteria
Per-Image CCT/Tint Estimation Accuracy
Given a product image composited into a scene containing a neutral reference or ground-truth CCT/tint When the system estimates scene white balance Then the estimated CCT error is ≤ 200 K relative to ground truth And the estimated tint error is ≤ 0.003 Δuv And repeated runs on the same input yield CCT variance ≤ 50 K and tint variance ≤ 0.001 Δuv And the applied CCT/tint on the product layer equals the estimated values
Brand Color Preservation During Relighting
Given brand-critical color swatches are provided in LAB or RGB with per-color tolerances When CCT/tint matching and relighting are applied Then the color difference per brand swatch is ΔE00 ≤ 2.0 in the chosen output color space And hue shift |Δh°| ≤ 1.0° And lightness change |ΔL*| ≤ 2.0 And clipped pixels within brand-swath regions are < 0.05% of those regions And if tolerance would be exceeded, gamut-safe clamping is applied and an event "brand_color_clamp" is recorded per image
Gamut-Safe, Color-Managed Transforms (sRGB/AdobeRGB)
Given an input with an embedded ICC profile (sRGB or AdobeRGB) and an explicit output color space selection When CCT/tint adjustments are computed and applied Then conversions use ICC-managed transforms between source, working, and output spaces And out-of-gamut colors are compressed using the configured rendering intent without channel overshoot And total pixels with any channel at 0 or 255 (or 1023/4095 for 10/12-bit) due to transform are < 0.1% of the image And neutral patches remain neutral with |a*| ≤ 1.0 and |b*| ≤ 1.0 after export And the output file embeds the correct ICC profile
Integration With Lighting Descriptor From Estimation Engine
Given the lighting descriptor provides direction, intensity, CCT, and tint When the relighting pipeline runs Then the applied CCT and tint equal the descriptor values unless a user override is set And the API response includes applied_cct, applied_tint, source="descriptor"|"override" And if the descriptor lacks CCT or tint, the estimator backfills the missing parameters and source="estimator" And intensity scaling does not alter the applied CCT/tint values
Batch-Level Uniformity Modes (Per-Image vs Batch-Locked)
Given a batch of N images and a user selection of Per-image Match or Batch-locked Match When Per-image Match is selected Then each image uses its own estimated CCT/tint and logs source per image When Batch-locked Match is selected with a master image or numeric values Then all images use the batch CCT/tint within ±50 K and ±0.001 Δuv of the specified batch values And any image whose estimate deviates by > 500 K or > 0.01 Δuv from the batch values triggers a "uniformity_large_deviation" warning in logs And the UI/API returns the batch values and per-image deviations
ICC/Profile Handling and Metadata Writeback
Given input files may include or omit ICC profiles and exports may target sRGB or AdobeRGB When processing completes Then untagged inputs are assumed sRGB unless an explicit input profile is provided via API/UI And exports embed the selected output ICC profile And XMP/EXIF metadata include wb:cct (Kelvin), wb:tint (Δuv), wb:source, and output_color_space And the API job result returns applied CCT/tint per image and any clamp/warning events
Physically Plausible Shadow Synthesis
"As a seller, I want realistic contact and soft shadows generated to match the scene so that images avoid the cut-out look and increase CTR."
Description

Generate contact shadows and soft shadows consistent with inferred light direction, intensity, and softness. Derive a ground-plane proxy from the background (or user-set plane) and produce a high-quality shadow matte with variable penumbra, depth-aware falloff, and adaptive opacity. Blend shadows with background textures without halos or clipping on white backgrounds. Provide controls for maximum opacity and softness caps to meet marketplace norms. Expected outcome: natural shadows that anchor the product to the scene, eliminating the ‘pasted’ appearance.

Acceptance Criteria
Directional Consistency with Inferred Light
Given an input product image and an inferred light vector (direction, intensity, temperature) When Physically Plausible Shadow Synthesis generates the shadow Then the shadow axis deviates ≤ 5° from the inferred light direction, measured by principal axis analysis And the mean shadow luminance in the umbra region matches the target intensity within ±10% And the shadow hue shift aligns with the inferred color temperature (ΔE00 ≤ 2 versus expected neutralized shadow) And when a user overrides light direction, the preview updates within 300 ms and the exported image reflects the override
Accurate Ground-Plane Projection and Contact
Given a derived ground-plane proxy with confidence ≥ 0.7 or a user-set plane When projecting the shadow Then the shadow makes contact with the product's support footprint with no gaps > 1 px at 100% zoom And the shadow foreshortening matches plane orientation within ±5° of the expected vanishing direction And if plane confidence < 0.7, the system defaults to the user-set plane and displays a 'Using user plane' indicator And the contact shadow peak opacity occurs within 3 px of the footprint boundary
Soft Shadow Penumbra and Depth-Aware Falloff
Given an inferred light softness parameter s and an object-to-plane distance map When rendering soft shadows Then penumbra width increases with distance, with slope within ±15% of the analytic soft-shadow model for s And the umbra-to-penumbra transition is smooth (no gradient reversals; max second-derivative sign changes ≤ 1 per 10 px) And shadow opacity decays with distance; the 1/e falloff length is within ±15% of the target for s
Seamless Blending Without Halos or White-Background Clipping
Given a background with textures or a pure white background (RGB ≥ 250) When compositing the shadow matte Then outside the shadow region (alpha ≤ 0.01) background pixels change by ΔE00 ≤ 1 compared to input And within the shadow region, no halos are detected (edge ring artifact area ≤ 0.5% of object silhouette area) And on pure white backgrounds, no clipping occurs (non-shadow pixels remain ≥ 250 and shadow pixels within the shadow remain ≥ 230) And SSIM in non-shadow regions between input and composited output is ≥ 0.99
User Controls for Maximum Opacity and Softness Caps
Given UI sliders for Max Shadow Opacity (0–100%) and Max Softness Cap (0–20 px Gaussian sigma) When a user adjusts these controls Then the preview updates within 200 ms and the exported output obeys the caps within ±2% And the selected values persist across a batch run and are applied consistently to all images in the batch And a Reset action restores documented defaults and updates both preview and exports
Shadow Matte Deliverables and Export Integrity
Given export formats PNG, TIFF, and JPEG When exporting images with shadow synthesis Then PNG/TIFF exports embed a 16-bit grayscale shadow matte as an unpremultiplied alpha channel, with an option to also save a separate matte file And JPEG exports bake the shadow into the background with the working ICC profile preserved And the composited result using the embedded matte matches the separate-matte composite with mean absolute difference ≤ 1 gray level (8-bit scale) And PNG/TIFF file size overhead due to the matte is ≤ 10% compared to the same image exported without the matte
Material-aware Specular & Highlight Adjustment
"As a seller, I want highlights and reflections adjusted based on material so that glossy items look convincingly lit without appearing overprocessed."
Description

Detect material categories (e.g., matte, glossy, metallic, fabric) and modulate specular intensity, roughness, and highlight color to match the scene’s light model. Preserve fine surface texture and micro-contrast while preventing plastic-like over-smoothing. Apply environment-tinted highlights using the matched color temperature and maintain 16-bit internal precision to avoid banding. Expected outcome: highlights and reflections feel native to the scene across diverse SKUs, improving realism without degrading detail.

Acceptance Criteria
Glossy Metallic SKU in Warm Directional Scene
Given an input product photo labeled as glossy metallic and a scene light model with warm CCT (3200–3800K) and a primary light direction, When Material-aware Specular & Highlight Adjustment is applied, Then the dominant specular highlight hue matches the scene: CCT difference <= 250K or Δuv <= 0.005 on sampled highlight pixels; And highlight lobe center aligns with the scene light vector within <= 10° angular error; And normalized peak specular intensity per highlight is within [0.60, 0.95] without clipping: fraction of pixels at value >= 254 (8-bit equivalent) <= 0.5%; And micro-scratch and brushed texture retention: high-frequency SSIM (1–8 px band) >= 0.95 vs. the input after removing low-frequency shading.
Matte Fabric SKU under Cool Softbox
Given a matte fabric product and a scene with cool CCT (5500–7000K) diffuse lighting, When Material-aware Specular & Highlight Adjustment is applied, Then specular intensity is suppressed: peak normalized specular <= 0.10 and no hard hotspots (highlight area peak-to-mean ratio <= 1.5); And weave texture is preserved: local RMS contrast in 1–4 px windows changes by <= 10% vs. input; And environment tint is present but subtle: CCT difference between faint highlight and scene <= 300K; And no haloing or edge glare: overshoot/undershoot < 2% within 3 px of material edges.
Mixed-Material Batch Consistency
Given a batch of >= 20 images with mixed materials processed under the same LightLock Match scene model, When Material-aware Specular & Highlight Adjustment is applied in batch mode, Then per-image highlight tint CCT variance across the batch <= 200K; And mean angular difference of highlight direction vs. the scene light <= 5° (max <= 12°); And per-material specular range compliance rate >= 98% (glossy/metallic in [0.60,0.95], matte/fabric <= 0.10, semi-gloss plastics in [0.15,0.50]); And any image violating thresholds is flagged in the batch report with material label and failing metric.
16-bit Internal Precision and Banding Prevention
Given any supported SKU and scene, When processing runs, Then all internal color and lighting buffers operate at >= 16-bit precision (integer or float) as recorded in debug or audit logs; And on a synthetic 12-bit gradient-plus-specular test image, longest run of identical luminance values in gradient regions <= 8 px after output quantization; And RMS quantization error in gradient regions <= 0.5/255 (8-bit normalized); And no posterization around highlights: adjacent-bin histogram counts in the top 10% luminance differ by <= 1 count step on downsampled 1D histograms.
Roughness Mapping Fidelity on Standard Material Chart
Given a calibrated material swatch chart with known roughness r ∈ [0,1] and a fixed scene light model, When Material-aware Specular & Highlight Adjustment is applied, Then measured highlight half-power width has Pearson correlation >= 0.85 with (1 − r); And relative error of lobe sharpness parameter vs. target <= 10% for glossy and metallic classes; And ordering of swatches by roughness is preserved with Kendall τ >= 0.8.
Detail Preservation and Anti-Plastic Look
Given a textured SKU (e.g., leather, brushed aluminum, woven cotton) with visible micro-contrast, When Material-aware Specular & Highlight Adjustment is applied, Then MTF50 in textured regions decreases by <= 5% vs. input (measured on slanted-edge patches or equivalent); And band-limited SSIM (2–16 px) >= 0.96 vs. input after removing global shading; And over-smoothing is limited: fraction of 5×5 windows in textured masks with local variance drop > 30% <= 0.3%.
Batch Presets & Consistency Controls
"As a seller managing batches, I want to apply consistent lighting settings across all images so that my catalog looks unified and professional."
Description

Enable creation and application of batch-level lighting presets that lock direction, intensity, softness, and color temperature across a set of images for catalog consistency. Provide auto-match per image with an option to snap to a batch baseline, plus preset import/export and versioned audit logs for reproducibility. Include outlier detection to flag images that deviate from the batch lighting model. Expected outcome: cohesive, repeatable results across large uploads with minimal per-image tweaking.

Acceptance Criteria
Create Batch Lighting Preset
Given I have uploaded images to a new batch and selected one reference image When I choose Create Preset from reference, enter a unique name, and save Then the preset stores lighting direction (azimuth 0–360°, elevation −90° to +90°), intensity (0–200%), softness (0–100%), and color temperature (2000K–9000K) And the preset appears in the Presets list with the saved name and version 1.0.0 And duplicate preset names within the workspace are rejected with a validation message And the saved preset can be preview-applied on the reference image showing locked values
Apply Preset to Batch With Snap-to-Baseline
Given a batch of N images and a selected preset When I enable Snap to Baseline and start processing Then all images are processed using exactly the preset's direction, intensity, softness, and color temperature And the per-image auto-match is bypassed And the standard deviation for each parameter across the batch equals 0 And each processed image is labeled Snapped in the UI
Auto-Match Per Image Default Mode
Given a batch of images without Snap to Baseline enabled When I process the batch Then each image's lighting direction, intensity, softness, and color temperature are determined by LightLock auto-match And the derived values are displayed per image for review And toggling Snap to Baseline and reprocessing replaces per-image values with the preset values without requiring re-upload
Outlier Detection and Flagging
Given a batch has been processed in auto-match mode and a batch lighting model has been computed When an image's deviation from the batch model exceeds thresholds (direction > 15°, intensity > 15%, color temperature > 500K, softness > 10%) Then the image is flagged as Outlier And thresholds are configurable at the batch level with the above defaults And the outlier count and filter are available in the batch view And choosing Snap selected to baseline reprocesses flagged images and removes the flag when the deviation falls within thresholds
Preset Import and Export With Integrity
Given an existing preset is selected When I export the preset Then a JSON file is downloaded containing parameters, metadata (name, description, createdBy, createdAt), version, and SHA-256 checksum And when I import the same file Then the preset is recreated with identical parameters and version and a matching checksum And importing a malformed or tampered file shows a validation error and does not create a preset And importing a preset with a duplicate name requires rename or auto-suffix before save
Versioned Audit Log and Reproducibility
Given audit logging is enabled When I create, edit, apply, import, export, version, or snap a preset, or process a batch Then an immutable audit entry is recorded with timestamp (UTC ISO 8601), actor, action, presetId, batchId, version, and before/after parameter deltas when applicable And editing preset parameters increments the version (minor for parameter changes, patch for metadata-only changes) And reprocessing the same batch with the same preset version produces identical per-image lighting parameter JSON and a matching processing-config checksum And audit logs are filterable by batchId or presetId and exportable as CSV
Per-Image Overrides Without Preset Mutation
Given a preset has been applied to a batch When I manually adjust lighting parameters for a single image Then the preset remains unchanged and the override is stored only for that image And a Reset to baseline option restores the image to the preset or auto-match values depending on mode And the override is captured in the audit log with before/after values And exporting the batch includes a sidecar JSON of per-image overrides
Manual Override & Fine-Tuning Controls
"As a seller, I want manual controls to fine-tune lighting when needed so that I can fix edge cases quickly without leaving the tool."
Description

Offer intuitive controls to adjust azimuth, elevation, intensity, color temperature, tint, shadow softness, and opacity at both batch and per-image levels. Start from the auto-matched baseline and apply non-destructive deltas with reset-to-auto options. Provide numeric inputs, slider granularity suitable for precision tweaks, and keyboard nudging for power users. Expected outcome: rapid correction of edge cases without round-tripping to external editors.

Acceptance Criteria
Batch-Level Overrides Apply Non-Destructively
- Given a selected batch of images, when the user adjusts any lighting control at the batch level, then only the selected images are updated and non-selected images remain unchanged. - Given auto-matched baselines exist, when a batch-level adjustment is applied, then the system stores per-image deltas relative to each image’s auto baseline and preserves the baseline values unchanged. - Given batch-level adjustments are active, when the user chooses Reset to Auto at the batch level, then all affected parameters for the selected images revert to 0 delta relative to auto baseline. - Given batch-level adjustments have been applied, when the user inspects any affected image’s control values, then the UI displays the delta values and not absolute baked values. - Given original source images, when batch adjustments are applied, then no destructive edits are committed to the source; only rendered previews/exports reflect the changes.
Per-Image Overrides Precedence and Reset Behavior
- Given batch-level adjustments exist, when a per-image override is set for a parameter, then that per-image delta takes precedence over the batch delta for that image and parameter. - Given a per-image override exists for a parameter, when the user clears the per-image value, then the image reverts to the batch delta for that parameter; if no batch delta exists, it reverts to the auto baseline (0 delta). - Given a single image, when the user triggers Reset to Auto for that image, then all parameters on that image reset to 0 delta regardless of batch settings. - Given a single parameter on an image, when the user triggers Reset to Auto for that parameter, then only that parameter resets to 0 delta and other parameters remain unchanged. - Given per-image overrides are present, when batch settings are modified, then only non-overridden parameters on that image are affected by the new batch values.
Precision Controls: Sliders, Numeric Inputs, and Keyboard Nudging
- Given sliders for azimuth, elevation, intensity, color temperature, tint, shadow softness, and shadow opacity, when displayed, then their ranges are: azimuth 0–360°, elevation −90–90°, intensity 0–200%, color temperature 2000–10000 K, tint −100–100, shadow softness 0–100%, shadow opacity 0–100%. - Given a slider is moved, then its step size is: azimuth 1°, elevation 1°, intensity 1%, color temperature 50 K, tint 1, shadow softness 1%, shadow opacity 1%. - Given a numeric input field is focused, when the user increments/decrements with Arrow keys, then the step equals the slider step; with Shift+Arrow it is 10× the slider step; with Alt/Option+Arrow it is 0.1× the slider step. - Given a numeric input, when the user types a value and presses Enter or the field loses focus, then the value is applied, clamped to the allowed range, and rendered in the preview. - Given azimuth wrapping, when a value exceeds 360° or goes below 0°, then it wraps within 0–360° while preserving the intended delta direction.
Real-Time Preview Accuracy and Performance
- Given an image is open in the editor, when the user adjusts any control, then the on-canvas preview updates within 150 ms of interaction end and within 300 ms during continuous dragging, with no visual tearing. - Given a grid view of up to 50 selected images, when a batch adjustment is applied, then all affected thumbnails update within 2 seconds. - Given a set of adjustments, when the user exports an image, then the exported result matches the preview within a pixel delta tolerance of ≤1% MAE or SSIM ≥ 0.99. - Given the preview is computing, when progress exceeds 300 ms, then a lightweight progress indicator is shown until the frame is fully updated.
Keyboard-Only Workflow and Reset Shortcuts
- Given keyboard-only navigation, when the user presses Tab/Shift+Tab, then focus moves through all controls in a logical order without requiring a mouse. - Given a focused slider or numeric input, when the user presses R, then that parameter resets to Auto (0 delta) for the current scope (per-image or batch) and the preview updates accordingly. - Given a focused image with overrides, when the user presses Ctrl/Cmd+R, then all parameters reset to Auto for that scope. - Given multiple images are selected, when the user uses keyboard increments, then batch adjustments apply to all selected images. - Given keyboard nudging, when the user holds the key down, then values repeat at a rate of 8–12 Hz and stop immediately on keyup.
Input Validation and Bounds Enforcement
- Given any control input, when an out-of-range value is entered, then the system clamps to the nearest valid bound and displays the clamped value within 100 ms. - Given a non-numeric entry in numeric fields, when entered, then the field rejects the character or reverts to last valid value on blur and shows inline validation state. - Given unit display, when values are shown, then units are consistently displayed as °, %, or K as applicable. - Given constraints, when intensity is set to 0%, then the lighting contribution is visually removed; when shadow opacity is set to 0%, then shadows are invisible; when shadow softness is 100%, shadows have fully soft edges without banding. - Given all parameters, when values change, then the state is saved to the current session immediately so that navigation within the app does not lose adjustments.
Real-time Preview & GPU Acceleration
"As a seller, I want instant previews and fast batch processing so that I can publish listings faster and save time."
Description

Deliver sub-second previews for up to 2048px images and high-throughput batch processing via GPU acceleration and smart caching of scene lighting descriptors. Use asynchronous queuing, progressive rendering, and tiling to maintain responsiveness under load. Provide graceful CPU fallback with adaptive quality when GPU is unavailable. Expected outcome: instant feedback during editing and fast batch completion that aligns with PixelLift’s promise of cutting editing time by up to 80%.

Acceptance Criteria
Sub-Second 2048px Preview Update
Given a 2048x2048 product image open in the editor with LightLock enabled And the system is on a host with available GPU When the user releases any LightLock control (direction, intensity, or color temperature) Then the preview updates to full-quality 2048px with TTUP P95 <= 700 ms and P99 <= 1000 ms over 100 interactions across cold and warm cache cycles And no UI freeze longer than 100 ms occurs during the interaction window And the preview visually matches the final render with SSIM >= 0.98 and CIEDE2000 <= 1.5
GPU-Accelerated Batch Throughput Under Load
Given a batch of 200 images at up to 2048 px with LightLock enabled on a GPU-capable host When processing starts with default concurrency Then effective throughput >= 120 images/minute and P95 per-image latency <= 1.5 s And overall job completes with transient-error retries (up to 2) yielding a failure rate < 1% And average GPU utilization during processing >= 60% and average CPU utilization <= 80%
Progressive Rendering Responsiveness While Dragging
Given a 2048 px image in the editor with LightLock controls visible When the user drags any LightLock control continuously for 3 seconds Then progressive preview frames render at >= 10 FPS with median input latency <= 50 ms and no frame stall > 200 ms And after release, a 1024 px resolve completes within 300 ms and full 2048 px within 700 ms (P95) And in-flight work is cancelled or reprioritized so obsolete tiles do not commit later than 50 ms after new input begins
Smart Caching of Lighting Descriptors
Given a sequence of 20 images shot in the same scene (shared scene hash) When LightLock is applied sequentially Then cache hit rate for lighting descriptors >= 75% and warm-cache TTUP improves by >= 30% versus cold-cache baseline And visual deviation between cached and recomputed descriptors stays within |Δdir| <= 5°, |Δintensity| <= 5%, |ΔCCT| <= 100 K And cache memory usage <= 256 MB with LRU eviction and automatic invalidation on scene-hash change
Asynchronous Queuing and Job Control
Given a user submits a new batch while another batch is running When the new job is enqueued Then the UI shows queue position within 200 ms and progress updates at least every 500 ms And the job starts within 2 s of a worker becoming available And cancel removes the job from the queue within 1 s; pause/resume take effect within 500 ms And the main UI thread experiences no blocks > 100 ms during enqueue or status updates
CPU Fallback With Adaptive Quality
Given the GPU is unavailable at app start or becomes unavailable during editing When the user edits with LightLock Then the system switches to CPU mode automatically within 300 ms and surfaces a non-blocking notice And while dragging, preview renders at adaptive quality (>= 1024 px) with TTUP P95 <= 1000 ms; after release, full 2048 px converges within 2500 ms And SSIM between CPU and GPU final renders >= 0.97 and ΔCCT <= 150 K; batch throughput in CPU mode >= 30 images/minute

Perspective Anchor

Detects the product’s base plane and camera angle, then aligns the scene’s ground plane and vanishing point to match—no more floating products or skewed countertops. One-tap horizon leveling and micro‑tilt controls keep compositions realistic and consistent across a set.

Requirements

Base Plane Detection Engine
"As a solo e‑commerce seller, I want PixelLift to reliably detect where my product sits so that my images don’t look like the item is floating or misaligned."
Description

Implement a robust vision module that identifies the product’s base plane from a single image by combining instance segmentation, edge/contour analysis, shadow cues, and learned geometric priors. The module outputs a plane normal, plane position, vanishing line candidates, and a confidence score, even on clean white or synthetic backgrounds. It must handle diverse product categories (e.g., shoes, boxes, bottles, electronics) and ambiguous bottoms by leveraging product masks from the existing background removal pipeline. Provide an overlay visualization (plane outline and contact region) for QA, and expose results via an internal API for downstream pose estimation and ground alignment. Designed to run within PixelLift’s batch processor with predictable latency and fallback modes when confidence is low.

Acceptance Criteria
Accurate Plane Normal and Contact Region Estimation
Given a labeled test set spanning shoes, boxes, bottles, and electronics with ground-truth base plane normals and contact regions When the Base Plane Detection Engine runs on 1024x1024 inputs Then the angular error between predicted normal and ground truth is <= 7 degrees for >= 90% of images and <= 12 degrees for >= 98% And the contact-region mask IoU with ground truth is >= 0.60 for >= 90% and >= 0.50 for >= 98% And the engine returns a finite, unit-length normal vector (||n|| within 1.0 +/- 0.01) and plane position parameters without NaN/Inf
Robust Detection on Clean White/Synthetic Backgrounds
Given a test subset of images shot on pure white or synthetic backdrops with minimal texture and available shadow cues When the engine runs Then the normal angular error is <= 10 degrees for >= 90% of images And the confidence score distribution has median >= 0.70 and no more than 10% of samples below 0.50 And failure-to-produce rate (missing required outputs) is 0%
Product Mask Utilization and Ambiguity Handling
Given a valid product mask from the background removal pipeline When the engine runs Then perturbing the mask by +/- 3 px (erode/dilate) changes the predicted normal by < 3 degrees and contact IoU by < 0.05 And when the mask is absent, the engine sets confidence <= 0.40, flags fallback_mode = true, and continues without throwing And on an ambiguous-bottom test set (e.g., spheres, soft bags), the engine sets confidence <= 0.50 and activates fallback
API Contract for Base Plane Outputs
Given the internal endpoint POST /internal/pose/base-plane:v1 with image_id and mask_id When invoked Then the response is HTTP 200 within 300 ms (p95) in-cluster and conforms to a JSON schema with fields: plane_normal:[x,y,z], plane_point:[x,y,z] or plane_offset_d, vanishing_lines:[{a,b,c,score}], confidence:[0,1], contact_region:{polygon|rle}, fallback_mode:boolean, fallback_reason:enum, version:string, timings:{} And plane_normal is unit-length within +/- 0.01; 1-5 vanishing_lines are returned sorted by score; confidence is within [0,1]; all numeric fields are finite And requests missing mask_id return 200 with fallback_mode=true and fallback_reason="missing_mask"
QA Overlay Visualization Generation
Given QA mode enabled via flag qa_overlay=true When the engine processes an image Then it writes an overlay PNG to the job artifact store with filename pattern {image_id}_baseplane.png And the overlay shows plane outline in green, contact region with 30-50% opacity, and text annotations of normal and confidence And the rendered overlay geometric alignment error vs the predicted contact polygon is <= 2 px average
Batch Processor Latency and Resource Bounds
Given a batch of 100 images at 1024x1024 on a reference node (GPU: NVIDIA T4 16GB, CPU: 4 vCPU, RAM: 16 GB) When processed through the batch pipeline Then per-image latency is <= 90 ms p50, <= 180 ms p95, <= 250 ms p99, and throughput >= 10 images/sec And peak GPU memory usage <= 1.0 GB and CPU memory <= 2.0 GB, with no external network calls
Deterministic Fallback and Downstream Compatibility
Given confidence < 0.60 When the engine returns results Then fallback_mode=true and fallback_reason in {"horizon_level","inherit_set","insufficient_cues"} And horizon_level provides at least one vanishing line candidate; inherit_set uses the batch median plane from the same SKU; all cases keep the response schema unchanged And downstream pose-estimation integration tests pass with 0 schema errors and >= 99.5% successful alignments across mixed-confidence batches
Camera Pose Estimation & Horizon Derivation
"As a marketplace seller, I want the camera angle and horizon auto-computed so that my catalog looks consistent without manual gridding or guesswork."
Description

Estimate camera roll, pitch, and yaw relative to the detected base plane and derive the scene horizon and dominant vanishing point. Fuse plane outputs with line-based RANSAC and symmetry cues to stabilize estimates on minimal-texture items. Provide numeric outputs in degrees and an uncertainty band, with graceful degradation to horizon-only estimation if plane confidence is low. Results feed both the one-tap leveling action and the micro-tilt UI, ensuring consistent perspective across a set and enabling reproducible adjustments in batch operations.

Acceptance Criteria
Pose Angles and Uncertainty Output
Given an input product image with a detected base plane where plane_confidence >= 0.60 When camera pose is estimated Then the response includes roll_deg, pitch_deg, yaw_deg as numeric values with precision >= 0.1 deg And an uncertainty_deg object with keys roll, pitch, yaw is returned with values >= 0 and < 10 And plane_confidence is returned within [0,1] And repeated runs on the same input yield max absolute difference per angle <= 0.05 deg
Horizon and Dominant Vanishing Point Derivation
Given any input image When deriving the horizon and dominant vanishing point Then horizon_line is returned as normalized endpoints (x1,y1,x2,y2) with each value in [0,1] And dominant_vanishing_point_px is returned as (x,y) within image pixel bounds And the perpendicular distance from dominant_vanishing_point_px to horizon_line is <= 2 px And the supporting line cluster for the dominant vanishing point has >= 8 inliers and inlier ratio >= 0.50 among detected lines
Fusion and Stabilization on Minimal-Texture Items
Given minimal-texture product images where plane_confidence >= 0.60 or symmetry cues are detected When fusing plane estimate, line-based RANSAC, and symmetry cues Then the fused roll_deg and pitch_deg standard deviation across a 5-shot burst of the same scene is <= 0.30 deg And when line inliers >= 8, the fused roll_deg and pitch_deg differ from the plane-only estimate by <= 1.00 deg And when symmetry_confidence >= 0.70, yaw_deg is within 1.00 deg of the detected symmetry axis orientation
Graceful Degradation to Horizon-Only
Given plane_confidence < 0.60 or no base plane detected When pose estimation runs Then mode is set to "horizon_only" And roll_deg and horizon_line are returned And pitch_deg and yaw_deg are null And uncertainty is returned for roll only And the horizon_line is estimated using line-based methods with at least 6 inliers or a fallback edge-based method
One-Tap Leveling Corrects Roll/Pitch
Given an image with estimated roll_deg = r and pitch_deg = p in mode "full" When the user triggers one-tap leveling Then the re-estimated roll on the leveled output satisfies |roll| <= 0.10 deg And the re-estimated pitch aligns the base plane within <= 0.20 deg of target alignment And the operation preserves image bounds validity (no NaN/Inf coordinates, valid pixel indices)
Micro-Tilt UI and Reproducible Batch Adjustments
Given micro-tilt adjustments of delta_roll, delta_pitch, delta_yaw in steps of 0.1 deg When the same adjustment is applied to a batch of N images with recorded original poses Then each output pose equals original + delta within +/- 0.10 deg per axis And numeric UI readouts display the adjusted values with 0.1 deg precision And applying the same batch adjustment twice to identical inputs produces identical outputs and pose values
Set-Level Consistency Across Similar Shots
Given a set of >= 20 images of the same product and rig under similar conditions When processed with pose estimation Then the within-set median absolute deviation for roll_deg and pitch_deg is <= 0.50 deg And the dominant vanishing point, normalized by image width, varies by <= 2% across the set And >= 95% of images remain in mode "full"; those in "horizon_only" still return valid roll_deg and horizon_line
Ground Plane Alignment & One‑Tap Leveling
"As a PixelLift user, I want a single action to fix the horizon and ground plane so that my products look grounded and professional with no manual edits."
Description

Apply a perspective correction that aligns the scene ground and vanishing point to the detected base plane while preserving product proportions. Implement a one-tap action that levels the horizon, adds smart padding to prevent cropping, and repositions the canvas to keep the product centered. Recompute and re-render a contact shadow consistent with the new ground orientation, and clamp extreme transforms to avoid visible distortion. Integrate into the export pipeline so corrected images are used for A/B variations and downstream marketplace templates.

Acceptance Criteria
One‑Tap Leveling: Horizon, Centering, Smart Padding
Given a product image with a detected base plane and product mask When the user taps One‑Tap Level Then the horizon angle relative to the canvas X‑axis is corrected to within ±0.5° And the product mask centroid is positioned within ±1% of canvas width and height from the canvas center And no pixel belonging to the product mask is cropped And smart padding is added as needed so all content remains visible, with total added padding not exceeding 12% of the longer canvas dimension And the canvas may grow by padding but never downscales the image content And the product remains fully within the visible canvas bounds with ≥5 px margin after padding
Base Plane & Vanishing Point Alignment with Proportion Preservation
Given a scene where the product’s base plane is detected with confidence ≥ 0.6 When perspective correction is applied Then the corrected ground plane normal aligns within ≤ 2° angular error of the detected base plane orientation And dominant parallel edges converge to a single vanishing point with RMS line‑to‑VP error ≤ 3 px And non‑uniform scaling between X and Y axes is ≤ 2% And the product bounding box aspect ratio changes by ≤ 1.5% from pre‑correction And straight edges in the product region deviate by ≤ 1 px over 1000 px length (no warping)
Contact Shadow Recomposition After Ground Realignment
Given an image with a computed product base mask and estimated light direction When the ground plane is realigned by the perspective correction Then a new contact shadow is rendered beneath the base mask with contact offset ≤ 2 px And shadow direction remains consistent with preserved light direction within ±10° And penumbra blur scales with the recomputed ground orientation; change in blur radius vs. pre‑correction is within ±10% when correction angle ≤ 5° And shadow opacity at contact lies between 35% and 65% unless user default overrides And no shadow pixels extend beyond the ground region by more than 3 px And rendering completes in ≤ 300 ms for a 3000×3000 px image on reference hardware
Transform Clamping & Distortion Guardrails
Given the detected correction implies extreme perspective or tilt When applying the correction Then vertical tilt correction is clamped to a maximum magnitude of 20° And perspective shear is limited such that far/near edge scaling ratio ≤ 1.15 And non‑uniform scaling between axes is limited to ≤ 3% And pixel aspect ratio deviation across the product region is ≤ 3% And if clamping occurs, a non‑blocking notice "Correction limited to avoid distortion" is surfaced in the UI and recorded in logs
Export Pipeline Integration for A/B Variants and Templates
Given a batch export configured with A/B variations and marketplace templates When export is executed after perspective correction Then all variants consume the corrected image buffer (no uncorrected originals in outputs) And exported files include metadata fields perspective_correction=true and level_angle (degrees) And filenames include a "-leveled" token (or variation token containing leveling state) before the extension And marketplace template renders use the leveled images without introducing additional cropping And a batch of 100 images exports without throughput regression > 10% vs. baseline export without correction
Micro‑Tilt Fine Controls & Preview Responsiveness
Given a leveled image displayed in the editor When the user adjusts micro‑tilt via slider or keyboard Then the adjustment range is ±3.0° with step size 0.1° (keyboard arrows) and 0.5° (Shift+arrows) And real‑time preview latency per step is ≤ 120 ms for images up to 3000×3000 px And undo/redo restores prior angles within ±0.05° precision And optional snapping to 0.0° engages within a dead‑zone of ±0.2° And cumulative tilt never exceeds clamp limits defined by distortion guardrails
Detection Failure Fallback & User Messaging
Given base plane detection confidence < 0.6 for an image When the user opens Perspective Anchor controls Then the One‑Tap Level action is disabled and a tooltip explains "Base plane not confidently detected" And manual micro‑tilt controls remain enabled And no automatic correction is applied during export unless the user explicitly enables it And an event is logged with the image ID and confidence value And if the user forces leveling, a confirmation dialog appears and the operation proceeds using conservative correction limits
Micro‑Tilt Controls with Real‑Time Preview
"As a detail‑oriented seller, I want precise micro‑tilt controls with instant feedback so that I can match perspective across images to my brand guidelines."
Description

Provide fine-grained roll and pitch adjustments in 0.1° increments with sliders, nudge buttons, and keyboard shortcuts. Deliver high-fidelity, low-latency previews with GPU-accelerated reprojection, snap-to-common angles (0°, ±2°, ±5°), and a reset-to-detected state. Ensure non-destructive edits that can be undone/redone and are stored per image and per variation. Design for accessibility (ARIA labels, contrast, focus states) and parity across web and desktop clients.

Acceptance Criteria
Fine-Grained Roll and Pitch Sliders at 0.1° Resolution
Given an image loaded in the editor, when the user drags the Roll slider, then the roll value changes in exact 0.1° increments and the numeric display shows one decimal place. Given an image loaded in the editor, when the user drags the Pitch slider, then the pitch value changes in exact 0.1° increments and the numeric display shows one decimal place. Given any slider interaction, when the user releases the pointer, then the final value is within ±0.05° of the displayed value with no cumulative rounding error across 100 consecutive adjustments. Given a value is typed directly into the numeric input, when the user confirms entry, then the control clamps to the nearest 0.1° and updates the slider position accordingly without jitter.
Nudge Buttons for Micro Adjustments
Given the editor is focused, when the user clicks the Roll minus nudge button once, then roll decreases by exactly 0.1°; clicking plus increases by exactly 0.1°. Given the editor is focused, when the user clicks the Pitch minus nudge button once, then pitch decreases by exactly 0.1°; clicking plus increases by exactly 0.1°. Given the user presses and holds a nudge button, then the value continues stepping in 0.1° increments at a steady repeat rate until release without skipping values or overshooting. Given any nudge action, when the value crosses 0°, then the sign change is reflected immediately and no snapping occurs unless within the defined snap tolerance.
Keyboard Shortcuts for Tilt Adjustments and Reset
Given focus is within the canvas or controls, when the user presses the mapped Roll decrement shortcut, then roll decreases by 0.1°; the increment shortcut increases by 0.1°. Given focus is within the canvas or controls, when the user presses the mapped Pitch decrement shortcut, then pitch decreases by 0.1°; the increment shortcut increases by 0.1°. Given the user presses the mapped Reset shortcut, when a detected state exists, then both roll and pitch instantly return to the detected values and the UI reflects the reset. Given keyboard shortcuts are used, when the in-app shortcuts reference is opened, then all roll/pitch and reset shortcuts are listed and match behavior on both web and desktop clients.
Snap-to-Common Angles with Visual Feedback
Given snapping is enabled, when roll or pitch value passes within ±0.2° of 0°, ±2°, or ±5° during drag or key repeat, then the value snaps to that angle and a snap indicator is shown. Given snapping occurs, when the user continues adjusting beyond the snap band, then the control exits the snap cleanly without stickiness. Given a modifier key for bypass, when the user holds the bypass key while adjusting, then no snapping is applied and no snap indicator is shown. Given multiple snap candidates (e.g., near 2° and 5°), when both are within tolerance, then the closest magnitude is chosen; if equal, the lower magnitude is chosen.
Real-Time GPU-Accelerated Preview
Given a supported GPU is available, when the user continuously adjusts roll or pitch, then the preview updates at ≥30 FPS (frame time ≤33ms) with p95 interaction latency ≤50ms on reference hardware. Given no supported GPU is available, when the user adjusts controls, then a CPU fallback preview updates with p95 latency ≤200ms and the UI remains responsive (no input jank >100ms). Given the final export is rendered, when compared to the live preview at the same parameters, then geometric reprojection differences are ≤1 pixel at 4K resolution and no visual tearing is present. Given rapid bidirectional adjustments, when the user stops input, then the preview settles to the final value within 100ms without oscillation or frame drops.
Non-Destructive Edits, Reset, Undo/Redo, and Persistence per Image/Variation
Given any roll/pitch change, when the session is saved or the app is closed and reopened, then the tilt values persist per image and separately per variation without altering the source image pixels. Given a user performs adjustments, when they invoke Undo, then the previous tilt state is restored; when they invoke Redo, the later state is restored, supporting at least 20 steps in each direction per image/variation. Given an image has a detected state, when the user clicks Reset to Detected, then roll and pitch revert exactly to the detected values and the action is recorded in the undo stack. Given variations exist for an image, when the user edits tilt in one variation, then other variations retain their own stored tilt values unchanged.
Accessibility Compliance and Cross-Client Parity
Given a screen reader is active, when focus enters each control (sliders, nudge buttons, numeric inputs), then ARIA roles, names, and current values are announced; contrast ratios meet WCAG 2.1 AA (≥4.5:1) and visible focus indicators are present. Given keyboard-only navigation, when tabbing through the interface, then all controls are reachable in a logical order and operable; arrow keys adjust sliders by 0.1° and page-step keys adjust by a larger step without requiring a mouse. Given identical input sequences (slider positions, nudges, shortcuts) on web and desktop, when applied to the same image, then both clients produce identical stored roll/pitch values (tolerance ±0.05°) and identical exported results. Given feature availability, when comparing web and desktop builds, then all micro-tilt controls, snapping, reset, persistence, and undo/redo are present and behave consistently, with performance metrics meeting the same thresholds on both clients.
Batch Consistency & Reference Anchoring
"As a seller managing dozens of listings, I want to apply one perspective anchor to a whole batch so that my catalog remains consistent with minimal effort."
Description

Enable users to select a reference image whose detected plane and pose become an anchor applied to a batch or SKU group. Provide similarity-based grouping and tolerances to adapt anchor parameters to size and framing differences, with automatic skip or review flags when confidence drops below a threshold. Support API-driven bulk application, progress tracking, and idempotent retries, ensuring that all A/B variations inherit the same perspective for a uniform storefront look.

Acceptance Criteria
Reference Image Selection and Anchor Extraction
Given a batch with at least one reference candidate image and a configurable reference_confidence_threshold (default 0.95) When a user selects a reference image and initiates anchor extraction Then the system detects base plane and camera pose and persists an anchor with parameters {horizon_angle_deg, ground_plane_normal, vanishing_point_xy, camera_fov_deg, scale_ratio} and confidence >= reference_confidence_threshold And if confidence < reference_confidence_threshold, the anchor is not created, an error is returned, and the reference image is flagged 'Invalid Reference'
Similarity Grouping and Tolerance Configuration
Given a batch with a stored anchor and configured tolerances {angle_tolerance_deg, scale_tolerance_pct, crop_tolerance_pct, aspect_tolerance_pct, vpoint_tolerance_px} When the system computes per-image deltas relative to the anchor Then each image is assigned a similarity group: 'Conformant' if all deltas are within tolerance, else 'Outlier' And per-image metrics {angle_delta_deg, scale_delta_pct, crop_delta_pct, aspect_delta_pct, vpoint_error_px} are stored and available via UI/API And a grouping summary {total, conformant, outlier} is produced
Auto-Skip and Review Flags on Low Confidence
Given apply_confidence_threshold (default 0.90) and a batch with similarity groups When applying the anchor to the batch Then any image whose computed apply_confidence < apply_confidence_threshold is skipped, receives status 'Needs Review', and no perspective transform is committed And the skip reason includes the failing metric(s) and measured values And the batch summary reports counts for {applied, skipped_review}
Uniform Perspective Application Across Batch
Given an anchor and a set of 'Conformant' images When the anchor is applied Then for each applied image, horizon angle deviation <= angle_tolerance_deg, ground plane normal deviation <= angle_tolerance_deg, and vanishing point error <= vpoint_tolerance_px And resulting scale change is within +/- scale_tolerance_pct and effective crop does not exceed crop_tolerance_pct of original dimensions And the operation completes with a success summary including per-image compliance results and no 'Conformant' image violates any tolerance
A/B Variations Inherit Anchor Parameters
Given A/B variations are generated from an image with an applied anchor When variations are created and exported Then all variations retain identical anchor parameters (horizon_angle_deg and ground_plane_normal within 0.1°, vanishing_point within 1 px of source) And visual edits (background, color, shadows) do not alter anchor parameters And exported assets include anchor_id and parameters in metadata and via API response
Bulk API: Idempotent Application with Progress Tracking
Given an API client calls POST /batches/{batch_id}/apply-anchor with {anchor_id, idempotency_key} When the same request is retried with the identical idempotency_key Then the service returns the original job_id and does not duplicate work And GET /jobs/{job_id} returns progress fields {status in [queued, running, completed, failed, cancelled], total, processed, applied, skipped, failed, percent_complete} And PATCH /jobs/{job_id}/retry resumes only incomplete items and preserves prior outcomes
Micro‑Tilt Adjustment Propagation and Audit
Given a user applies a micro-tilt adjustment within +/- 2.0° to the anchor prior to batch application When the batch is processed Then the adjusted tilt is applied consistently to all 'Conformant' images and total tilt per image remains within angle_tolerance_deg of the adjusted anchor And an immutable audit log records {user_id, timestamp, batch_id, tilt_delta_deg} and is retrievable via UI/API
Confidence Feedback & Manual Override
"As a user, I want clear confidence indicators and intuitive overrides so that I can quickly fix edge cases when automation is uncertain."
Description

Expose detection and pose confidence as a simple indicator with tooltips, and offer manual override tools: three-point plane pinning, vanishing point drag, and a grid overlay. Include before/after toggles and highlight the inferred contact region to build user trust. Record manual adjustments for analytics and future model improvements while ensuring overrides take precedence in the pipeline and are preserved through exports and reopens.

Acceptance Criteria
Detection Confidence Indicator & Tooltip
- Given an image has been auto-detected, When the editor loads, Then a confidence pill shows overall state per thresholds: High (>=0.80, green), Medium (0.50–0.79, amber), Low (<0.50, red). - Given the user hovers or focuses the pill, When the tooltip opens, Then it displays numeric plane and pose confidences to two decimals, a one-line explanation, and a "Consider manual override" hint when Low. - Given keyboard-only navigation, When the pill receives focus, Then the tooltip opens and is screen-reader readable with ARIA role=tooltip and label text. - Given confidence is Low, When the image opens, Then a non-blocking prompt to "Consider manual override" is visible until dismissed. - Given a batch of N images, When moving between images, Then the pill and tooltip render within 300 ms and show per-image values.
Manual Override via Three-Point Plane Pinning
- Given the user activates the Three-Point Pin tool, When three non-collinear points are placed within the image bounds, Then the ground plane is recalculated and applied to the scene. - Given points are adjusted (drag/move/delete), When the user confirms, Then the resulting plane aligns those points to a common plane with average reprojection error <= 3 px at 100% zoom. - Given the user cancels before confirm, When cancel is clicked or Esc is pressed, Then no manual override is saved and the view reverts to auto-detection. - Given an override exists, When the image is re-opened, Then the three control points and computed plane are restored to their last saved positions.
Manual Override via Vanishing Point Drag
- Given the user enables Vanishing Point Drag, When the vanishing point handle is moved, Then the scene's perspective updates in real-time at >= 30 FPS and the grid aligns to the new vanishing point. - Given the user releases the drag, When release occurs, Then the final vanishing point coordinates are saved as the manual override. - Given the user selects Reset, When reset is applied, Then the manual override is cleared and auto-detection resumes. - Given accessibility, When using keyboard arrows on the focused handle, Then the vanishing point moves in 1 px increments (Shift = 10 px).
Grid Overlay Alignment & Behavior
- Given Grid Overlay is toggled on, When the user pans/zooms, Then the grid remains anchored to the current ground plane with crisp lines at all zoom levels and no lag > 50 ms. - Given Grid Overlay is on, When exporting an image, Then the overlay does not appear in the exported asset. - Given an override is applied, When the grid is visible, Then it updates immediately to reflect the overridden plane and vanishing point. - Given the user presses G or uses the UI toggle, When toggled, Then the grid visibility changes within 100 ms and persists per image until changed.
Before/After Toggle Preserves View State
- Given the user has auto or manual adjustments, When Before/After is toggled, Then only the rendering switches between original and adjusted alignment while pan, zoom, and selection state remain unchanged. - Given Before/After is active, When the user exports, Then the exported image always uses the adjusted (After) state unless explicitly overridden by export settings. - Given a batch workflow, When switching images, Then the Before/After toggle resets to After by default.
Inferred Contact Region Highlight
- Given auto-detection succeeds, When Contact Region Highlight is toggled on, Then a semi-transparent overlay marks the inferred product–ground contact area with IoU >= 0.75 against the model's mask and updates within 100 ms after plane changes. - Given manual overrides are applied, When the highlight is on, Then the contact region recalculates from the overridden plane with edge alignment error <= 2 px at 100% zoom. - Given exporting, When creating outputs, Then the highlight is not rendered in exports. - Given accessibility needs, When the highlight is on, Then its color meets WCAG 2.1 AA contrast against typical backgrounds, and a legend is available via tooltip.
Override Precedence, Persistence, and Analytics Logging
- Given a manual override exists, When subsequent auto-detections run (e.g., re-analysis), Then the pipeline uses the manual values and does not change them unless the user explicitly resets overrides. - Given a project is saved and closed, When it is re-opened, Then the manual override data (tool type, plane normal, vanishing point, control points, timestamp, app version) is restored exactly, and results match the prior session pixel-for-pixel. - Given an export occurs, When exporting to PNG/JPEG, Then the visual output reflects the manual override and a sidecar JSON (.plift.json) including override metadata is written alongside the asset; When exporting to PSD, Then overrides are embedded in a metadata layer or XMP block. - Given analytics is enabled and user consent recorded, When the user performs a manual override, Then an analytics event is logged with image ID (hashed), session ID, tool type, pre/post parameters, deltas, and time-to-complete; When consent is disabled or offline, Then no PII is sent and events are queued locally and retried with exponential backoff. - Given privacy requirements, When analytics are stored, Then they are sent over TLS 1.2+ and retained per policy, and tests verify that no image pixels are uploaded as part of analytics.
Parameter Persistence & Exportable Metadata
"As a returning PixelLift user, I want my perspective settings saved and reusable so that I can maintain consistency across future edits and exports."
Description

Persist all perspective parameters (plane normal, horizon line, vanishing point, roll/pitch adjustments, confidence) in project metadata and embed them in exports via sidecar JSON and standard image metadata tags where available. Version the schema for forward compatibility, surface these parameters via API, and enable one-click reapply in future sessions. Ensure downstream processes (background replacement, shadow synthesis, and A/B variant generation) consume this metadata for consistent, repeatable output.

Acceptance Criteria
Project Save/Reload Persists Perspective Parameters
Given an image with detected plane normal, horizon line, vanishing point, roll and pitch adjustments, and a confidence score When the user saves the project and later reopens it Then project metadata includes keys: schemaVersion, planeNormal, horizonLine, vanishingPoint, rollDeg, pitchDeg, confidence And values match last applied values within tolerances: vector components <= 1e-6 difference, angles <= 0.01 deg, pixel coords <= 0.5 px And the Perspective Anchor controls initialize to the stored values without re-detection
Export Embeds Metadata and Writes Sidecar JSON
Given a project with persisted perspective parameters When the user exports images in JPEG, PNG, WebP, or TIFF Then a sidecar file <basename>.perspective.json is written adjacent to each image containing schemaVersion and all parameters And the image embeds the same data using standard metadata blocks where available (e.g., XMP namespace and appropriate container fields) And re-importing the exported image restores parameters from embedded metadata, or from sidecar if embed is unavailable And export succeeds even if the image format lacks writable metadata by relying on the sidecar JSON
Versioned Schema with Forward Compatibility
Given metadata uses semantic versioning (MAJOR.MINOR.PATCH) in schemaVersion When reading metadata with the same MAJOR and higher/equal MINOR/PATCH Then the app parses known fields and ignores unknown fields without error When reading metadata with a higher MAJOR Then the app does not apply parameters, surfaces a non-blocking warning, preserves the raw metadata, and continues without crash
One-Click Reapply in Future Sessions
Given an image or project that contains perspective metadata When the user clicks Reapply Perspective Then plane normal, horizon line, vanishing point, rollDeg, and pitchDeg are applied to the current session And if current image dimensions differ, all pixel-based coordinates are scaled proportionally and angles preserved And the reapply action completes in under 200 ms per image on the reference environment
API Exposure of Perspective Parameters
Given an authenticated client When it requests GET /api/v1/projects/{id}/perspective or GET /api/v1/assets/{id}/perspective Then the service returns HTTP 200 with JSON containing schemaVersion and parameters matching stored metadata When the client PUTs valid perspective metadata to the same resource Then the service validates fields, persists the record, updates modifiedAt, and returns HTTP 200 with the stored payload And invalid payloads return HTTP 422 with field-level error details
Downstream Consumers Use Perspective Metadata
Given perspective metadata is present for an asset When background replacement runs Then the replacement ground plane aligns to the stored horizon and vanishing geometry within 0.5 px and 0.1 deg When shadow synthesis runs Then shadow projection aligns to the ground plane with contact on the base plane and direction within 3 deg of expected from vanishing geometry When A/B variant generation runs Then all variants maintain identical horizon/vanish alignment within 0.5 px and 0.1 deg across outputs And processing logs include a flag metadataConsumed=true for each downstream step

TrueScale

Estimates real‑world scale from category hints and bounding boxes to size surfaces, props, and shadow lengths correctly. Maintains believable proportions across scenes so small goods don’t look oversized—and larger items don’t shrink—improving credibility at a glance.

Requirements

Category-Aware Scale Inference Engine
"As a solo e-commerce seller, I want the system to infer true product size from the category and image so that my items look realistically proportioned without manual tuning."
Description

Implements a server-side inference component that estimates a pixel-to-real-world scale factor for the primary product by combining marketplace category hints, SKU metadata, and the product’s tight bounding box. The engine uses learned size priors per category (e.g., rings, sneakers, chairs) with robust statistics and outlier handling to infer typical dimensions, then computes a px-per-unit scale and a confidence score. Outputs are embedded as asset metadata and consumed by the layout, prop sizing, shadow synthesis, and batch consistency modules. Includes fallback logic for unknown/ambiguous categories (neutral prior, user-calibration prompt) and caching of category priors for low-latency processing. Designed for sub-150 ms inference per image at batch scale with CPU/GPU backends, multilingual category mapping, and versioned models to ensure reproducibility. Expected outcome: believable product proportions within acceptable error bounds across supported categories.

Acceptance Criteria
Known Category Scale Inference Accuracy
- Given a validation set of 1,000 images across supported categories (e.g., rings, sneakers, chairs) with tight bounding boxes and ground-truth longest-side dimension per item, When the engine infers px_per_unit, Then the computed longest-side has MdAPE <= 8% per category and P90 absolute percentage error <= 15%. - Given the same validation set, When items are binned into confidence quartiles, Then the top-confidence quartile has P90 error at least 20% lower than the bottom-confidence quartile, and no higher than 12% P90 error overall. - Given any processed image, When outputs are written, Then scale.px_per_unit is a positive float, scale.unit ∈ {"mm","cm","in"}, and scale.confidence ∈ [0,1].
Scale Metadata Embedding and Downstream Consumption
- Given successful inference, Then asset metadata includes keys with non-null values: scale.px_per_unit (float), scale.unit (string), scale.confidence (float), scale.model_version (string), scale.category (string), scale.source ∈ {"prior","sku","hybrid","neutral","calibrated"}, scale.schema_version (string), scale.timestamp (ISO 8601). - Given a processed asset, When layout, prop sizing, and shadow synthesis modules request metadata via Asset API v2, Then retrieval succeeds with P95 latency ≤ 10 ms and schema validation reports zero errors. - Given a change in model version, When new inferences are produced, Then metadata reflects the new scale.model_version and maintains backward compatibility (previous consumers continue to parse both the old and new scale.schema_version without errors).
Fallback for Unknown or Ambiguous Categories
- Given inputs with unmapped or conflicting category hints, When the engine runs, Then it sets scale.source = "neutral", scale.confidence ≤ 0.30, and computes px_per_unit using a neutral prior without throwing errors. - When a user provides a single real-world calibration along the bounding-box width via the calibration UI, Then the engine recomputes px_per_unit, updates metadata with scale.source = "calibrated" and scale.confidence ≥ 0.90, and persists the calibration per SKU for reuse within the session and future batches. - Given an unknown/ambiguous category result, When post-processing completes, Then a user-calibration prompt event is queued within 200 ms and is visible in the notifications feed for that asset within 2 seconds.
Bounding Box and Metadata Outlier Robustness
- Given bounding boxes padded by up to 10% relative to the tight box on each side, When inference runs, Then resulting px_per_unit differs by ≤ 5% median and ≤ 10% P90 from the tight-box baseline across a test set of 500 images. - Given SKU size metadata values > 3 MAD from the category prior median, When inference runs, Then those values are excluded from the hybrid estimate and metadata includes scale.meta.outlier = true. - Given images with up to 15% occlusion of the product area, When inference runs, Then scale.confidence is reduced by ≥ 0.20 relative to the same item without occlusion, and px_per_unit remains within P90 error ≤ 18% on the occluded subset.
Sub-150 ms Inference Under Batch Load
- Given a warmed service (models loaded; priors cached), When processing batches of 256 images on the GPU backend, Then P95 per-image inference latency ≤ 150 ms and mean ≤ 90 ms over 10,000 images. - Given a CPU-only backend, When processing batches of 64 images, Then P95 per-image inference latency ≤ 150 ms over 5,000 images. - Given a cold start with zero cached models, When serving the first 100 images, Then P95 per-image latency ≤ 250 ms and subsequently ≤ 150 ms after warm-up completes (within 5 minutes).
Category Prior Caching and Invalidation
- Given repeated processing from the same marketplace and locale, When 10,000 images are processed, Then the category prior cache hit ratio is ≥ 95% and cache lookup contributes ≤ 2 ms P95 per inference. - Given a change to scale.model_version or category-mapping version, When the new version is deployed, Then all affected cache entries are invalidated within 60 seconds and no batch contains mixed-version priors (enforced via batch_id tagging). - Given cache size pressure above the configured limit, When the cache exceeds 1,000,000 entries, Then LRU eviction maintains P95 inference latency increase ≤ 5 ms compared to baseline.
Multilingual Category Mapping Accuracy
- Given category labels in EN, ES, FR, DE, PT, IT, and ZH (Simplified) including marketplace synonyms, When mapping executes, Then ≥ 99% of the top 500 marketplace categories resolve to the correct canonical category (validated against a gold mapping set). - Given out-of-vocabulary or multi-label inputs, When mapping executes, Then the engine returns a single canonical category with confidence or "unknown" with scale.category_confidence ≤ 0.30, and logs OOV tokens for review without raising errors. - Given diacritics, case, punctuation, and whitespace variants, When normalization runs, Then mapping results are identical to the canonical form across 100% of tested variants.
Precision Segmentation & Bounding Box Normalization
"As a user, I want the product to precisely detect my item’s edges and true footprint so that scale calculations are based on the actual object, not background noise."
Description

Delivers high-precision instance segmentation to isolate the primary product and compute a tight, perspective-aware bounding box and oriented footprint for scale estimation. Handles multi-object scenes by selecting the sellable unit via heuristics (size, centrality, category coherence) and excludes detachable accessories unless configured. Normalizes skew and camera tilt where possible to reduce measurement error, and records mask quality/confidence for downstream modules. Integrates immediately after background removal and before scale inference to ensure the scale engine operates on accurate geometry. Expected outcome: consistent, tight object bounds that reduce scale error and eliminate background bias.

Acceptance Criteria
Single Product Segmentation With Tight Perspective-Aware Bounds
- Given an image with a single primary product centered and no occlusions, when segmentation runs, then the product mask IoU against ground truth is >= 0.92 and boundary F-score at 1px (F1@1) >= 0.88. - Given the generated mask, when computing the oriented bounding box (OBB), then the box encloses >= 99.0% of mask pixels and includes <= 2.0% background pixels. - Given the product lies on a plane with perspective, when computing the oriented footprint, then angular deviation from ground-truth major axis <= 5° and footprint area error <= 6%.
Multi-Object Scene: Sellable Unit Selection
- Given an image containing multiple objects in the same category, when selection heuristics are applied, then the chosen instance maximizes heuristic score and matches the labeled sellable unit in >= 95% of validation cases. - Given objects include packaging props and duplicates, when selection runs, then props/duplicates are deprioritized per centrality, size, and category coherence weights and excluded if score gap >= 10%. - Given multiple valid candidates of equal score, when tie-breaking is required, then the most central instance is selected consistently.
Detachable Accessories Exclusion With Configurable Override
- Given the selected sellable unit includes detachable accessories (e.g., charger, strap), when default configuration is applied, then accessories are masked as separate instances and excluded from the primary OBB and footprint. - Given config flag include_accessories=true, when segmentation runs, then accessories overlapping > 10% with primary are merged into the primary mask; else remain separate. - Given accessories remain separate, when exporting masks, then each accessory has its own mask id/class metadata and is not included in scale estimation inputs.
Skew and Camera Tilt Normalization for Bounding Box Accuracy
- Given the camera tilt estimated from vanishing lines is within |tilt| <= 15°, when normalization runs, then the rectified OBB angle error reduces by >= 30% relative to unnormalized baseline on the validation set. - Given the object plane is detected, when rectification is applied, then aspect ratio distortion measured by pixel anisotropy reduces to <= 1.05. - Given tilt exceeds 15° or confidence < 0.6, when normalization is attempted, then the system bypasses correction and flags normalization_skipped=true.
Post-Background-Removal Integration Before Scale Inference
- Given the pipeline executes, when a job reaches segmentation, then background has been removed (bg_removed=true) and segmentation operates on the cutout layer. - Given segmentation completes, when the scale engine starts, then it consumes the normalized OBB and oriented footprint from this module and not the raw image bounds (verified by pipeline trace step_ids). - Given any upstream failure is detected, when this stage runs, then it emits error code PRE_SCALE_SEGMENTATION_MISSING and halts scale inference.
Mask Quality and Confidence Metadata Emission
- Given segmentation outputs, when metadata is written, then each instance includes fields: mask_quality_f1, mask_confidence, bbox_tightness, and normalization_status with schema version v1.2. - Given mask_quality_f1 < 0.80 or mask_confidence < 0.6, when passing to downstream modules, then the instance is flagged for review and excluded from automatic scale estimation by default. - Given quality metrics meet thresholds, when emitting, then values are persisted in the job record and are queryable via API GET /jobs/{id}/instances.
Failure and Low-Confidence Handling with Safe Fallbacks
- Given segmentation fails to produce any mask, when fallback is enabled, then the system returns a conservative axis-aligned bounding box around the largest foreground blob and sets confidence=0.0 and fallback_used=true. - Given conflicting instances overlap > 25% IoU, when resolving overlaps, then non-primary instances are suppressed (NMS) with IoU threshold 0.25 and suppression rationale recorded. - Given a timeout of 4 seconds per image for this module, when processing exceeds the timeout, then processing is aborted for that image, partial results are not emitted, and a retry is scheduled up to 2 times.
Physically Plausible Surface & Shadow Scaling
"As a seller, I want surfaces and shadows to scale realistically to my product’s size so that images feel credible and premium at a glance."
Description

Applies the inferred scale factor to size scene surfaces (plinths, platforms, drop shadows) and synthesize shadows with lengths and softness consistent with a configurable virtual light model. Estimates a ground plane from mask geometry and template context, then computes shadow length, penumbra, and occlusion consistent with product size and light height. Ensures props, textures (e.g., gridlines, tiles), and backdrop details scale proportionally to avoid visual contradictions. Exposes light presets and safety bounds to guarantee credibility while maintaining brand style. Expected outcome: scenes where surfaces and shadows match product scale, increasing realism and conversion trust.

Acceptance Criteria
Shadow Length and Direction from Virtual Light Preset
Given a product mask with bounding box and a computed scale factor S and a configured light preset with elevation E (deg) and azimuth A (deg) When the scene is rendered Then the primary shadow direction deviates by <= 3 deg from azimuth A And the measured shadow length L_px along the ground plane equals L_expected = (H_px / tan(E)) within 10%, where H_px is the product height on the ground plane derived from S And for E in {15, 30, 45, 60} deg, L_px decreases monotonically as E increases
Penumbra Softness Matches Light Hardness
Given a configured light with softness parameter R (0..1) and elevation E When the scene is rendered Then the penumbra width w_px at 50% intensity equals w_expected within 15% per model w ∝ R / sin(E) And for R in {0.2, 0.5, 0.8} with fixed E, w_px increases monotonically with R And the shadow core umbra remains present (min luminance reduction >= 20% vs background) when R <= 0.5
Ground Plane Estimation and Contact Occlusion
Given product mask geometry and a template with a planar ground context When the ground plane is estimated Then the estimated plane normal deviates by <= 5 deg from the template ground normal And the darkest contact shadow pixels occur within 3 px of the product footprint boundary And average luminance at the contact band (0-5 px from footprint) is at least 20% lower than luminance at 20-25 px away on the same plane And no visible gap > 1 px exists between the product mask and the start of the contact shadow
Surface, Plinth, and Platform Scaling
Given scale factor S inferred by TrueScale and a platform with base dimensions D_base When rendering the product on the platform Then platform dimensions equal D_base * S within 2% And corner radius, bevels, and surface pattern scales equal their base values * S within 2% And the product bounding box remains fully within platform bounds with margin >= 4 px And the configured product-to-platform area ratio r_target is met within +/- 5%
Texture and Backdrop Detail Proportionality
Given a tiled texture with nominal unit size U_base and scale factor S When the texture is applied to the ground plane Then the measured tile width W_px equals W_base * S within 2 px And texture spatial frequency scales by 1/S within 5% relative to baseline And seams remain aligned with mismatch <= 1 px across tile boundaries And backdrop prop sizes scale by S within 5% relative to their base definitions
Light Presets and Safety Bounds Enforcement
Given a user selects a light preset whose parameters are outside safe bounds (elevation not in [15,85] deg or softness not in [0.1,0.9]) When the preset is applied Then parameters are clamped to the nearest safe bound And the API response includes warnings with code LIGHT_CLAMPED and safetyBoundApplied=true And normalized shadow length L_norm = L_px / H_px lies within [0.2, 3.0] And the brand style tag in metadata remains unchanged
Batch Consistency Across Variations
Given N >= 5 A/B variations for the same product processed with the same light preset When the batch is rendered Then the per-image scale factor S_i deviates by <= 2% from the batch median S_med And normalized shadow length L_norm deviates by <= 5% across the batch And ground plane normal deviates by <= 3 deg across the batch And processing time per image is <= 2 s at 2048 px longest edge on reference hardware And all outputs include identical presetId in metadata
Batch Proportion Consistency (SKU Scale Anchor)
"As a seller managing many listings, I want consistent apparent size across all images of a SKU so that my storefront looks uniform and trustworthy."
Description

Creates a persistent “Scale Anchor” per SKU that locks apparent size across all renders, templates, and A/B variants. On first image, the anchor is set from the best-available scale (inference or manual calibration) and stored in the SKU profile; subsequent images auto-adjust camera distance, crop, and prop sizing to keep the product’s visual size within a configurable tolerance. Provides detection of drift and auto-correction during batch processing, plus a per-batch report of adherence. Expected outcome: a uniform look and trustworthy proportions across a seller’s gallery, reducing buyer confusion and returns.

Acceptance Criteria
Initialize SKU Scale Anchor on First Image
Given a SKU without an existing scale anchor and a batch containing at least one image with a detected product bounding box and category hints When the first such image for the SKU is processed Then the system sets target_normalized_size S using manual calibration if provided; otherwise uses TrueScale inference only if confidence >= 0.80; otherwise marks the SKU as CalibrationRequired and skips outputs for that SKU And the created scale anchor {anchor_id, version=1, S, tolerance=t, method(inferred|manual), timestamp} is persisted to the SKU profile And GET /skus/{skuId} returns the anchor fields exactly as stored And an audit event anchor_created is recorded with correlation to batch and image IDs And the anchor is used for all remaining images of the SKU in the batch
Auto-Adjust to Maintain Apparent Size Within Tolerance
Given a SKU with an anchor having target_normalized_size S and tolerance t (expressed as a percentage) And normalized_size is defined as product bounding-box height / final image height When any subsequent image for the SKU is processed Then camera distance, crop, and prop scaling are adjusted so that final normalized_size S' ∈ [S*(1−t), S*(1+t)] for 100% of images where the product bounding box is detected And images initially outside tolerance are auto-corrected once and must meet tolerance post-correction; otherwise they are marked Fail:OutOfTolerance And shadow length parameters scale proportionally to product scale such that shadow metrics remain within ±t of anchor-derived expectations
Drift Detection and Auto-Correction During Batch
Given a running batch for a SKU with anchor S and tolerance t When the pre-correction deviation d_pre = |S_pre − S| / S exceeds t for any image OR the 5-image moving average deviation exceeds t/2 Then a drift event is recorded with {image_id, deviation_pre_pct, correction_applied, deviation_post_pct} And an auto-correction is applied to bring S_post within tolerance And if |S_post − S| / S > t after correction, the image is marked Fail:OutOfTolerance And if ≥3 drift events occur within any 20-image window, the batch summary includes SuggestRecalibration=true for the SKU
A/B Variants and Templates Honor Scale Anchor
Given a base render and N A/B variants generated via templates for the same SKU using the stored anchor When the variants are produced Then the product's normalized_size in each variant differs by no more than ±1% across variants and lies within [S*(1−t), S*(1+t)] And prop dimensions and shadow parameters are scaled consistently with product scale (≤±1% variance across variants) And output crops maintain margin consistency within ±2px or ±0.5% (whichever greater) relative to the base render
Per-Batch Adherence Report
Given completion of a batch for one or more SKUs When the batch finishes Then a Scale Adherence Report is generated within 60 seconds and available via UI and API GET /batches/{batchId}/scale-adherence in JSON and CSV formats And each image entry includes {image_id, sku_id, anchor_id, anchor_version, normalized_size_pre, normalized_size_post, deviation_pre_pct, deviation_post_pct, correction_applied(bool), status} And the batch summary includes {effective_tolerance, pass_rate, fail_count, no_measure_count, mean_deviation_post_pct, max_deviation_post_pct, drift_event_count} And pass_rate excludes images with no measurable bounding box And the published report is immutable (subsequent reads return identical content hash)
Configurable Tolerance Hierarchy (Global > SKU > Batch Override)
Given a global default tolerance g, an optional SKU-level tolerance s, and an optional batch-level override b When processing images for that SKU in a batch Then the effective tolerance t is chosen with precedence b > s > g and recorded in the report And providing b requires 0% < b ≤ 10%; values outside are rejected with HTTP 400 and a validation message And changing s after a batch starts does not alter t for the running batch And the effective t used is displayed in the batch summary and per-image records
Concurrency and Versioning of Scale Anchor
Given two batches B1 and B2 start at times T1 and T2 for the same SKU When B1 begins Then it locks anchor version v for the entire batch duration And if a recalibration occurs at T2 producing a new anchor version v+1, then B1 continues with v while any new batches use v+1 And per-image records include anchor_version used And no image within a batch is processed with mixed anchor versions And attempting to update S in-place for a running batch returns HTTP 409 Conflict unless a new version is created
Marketplace Size Compliance Guardrails
"As a marketplace seller, I want my images to automatically meet each platform’s size guidelines without distorting scale so that listings are approved and perform well."
Description

Implements a rules engine that validates and enforces marketplace-specific guidelines for product coverage and framing (e.g., primary object occupies 85%± of the longest side) without compromising true scale. For each target marketplace, the system evaluates relative object size, auto-crops/pads, and adjusts camera framing while preserving the scale anchor and physical plausibility. Provides preflight warnings, auto-fix options, and a compliance summary in export manifests. Expected outcome: listings that pass platform checks on first submission while maintaining believable proportions.

Acceptance Criteria
Per‑Marketplace Rule Evaluation
Given an input image with a valid TrueScale anchor, category hint, and primary object bounding box And a target marketplace ruleset is selected When the system evaluates size and framing rules Then it computes the primary object's coverage on the longest side using refined segmentation within the bounding box And applies the exact marketplace tolerance band to determine Pass or Fail And reports per rule: rule_id, threshold, tolerance, measured_value (percent), result (Pass|Fail), and rationale_code And makes no modification to the image during evaluation (scale factor unchanged, no resample)
Auto‑Crop/Pad Compliance Fix
Given a rule fails solely due to framing or coverage and marketplace minimum resolution can be maintained When the user selects Auto‑Fix for that marketplace Then the system applies crop and/or symmetric padding to reach the rule's target within tolerance And preserves the TrueScale anchor (pixel‑per‑unit delta <= 0.2%) and forbids non‑uniform scaling And ensures post‑fix longest side >= marketplace minimum and aspect ratio within allowed range And recomputes and displays post‑fix measured_value and updates result to Pass And records fix_applied=true with before/after values in the session
Camera Reframe Preserves Plausibility
Given a rule still fails after crop/pad or crop/pad would violate minimum resolution When camera reframing is enabled for the image Then the system adjusts virtual camera parameters (pan/zoom) without warping, keeping object scale anchor invariant (pixel‑per‑unit delta <= 0.5%) And maintains perspective plausibility (object aspect distortion <= 1%, no skew > 1°) And recomputes shadows and prop placements to remain consistent with TrueScale within ±5% of expected lengths And achieves rule compliance within tolerance and introduces no new violations
Preflight Warnings and Interactive Auto‑Fix
Given a batch of images is queued for one or more marketplaces When preflight analysis completes Then each image–marketplace pair is annotated with status (Pass|Warn|Fail) per rule and an overall status And Warn is assigned when measured_value falls within a configurable warn band of ±2% of the threshold And each entry includes an actionable suggestion and an Auto‑Fix toggle (per rule and per image) And toggling Auto‑Fix updates the preview and status within 1 second for single images and within 5 seconds for batches up to 100 images
Compliance Summary in Export Manifest
Given an export job is initiated with selected marketplaces When export completes Then an export manifest is produced in JSON and CSV containing, per image–marketplace pair: image_id, marketplace_id, overall_compliance (Pass|Fail|Warn), autofix_applied (boolean), rules [rule_id, threshold, tolerance, measured_before, measured_after, result, rationale_code] And the manifest schema validates against the published contract and includes version and generated_at (UTC) timestamp And any non‑Pass entries include a human‑readable summary and machine‑readable reason codes
Primary Object Disambiguation and Edge Cases
Given an image contains multiple candidate objects or ambiguous bounding boxes When determining the primary object for compliance evaluation Then the system selects the primary object by category‑prioritized rules: explicit user box > highest IoU with hint region > largest TrueScale physical size And the chosen object's segmentation mask drives coverage calculations; other objects are excluded from coverage metrics And if ambiguity remains (tie within 3%), the system flags Warn with reason=AMBIGUOUS_PRIMARY and offers user selection And if no valid primary can be determined, status is Fail with reason=NO_PRIMARY and Auto‑Fix is disabled
User Calibration & Manual Override
"As a seller, I want to enter a known dimension or adjust scale manually so that I can correct edge cases and ensure accuracy across my batch."
Description

Offers UI and API tools for explicit calibration when ground truth is known or the model is uncertain. Users can enter a real measurement (e.g., longest edge = 10 cm) or draw a reference segment; the system converts units, computes the scale factor, and propagates it to the SKU Scale Anchor and batch. Includes guardrails to prevent unrealistic values, conflict resolution with model inference (prefer manual, log discrepancy), versioned audit trails, and one-click revert. Expected outcome: fast correction of edge cases and increased trust through transparent, controllable scaling.

Acceptance Criteria
Manual numeric calibration with unit conversion
Given a SKU image with a detected product bounding box and a user enters a numeric measurement and selects a unit (mm, cm, m, in, ft) When the user saves the calibration Then the system converts the unit to a canonical unit with relative error ≤ 0.1% And computes scale_factor = real_length / pixel_length (for the selected dimension; default = longest edge) And updates the SKU Scale Anchor and applies the calibration to all images in the selected batch within 1 second And updates the on-canvas dimension overlay to reflect the new scale And records an audit entry with before_scale, after_scale, method=manual_numeric, input_value, unit, user_id, timestamp, sku_id, batch_id, scale_factor, and increments calibration version by +1 And logs a discrepancy record comparing model_inference vs manual_value with absolute and percentage deltas And displays a success confirmation in the UI and returns a success response containing calibration_id
Drawn reference segment calibration
Given a user opens an image and selects "Draw reference" When the user draws a segment of at least 30 pixels, enters a real-world length (> 0) and selects a unit Then the system shows the segment’s pixel length and snaps endpoints with ≤ 0.5 px precision And validates the input and computes scale_factor from the segment length And applies the calibration to the chosen scope (SKU only or entire batch) and updates previews within 1 second And persists the reference segment geometry in the calibration version history And records an audit entry with method=manual_segment, segment_coordinates, input_value, unit, user_id, timestamp, sku_id, batch_id, and new version And blocks saving if the segment length is below minimum, outside allowed bounds, or intersects outside the product bounding box unless "Allow background reference" is explicitly enabled And shows a clear validation message on any failure and does not update the active calibration
Manual override precedence and propagation
Given both model inference and a manual calibration exist for a SKU or batch When images are previewed, exported, or accessed via API Then the manual calibration is used as the active scale across UI, API, and exports And the UI displays a "Manual override" badge including source=manual and calibration_version And the API returns active_calibration.source=manual, calibration_id, and calibration_version in responses And a discrepancy log links model_estimate and manual_value with absolute and percentage deltas And the manual calibration propagates to the batch unless the user deselects batch propagation during save
Calibration guardrails and error handling
Rules: - Input must be numeric > 0 (max 3 decimal places); unit ∈ {mm, cm, m, in, ft} - Category-aware hard bounds enforced; out-of-range values are blocked with error code CAL_RANGE_HARD - Category-aware soft bounds warn; require explicit confirmation (checkbox) with code CAL_RANGE_SOFT - Minimum reference segment length ≥ 30 px; else error CAL_SEGMENT_TOO_SHORT - Length-to-bounding-box ratio must be between 0.05 and 20; else warning or error CAL_RATIO_OUTLIER per category policy - Negative/zero/NaN/Infinity inputs rejected with CAL_INPUT_INVALID - All validation feedback shown within 200 ms and logged Given an input that violates any rule When the user attempts to save a calibration Then the system blocks the save (for hard failures), highlights the offending field/segment, shows the correct code/message, and emits a structured validation error in API responses And does not update the SKU Scale Anchor, active calibration, or version history on failure
API calibration endpoints
Given an authenticated client calls POST /calibrations with sku_id or batch_id, method ∈ {numeric, segment}, value, unit, optional segment_coordinates, and an Idempotency-Key header When the payload is valid Then the API returns 201 Created with calibration_id, source=manual, scale_factor, version, active=true, and propagation scope And applies the calibration to the referenced SKU/batch within 2 seconds and makes it effective for subsequent exports and previews And emits a webhook event calibration.updated containing sku_id, batch_id, calibration_id, version, user_id/client_id, old_scale, new_scale, and discrepancy fields And a repeat request with the same Idempotency-Key and identical payload returns 200 with the original calibration_id (idempotent) And invalid payloads return 400 with field-level errors; conflicts return 409; unauthorized/forbidden return 401/403
One-click revert with versioned audit trail
Given a SKU or batch has at least one prior calibration version When the user clicks Revert in the UI or calls POST /calibrations/{calibration_id}/revert Then the system restores the previous active calibration (manual or model), increments version, and marks the reverted-from version as superseded And updates previews within 1 second and ensures all exports use the restored scale And records an audit entry with method=revert, reverted_from, reverted_to, user_id, timestamp, reason(optional) And emits a webhook calibration.reverted and updates API metadata to reflect the active calibration And provides an Undo option for 30 seconds to return to the pre-revert state

Color Harmony

Balances background hues and ambient tone to complement the product while locking item color accuracy. Prevents unwanted color cast on packaging and textiles, supports brand palette targets, and preserves marketplace‑safe fidelity—reducing returns from color mismatch.

Requirements

Color Cast Detection & Neutralization
"As a solo e-commerce seller, I want automatic detection and removal of unwanted color cast while keeping my product color unchanged so that my listings look professional and reduce color-related returns."
Description

Analyze each image to detect unwanted ambient color casts in backgrounds and scene lighting, isolate the product region, and apply localized white balance and tonal corrections that neutralize the environment while preserving product hue and saturation. Uses a hybrid approach (learning-based illuminant estimation plus physics‑guided color constancy) to handle textiles, glossy packaging, and mixed lighting. Supports adjustable strength, shadow preservation, and skin/label safeguards. Integrates into PixelLift’s non‑destructive pipeline with GPU batching, per-image diagnostics, and rollback, ensuring consistent, professional results across large batches.

Acceptance Criteria
Adjustable Neutralization Preserves Product Color
Given a product image with ambient color cast and a user-selected strength of 50% When Color Cast Detection & Neutralization runs Then background LAB a* and b* are within ±3 of 0 And product region hue shift <= 2 degrees and saturation change <= 5% And channel clipping is <= 0.5% of pixels at 0 or 255 per channel
Shadow Preservation Under Neutralization
Given an image with soft product shadows and Preserve Shadows enabled When neutralization is applied Then SSIM on luma within the shadow mask >= 0.95 vs. original And shadow pixel median luminance change is between -5% and +5% And no halo artifacts wider than 2 px along shadow edges
Skin and Label Safeguards
Given an image containing skin and printed labels When neutralization is applied with safeguards enabled Then skin pixels deltaE2000 <= 3 vs. original and hue shift <= 2 degrees And OCR text accuracy on labels >= 98% vs. original And sRGB gamut clipping <= 0.5% of pixels
Mixed Lighting Robustness on Glossy Packaging
Given a test set of mixed-light images with glossy packaging When processing completes Then product region mean deltaE2000 vs. reference target <= 4 And white patch neutrality has |a*| <= 2, |b*| <= 2, and |ΔL*| <= 2 And edge gradient continuity drop across lighting boundaries < 10%
GPU Batch Processing With Diagnostics and Rollback
Given a batch of 500 images and GPU batching enabled When processing runs on the reference GPU profile Then throughput >= 12 images/sec and p95 latency <= 400 ms per image And each output includes diagnostics fields: estimated_illuminant, cast_magnitude, product_hue_shift, background_deltaE, shadow_preserved, version And any image exceeding product_hue_shift > 3 degrees is auto-rolled back with status "rolled_back" and recorded reason And batch completes with 0 crashes and < 1% failures
Brand Palette-Compliant Background Harmonization
Given a brand palette is selected and Harmonize Background is enabled When neutralization completes Then background average deltaE to nearest palette swatch <= 3 And product region hue shift <= 2 degrees and saturation change <= 5% And output embeds sRGB IEC61966-2.1 profile and exported PNG/JPEG renders with correct profile
Product Color Lock (Delta E Guardrail)
"As a marketplace seller, I want the system to keep my product’s true color within a tight tolerance so that customers receive items that match what they see online."
Description

Lock product colors within a configurable tolerance by generating a high-precision product mask and enforcing a ΔE00 threshold between input and output in device-independent color space. Establish the reference from the source image, user-provided swatches, or sampled neutral regions, and prevent downstream adjustments from shifting product pixels beyond tolerance. Provide per-pixel audit maps, warnings when tolerance would be exceeded, and automatic fallback strategies (e.g., reduce background correction intensity near edges). Works with reflective materials and fine textures, and integrates before/after harmonization steps in the pipeline.

Acceptance Criteria
Auto-Reference ΔE00 Guardrail on Product Pixels
Given Color Harmony is enabled and tolerance T (ΔE00) is configured And the system auto-selects a product reference from the source image And a product mask M is generated When the pipeline applies harmonization adjustments Then the 99.9th percentile ΔE00 over pixels in M is <= T And the maximum ΔE00 over pixels in M excluding a 3 px boundary band is <= T And the pipeline enforces the constraint after harmonization
Swatch-Based Reference Mapping and Enforcement
Given the user provides one or more LAB swatches as the product color reference And pixels are clustered to the nearest swatch in LAB When processing completes Then for each swatch cluster, the mean ΔE00 to its swatch is <= T And the 95th percentile ΔE00 to its swatch is <= T + 0.5 And a warning is emitted for any swatch with fewer than 100 assigned pixels
Edge-Aware Fallback to Protect Tolerance
Given the predicted ΔE00 over M near the mask boundary exceeds T When processing runs Then background correction intensity is reduced within 10 px of the mask boundary with a smooth ramp And after fallback, the 99th percentile ΔE00 over M is <= T And if still exceeded, product pixel adjustments are clamped to maintain ΔE00 <= T and a non-blocking warning is recorded
Preflight Prediction and Warning for Tolerance Breach
Given an image, tolerance T, and reference When the user requests a preview or starts batch processing Then the system estimates the ΔE00 distribution over M before committing changes And if estimated p99 ΔE00 > T, a preflight warning is shown indicating estimated fraction of pixels above T, p95, p99, and max And the user can choose to auto-adjust intensity to meet T or proceed with warning And the decision is logged in the job metadata
Per-Pixel ΔE Audit Map and Metrics Export
Given processing completes When outputs are generated Then a per-pixel ΔE00 heatmap PNG aligned to the output and a JSON report with tolerance T, reference type, mean, p95, p99, max, fraction > T, and swatch mappings are saved alongside the output And files are named with the pattern {basename}_deltaE.png and {basename}_deltaE.json And batch exports include these artifacts
Reflective Material Handling Without False Violations
Given the product contains reflective or specular regions And highlight pixels H are detected by luminance and local contrast thresholds When Color Harmony is applied Then pixels in H are excluded from percentile enforcement but reported separately And non-highlight product pixels meet ΔE00 <= T at 99.9th percentile And a binary highlight mask artifact {basename}_highlight.png is exported
Brand Palette Targeted Backgrounds
"As a brand-focused seller, I want backgrounds to align with my brand palette so that my storefront appears consistent and on-brand without distorting the product itself."
Description

Enable users to specify brand palettes (HEX, RGB, Pantone equivalents) and automatically harmonize background and ambient tones to the nearest compliant brand hue while maintaining safe luminance and contrast against the product. Provide controls for hue tolerance, saturation ceilings, vignette/gradient application, and adaptive harmonization that avoids color bleed. Support per-workspace presets, instant preview, and batch application with overrides, ensuring consistent visual identity across large catalogs without compromising product color fidelity.

Acceptance Criteria
Brand Palette Input and Mapping
Given a workspace palette entry form When the user adds colors via HEX (#112233, #FF5500), RGB (17,34,51), and selects Pantone 186 C Then all valid entries are accepted and invalid tokens are rejected with inline error messages naming each invalid value And accepted colors are normalized to sRGB and CIELAB (LAB stored to 2 decimals) And Pantone 186 C maps to its sRGB equivalent with ΔE00 ≤ 2.0; if ΔE00 > 2.0 a non-blocking approximation warning is shown and the mapped value is stored And duplicates are de-duplicated and the palette is saved and available in the Brand Palette picker within 1 second
Safe Luminance and Contrast Compliance
Given a product image with a valid product mask When harmonization is applied with a selected brand hue Then the background maintains an edge-contrast ratio ≥ 3:1 against the product boundary (luminance) across ≥ 99% of boundary pixels And background relative luminance is clamped between 0.10 and 0.95 to avoid clipping And if contrast cannot be met within tolerance/saturation limits, the system suggests the nearest compliant brand hue and shows a "Contrast not achievable" notice; application proceeds only with user confirmation
Hue Tolerance and Saturation Ceiling Controls
Given an active brand hue and controls set to Hue Tolerance = 8° and Saturation Ceiling = 65% When harmonization renders Then background hue deviates from the brand hue by ≤ 8° at all sampled background pixels (each gradient stop complies) And background saturation does not exceed 65% (HSL scale) at any sampled pixel And when the user adjusts Hue Tolerance or Saturation Ceiling, the on-canvas preview updates within 300 ms in 95% of interactions for images up to 3000×3000 px
Vignette and Gradient Application
Given gradient mode is enabled with Linear 120° and two brand-color stops When applied to an image with a product mask Then 0% of product-mask pixels are altered by background rendering And a minimum safe padding of 12 px from the product boundary is preserved before gradient falloff begins And radial and linear gradients render without visible banding (adjacent quantization steps differ by ≤ 2 ΔE00) And vignette strength, spread (0–100%), and center position are applied exactly as configured
Adaptive Harmonization Without Color Bleed
Given adaptive harmonization is ON and a valid product mask exists When ambient tone adjustments are rendered Then the mean color shift inside the product mask is ΔE00 ≤ 1.0 and the 95th percentile is ≤ 2.0 versus the source image And product hue shift is ≤ 0.5° on average And if thresholds would be exceeded, ambient intensity is auto-reduced until thresholds are met and a non-blocking notice "Reduced intensity to protect product color" is shown
Per-Workspace Presets
Given a user creates a preset named "Acme Amazon" with palette selection, hue tolerance, saturation ceiling, gradient settings, and adaptive options When the preset is saved Then it is stored at the workspace level and appears in the Presets list within 1 second And setting it as default auto-loads it for new sessions in the workspace And editing or deleting the preset propagates to all workspace members within 5 seconds; existing jobs retain their original settings
Batch Application with Overrides
Given a batch of 500 images with the preset "Acme Amazon" selected and per-image overrides on 12 images When batch processing starts Then 500 jobs are queued and run; failures do not halt other jobs and per-image status/progress is displayed And the 12 overridden images use their overrides while all others use the preset unchanged And outputs are deterministic: re-running the same batch with identical inputs yields byte-identical files And on completion, a CSV/JSON summary is available with file name, applied palette color, tolerance, saturation ceiling, gradient type, achieved contrast ratio, ΔE00 stats, and status
Marketplace Fidelity & ICC Management
"As a seller listing on multiple marketplaces, I want color-managed, compliant outputs so that my images pass checks and accurately represent product colors across devices."
Description

Validate and constrain output to marketplace-safe color fidelity by standardizing to sRGB with embedded ICC profiles, performing out-of-gamut compression, and enforcing background neutrality thresholds where required. Provide selectable compliance profiles (e.g., neutral-white background tolerance, minimum contrast ratios) and surface warnings when an input cannot meet constraints without violating product color lock. Export with color-managed metadata, retain EXIF/XMP as configured, and offer soft-proof previews to reduce post-upload rejections and color mismatch complaints.

Acceptance Criteria
sRGB Standardization with Embedded ICC on Export
Given a source image in any color space and Marketplace Fidelity enabled When the user exports the image Then the output is converted to sRGB IEC 61966-2-1 using relative colorimetric intent with black point compensation And the file embeds exactly one ICC profile whose profile description equals "sRGB IEC61966-2.1" And the EXIF ColorSpace field equals 1 (sRGB) in the exported file
Out-of-Gamut Compression Respecting Product Color Lock
Given a source image with a detected product mask and out-of-gamut colors relative to sRGB When exporting with product color lock enabled Then gamut compression is applied such that at least 98% of product-mask pixels have ΔE00 <= 2.0 versus the source under D65 after conversion And non-product pixels may be adjusted without restriction to meet compliance And if the 98%/2.0 threshold cannot be met, a pre-export warning is shown that lists max ΔE00 in the product region and % of affected product pixels and requires user confirmation to proceed
Background Neutrality Enforcement per Profile
Given a compliance profile "Neutral White" with thresholds on background pixels: median L* >= 97, 5th percentile L* >= 95, |mean a*| <= 1.0, |mean b*| <= 1.5 (CIELAB, D65) When processing and exporting Then the background pixels in the exported image meet all thresholds And if any threshold cannot be met without exceeding the product color lock, the system flags "Background Neutrality: Fail" with a warning and retains product color, unless the user opts to prioritize compliance
Minimum Silhouette Contrast Ratio Enforcement
Given a compliance profile defining a minimum product-to-background contrast ratio C_min (default 3:1) When processing and exporting Then along the product silhouette, the median contrast ratio is >= C_min and fewer than 2% of sampled edge points fall below 0.9 * C_min And if criteria are not met, background luminance is adjusted to reach compliance unless this would violate product color lock, in which case a warning "Contrast: Fail" is shown and user confirmation is required to proceed
Compliance Profile Selection, Batch Application, and Reporting
Given the user selects a compliance profile and starts a batch export of N images When the batch completes Then the selected profile has been applied consistently to all N images And each image has a recorded compliance result (Pass/Fail) with metrics: profile ID, background neutrality stats, min/median edge contrast, product-region max ΔE00 and % product pixels with ΔE00 > 2.0 And Pass images are exported; Fail images are exported only if 'Allow noncompliant export' is enabled; otherwise they are skipped with reason "Compliance Failed" And a batch summary report lists counts of Pass, Fail, and Skipped
Soft-Proof Preview with Gamut and Compliance Overlays
Given the user opens Soft Proof for a selected compliance profile When Soft Proof is toggled on Then the preview shows the sRGB-converted result with optional before/after split And out-of-gamut regions are highlighted and on-canvas indicators show background neutrality and contrast status (Pass/Fail) And compliance metrics (max ΔE00 in product region, % product pixels > 2.0, background LAB stats, min edge contrast) update in real time as adjustments are made And the export uses the same settings and produces results matching the soft-proofed preview within ΔE00 <= 1.0 for background and product pixels
Configurable EXIF/XMP Retention and Color-Managed Metadata
Given metadata retention toggles: Retain EXIF (on/off) and Retain XMP (on/off) When exporting with Marketplace Fidelity enabled Then the file embeds the sRGB ICC profile and sets EXIF ColorSpace to 1 (sRGB) And EXIF and/or XMP sections are retained or stripped exactly according to toggles, with no residual tags when off And when retained, original values for standard tags (e.g., DateTimeOriginal, Orientation, IPTC/XMP dc:title) are preserved unchanged And the export job report lists for each file which metadata sections were retained or removed
Batch Presets & Automation Rules
"As a solo operator handling many listings, I want to save presets and automate when they apply so that I can process large batches consistently with minimal manual effort."
Description

Create reusable Color Harmony presets and rule-based automation that apply different harmonization settings by product category, detected material (e.g., textile vs. packaging), or source lighting conditions. Support CSV/API-driven batch runs, preset versioning, per-image overrides, and idempotent reruns. Expose concurrency controls and resource-aware job scheduling to sustain throughput at scale while keeping results consistent across large image sets.

Acceptance Criteria
Preset Creation & Versioning for Color Harmony
Given a user with preset-manage permission creates a Color Harmony preset with name, background hue targets, ambient tone bounds, color-lock strength, and marketplace profile When they save the preset Then the system stores it as version 1.0.0, returns the preset_id and version, and records an audit entry with parameters and creator Given an existing preset version When the user edits any parameter and saves Then a new immutable version is created with incremented semantic version, the previous version remains unchanged, and both are selectable Given a preset referenced by any rule or job history When a user attempts to delete that preset version Then deletion is blocked and the version can only be marked Deprecated Given category defaults are configured When a preset version is set as default for a category Then new rules for that category auto-suggest that preset and jobs without explicit rules use the default
Rule Engine Applies Presets by Category, Material, and Lighting
Given rules exist with predicates on product_category, detected_material, and source_lighting, and each rule targets a specific preset version When a batch with mixed categories/materials/lighting is executed Then each image resolves to exactly one preset using deterministic priority order (higher priority index first, then newest rule), and an explicit default rule is used if no predicates match Given conflicting rules match the same image When evaluation runs Then the rule with higher priority is applied and the decision is logged with matched predicates and chosen rule_id Given a rule refers to a Deprecated preset version When evaluation runs Then the rule still applies but a warning is logged and flagged in rule health Given the rule tester tool is provided an example image and metadata When the user tests Then the engine returns the matched rule, preset version, and an explanation of predicate matches within 200 ms for cached models
CSV-Driven Batch Run with Per-Image Overrides
Given a CSV matching the documented schema (id,image_url,category,lighting,material,override.*) is uploaded up to the configured row limit When validation runs Then invalid rows are rejected with row-level error messages and the job can proceed with only valid rows if requested Given a valid CSV batch is submitted When the job is created Then the API returns job_id, total_valid_count, total_invalid_count, and a signed upload receipt Given per-image override columns are present (e.g., override.preset_id, override.preset_version, override.params.color_lock_strength) When processing that row Then overrides are applied only to that image, do not mutate the underlying preset, and are recorded in the image audit trail Given the batch completes When results are exported Then a manifest (CSV/JSON) is produced with id, applied_preset_id, applied_version, overrides_applied, status, output_urls, and timing metrics
API-Driven Batch Run with Webhooks and Result Pagination
Given a POST /batches request with payload including sources, rule_set_id (optional), concurrency_limit, and webhook_url When the request is valid Then the service returns 202 with job_id and initial status queued Given the job is running When progress updates occur Then PATCH webhooks are sent to webhook_url at start, 25%, 50%, 75%, and completion, with HMAC signature and retry with exponential backoff on 5xx Given the client requests GET /batches/{job_id}/results?cursor=…&limit=… When results exceed a single page Then the API returns a stable cursor, up to limit items, and a next cursor until all results are fetched Given the client requests GET /batches/{job_id} When the job is completed Then the response includes final status, counts (succeeded, failed, skipped), and links to artifacts (manifest and archives)
Idempotent Reruns and Deterministic Outputs
Given a job is executed with a specific input set, rule set, and exact preset versions When the same job is rerun without changes Then each image output is byte-identical (or hash-identical) and the system de-duplicates processing using a content+config fingerprint, returning prior outputs without double-charging Given any preset version or override differs between runs When the job is rerun Then outputs are regenerated and linked to the new config fingerprint, and prior outputs remain accessible and unchanged Given a rerun is requested with force=true When a prior identical run exists Then the system regenerates outputs and increments the rerun counter while maintaining audit linkage between runs Given two workers process the same image due to retry When deduplication occurs Then only one result is committed and reported; duplicates are discarded and noted in logs
Override Precedence, Validation, and Auditability
Given both a matching automation rule and per-image overrides are present When the image is processed Then per-image overrides take precedence over rule defaults, and any unspecified fields are inherited from the resolved preset version Given an override specifies a non-existent preset_id or version When validation runs Then the row is rejected with a precise error indicating the missing resource Given an override attempts to change a forbidden parameter (e.g., marketplace profile lock) When validation runs Then the row is rejected and the set of allowed override keys is returned in the error payload Given processing completes When the image audit is retrieved Then it shows the effective parameters, source of each parameter (rule/preset/override), timestamps, and processor version
Concurrency Controls and Resource-Aware Scheduling at Scale
Given a batch is submitted with concurrency_limit=N and the cluster has sufficient capacity When the job runs Then no more than N images are processed concurrently for that job, as reflected in system metrics and worker logs Given cluster resource pressure is detected (e.g., GPU memory high) When scheduling next tasks Then the scheduler adaptively reduces effective concurrency to prevent worker OOM and logs the adjustment with reason codes Given multiple jobs are queued When scheduling occurs Then tasks are fairly interleaved without starvation and job-level concurrency limits are respected for each job Given a running job is paused via PATCH /batches/{job_id} {status:"paused"} When the pause command is acknowledged Then no new tasks start, in-flight tasks finish, and the job can be resumed to continue within prior limits Given consistency requirements across large image sets When the same image is processed across different workers under the same preset version Then outputs are deterministic (hash-identical), and cross-worker color drift is within the defined tolerance in system tests
A/B Variant Generator (Harmonized Backgrounds)
"As a growth-focused seller, I want quick A/B-ready background variations that keep the product color identical so that I can test what drives higher CTR without quality risks."
Description

Generate multiple background/ambient tone variants per image within brand and marketplace constraints while enforcing product color lock. Provide parameterized variant recipes (e.g., warm, cool, neutral; solid or subtle gradient), deterministic naming, and bulk export. Store variant metadata (palette choice, hue angle, luminance) for downstream analytics and ensure reproducibility across re-exports and additional sizes.

Acceptance Criteria
Parameterized Harmonized Background Variant Generation
Given a source product image with a valid product mask, an active brand palette, and a selected marketplace profile And the user configures N variant recipes (e.g., warm-solid, cool-solid, neutral-gradient) When the user clicks Generate Variants Then exactly N variants are produced for the image And for each variant, background hue center is within the selected palette target ±5° on the hue wheel And background luminance is within the recipe’s target ±3 L* And ambient tone classification equals the recipe category (warm|cool|neutral) And no variant violates the marketplace background rules of the active profile And no background spill occurs into the product mask (0 pixels flagged by spill detector)
Product Color Lock Fidelity
Given a source image and its product mask with Color Lock enabled When harmonized variants are generated Then mean ΔE2000 within the product mask between source and each variant ≤ 1.5 And 95th percentile ΔE2000 within the product mask ≤ 3.0 And hue shift within the product mask ≤ 3° and saturation change ≤ 2% absolute And a color-lock integrity report (meanΔE, p95ΔE, maxΔE) is stored per variant
Deterministic Variant Naming and IDs
Given deterministic naming is enabled and inputs are identical (source image hash, recipeSetId, parameters, engineVersion) When variants are generated at different times or by different operators Then every variant filename and variantId are identical across runs And filenames match the regex ^[a-z0-9-]+__([A-Z0-9]{2,12})_H(\d{1,3})_L(\d{1,3})_S(\d{3,5})_V(\d{2})\.(png|jpg|webp)$ And names are unique within the batch and ≤ 64 characters
Variant Metadata Persistence and Accessibility
Given variant generation completes for an image Then a metadata record exists per variant including: paletteChoice, recipeCode, hueAngleDeg, luminanceLStar, ambientToneCategory, marketplaceProfileId, productColorLockMeanDE2000, productColorLockP95DE2000, reproducibilitySeed, sourceImageId, variantId, engineVersion, createdAt And GET /variants/{variantId}/metadata returns these fields exactly as stored And the batch manifest (CSV and JSON) includes one row per variant with these fields And numeric ranges are enforced: hueAngleDeg ∈ [0,359], luminanceLStar ∈ [0,100], productColorLock metrics ≥ 0
Bulk Export and Re-Export Reproducibility
Given a batch of M images and a fixed recipe set, size set, format, and quality parameters with engineVersion locked When the user exports the batch and then repeats the export later with identical inputs Then the ZIP archive name, folder structure, manifest contents, variant filenames, and image bytes are identical across both exports And additional size exports (e.g., 1000x1000) derived from the same seed maintain background hue within ±1° and luminance within ±1 L* of the corresponding 2000x2000 variant
Brand and Marketplace Constraint Enforcement with Feedback
Given a brand palette that excludes specific hue ranges and a marketplace profile that forbids gradients When a recipe would produce a disallowed hue or gradient Then the system either adjusts to the nearest allowed hue and switches to a permitted background style per policy, or excludes the variant And every adjustment/exclusion is surfaced in UI and in the manifest with reason code and before/after values And the final output set contains only variants that conform 100% to brand and marketplace constraints

Scene Kits

Curated, marketplace‑safe scene bundles per category (e.g., Kitchen Wood, Marble Studio, Clean Desk) with regional styles. Apply in one tap, keep a Natural variant for craft/vintage items, and batch‑assign across SKUs to achieve a consistent lifestyle look without art‑directing every image.

Requirements

Curated Scene Kit Library & Regional Styles
"As an e-commerce seller, I want to browse and preview curated scene kits by category and region so that I can quickly choose a marketplace-safe style that matches my brand and audience."
Description

Provide a managed library of curated, marketplace-safe scene bundles organized by category (e.g., Kitchen Wood, Marble Studio, Clean Desk) and regional styles (e.g., NA, EU, APAC). Each kit includes metadata (category, marketplace compatibility, region, lighting profile, prop set, shadow style), thumbnail previews, and usage guidance. Integrate the library with the rendering pipeline so that selected kits deterministically apply consistent backgrounds, shadows, and lighting across products. Include admin tools for curation, versioning, and deprecation of kits, plus search and filters for end users. Ensure kits default to marketplace-safe configurations and can be updated without breaking previously generated outputs.

Acceptance Criteria
Library Organization & Metadata Completeness
- Given an admin creates or edits a Scene Kit, When they save, Then the system requires and persists: category, marketplaceCompatibility[], region, lightingProfile, propSet, shadowStyle, version, status (draft/published), and usageGuidance. - Given a kit is published, When fetched via API, Then all metadata fields are present and non-null with valid enums/values, and marketplaceCompatibility contains only supported marketplaces. - Given a kit lacks any required field, When attempting to publish, Then the action is blocked with field-level validation errors.
Marketplace-Safe Defaults Enforcement
- Given marketplace-safe mode is enabled by default, When a new kit is created or updated, Then prohibited props and unsafe background types for each tagged marketplace are rejected at validation time. - Given a user applies a kit tagged for Marketplace X, When preview and export are generated, Then the outputs pass automated policy checks for X (e.g., background neutrality, shadow intensity range, watermark=none) and are marked compliant. - Given a kit is non-compliant for any tagged marketplace, When attempting to publish, Then publish is blocked with a list of failing rules.
Deterministic Rendering Consistency
- Given identical source image, kit ID, and kit version, When rendered on any worker node, Then the output for lossless formats is byte-identical and for lossy JPEG/WebP has SSIM ≥ 0.99. - Given a batch of N SKUs uses the same kit version, When rendered, Then lighting temperature, shadow direction, and intensity vary by ≤ 1% across items as measured by renderer telemetry. - Given a re-render is requested without a version change, When completed, Then the output file checksum (SHA-256) matches the previous result for lossless formats.
Thumbnail Previews & Usage Guidance
- Given a kit is published, When listed in the library, Then a thumbnail (≥300x200, <50 KB) and alt text are displayed within 200 ms p95. - Given a kit detail view is opened, When loaded, Then usage guidance is visible including recommended product categories, do/don't notes, and 1–3 example images. - Given thumbnails fail to generate, When the listing loads, Then a fallback placeholder is shown and an error is logged with the kit ID.
Search & Filter Experience
- Given 10,000 kits in the library, When filtering by category, region, marketplaceCompatibility, lightingProfile, propSet, and shadowStyle in any combination, Then results return within 300 ms p95 and match the applied filters. - Given a free-text query, When searching, Then results include kits matching name, tags, and synonyms for category/region terms. - Given no results, When search completes, Then an empty state with a clear reset option is displayed.
Admin Curation, Versioning, and Deprecation
- Given an admin role, When creating or editing a kit, Then changes are saved as a new immutable version with semver (MAJOR.MINOR.PATCH) and change notes. - Given a version is published, When a newer version is created, Then existing jobs remain pinned to the prior version and the new version becomes default for future use. - Given a kit is deprecated, When users query the kit, Then it is hidden from default search but remains accessible via direct ID for re-renders, with a deprecation banner. - Given outputs were generated using kit version V, When the kit is updated to V+1, Then previously generated files remain unchanged and re-downloads return the original artifacts unless the user opts in to re-render with V+1.
Regional Style Application Accuracy
- Given a user selects a kit with region=EU, When applied to a product, Then the renderer uses EU-specific props, color grading, and lighting profile defined by the kit and logs region=EU in job metadata. - Given a user’s account default region=APAC, When browsing kits without an explicit region filter, Then APAC kits rank first and non-localized kits are shown next. - Given a kit lacks a localized variant for the user’s region, When applied, Then the system falls back to the kit’s Global variant and records the fallback in the render log.
One-Tap Apply with Natural Variant Preservation
"As a seller, I want to apply a scene kit to a product image in one click and keep the natural version so that I can switch between lifestyle and plain looks without re-editing."
Description

Enable application of a chosen Scene Kit to any product image with a single action that performs background removal, scene composition, lighting/shadow adjustments, and color consistency. Maintain a non-destructive workflow that automatically creates and links a preserved Natural variant (un-stylized but cleaned) alongside the styled output. Provide quick toggle between Natural and Scene variants, persist the last-used kit per SKU, and record applied kit metadata for reproducibility. Ensure outputs meet baseline quality thresholds (resolution, sharpness) and support undo/redo.

Acceptance Criteria
Single-Image One-Tap Apply with Natural Variant Preservation
Given a SKU with at least one source image And a Scene Kit is selected When the user clicks Apply Then the system removes the background, composes the scene per the kit, adjusts lighting/shadows, and harmonizes color And creates two linked variants: Scene (styled) and Natural (un-stylized but cleaned) And the Natural variant has a neutral background (transparent or pure white) compliant with marketplace rules And the operation is non-destructive: the original source remains unchanged and variants are versioned And both variants are saved under the SKU and selectable in the UI And the operation completes within 8 seconds for a 3000x3000 image on a standard plan account And a success event with identifiers is logged
Quick Toggle Between Natural and Scene Variants
Given a SKU with linked Scene and Natural variants When the user activates the toggle control or presses T Then the displayed image switches between variants within 200 ms without changing zoom, pan, or selection state And the toggle state is reflected consistently in canvas, details panel, and grid And the control is keyboard-accessible and has an ARIA label "Toggle Natural/Scene" And the last viewed variant for the SKU is remembered during the session
Persist Last-Used Scene Kit Per SKU
Given a SKU has had a Scene Kit applied When the user reopens the SKU in a new session Then the last-used Scene Kit is preselected in the Scene Kit picker for that SKU And the stored data includes kit ID, kit version, user ID, and timestamp And batch apply defaults to each SKU’s last-used kit unless the user overrides it And the user can Reset to clear the preselection and the change persists
Applied Scene Kit Metadata for Reproducibility
Given a Scene variant exists for a SKU When the user opens the Metadata panel Then it shows: kit name, kit ID, kit version, parameter set, model version, random seed, lighting preset, color normalization settings, crop/scale, shadow type, background asset IDs And exporting the asset includes a JSON sidecar with the same metadata and a reproducibility hash And invoking Reproduce with the same source image and metadata regenerates a pixel-identical result (allowing 1px epsilon due to compression) And the audit log records user, timestamp, and action details
Enforce Baseline Output Quality Thresholds
Given Scene and Natural variants are generated Then each variant’s longest edge is at least 2000 px unless the source is smaller, in which case original resolution is preserved without upscaling And JPEG exports embed sRGB with quality ≥ 90; PNG preserves transparency for the Natural variant And average color shift within the product mask vs. source is ΔE00 ≤ 3 And variance of Laplacian within the product mask is not reduced by more than 10% vs. source And histogram clipping is ≤ 0.5% pixels at 0 or 255 per RGB channel And failures block auto-export and surface a fix/warn banner with retry option
Undo/Redo for Apply Operations (Single and Batch)
Given an Apply operation has completed When the user presses Undo (Ctrl/Cmd+Z) Then the SKU (or selected SKUs for batch) reverts to the exact prior state, removing created variants and metadata And when the user presses Redo (Ctrl/Cmd+Shift+Z) Then the same variants and metadata are recreated deterministically And a batch Apply is undone/redone atomically as one history step And history persists through page reload within the same session until changes are explicitly discarded or published
Batch Apply with Natural Variant Preservation and Robustness
Given the user selects 10–50 images across SKUs and a Scene Kit When the user clicks Apply Then each image receives a styled Scene variant and a preserved Natural variant linked to its SKU And per-item progress and overall progress are displayed, with up to 2 automatic retries on transient failures And partial failures are clearly listed with error codes and can be retried without reprocessing successful items And the batch completes within 3 minutes for 50 images at 3000×3000 on a standard plan account
Batch Assign Across SKUs & Queued Processing
"As a seller managing many listings, I want to batch-assign a scene kit across SKUs so that all my images share a consistent look with minimal manual effort."
Description

Allow users to select multiple SKUs/folders and batch-assign a Scene Kit with consistent parameters. Jobs are enqueued and processed in parallel with progress tracking, failure/retry logic, and idempotency to avoid duplicate charges. Provide cost/time estimates before submission, per-image status, and notifications on completion. Ensure consistent composition across variants (camera angle, crop rules, subject scale) for visual uniformity. Integrate with billing, rate limits, and concurrency controls to maintain performance at scale.

Acceptance Criteria
Batch Assign Scene Kit to Multiple SKUs
Given I have selected two or more SKUs or folders in the Scene Kits assignment view And I have chosen a Scene Kit and set parameters (camera angle preset, crop rule, subject scale, regional style) When I click "Apply to N images" Then a single batch job with a unique Job ID is created that includes all target images from the selected SKUs/folders And the exact same parameters and Scene Kit version are applied to every targeted image And no images outside the selection are modified
Pre-Submission Cost and Time Estimates
Given I have a current selection of SKUs/folders and a chosen Scene Kit with parameters Then the UI displays: total target image count, per-image price, total estimated cost (currency, 2 decimals), and an estimated completion time window/ETA based on current queue depth and account concurrency When I add/remove SKUs or change parameters that affect cost or processing time Then all estimates update within 500 ms of the change When I submit the batch Then the final charged total is equal to the displayed total estimated cost at submission time, within a rounding tolerance of ±$0.01
Queued Parallel Processing with Progress Tracking
Given a batch job is created Then it is enqueued and processed respecting the account concurrency limit C and global rate limits And no more than C images from the account are in Processing state concurrently And the job progress view shows counts by status (Pending, Processing, Completed, Failed, Skipped), overall percent complete, and an ETA And each image row shows current status, attempt count, and processing duration And progress updates are pushed or refreshed at least every 5 seconds without a full page reload
Failure Handling, Retries, and Per-Image Status
Given an image processing attempt fails with a transient error Then it is retried automatically up to 3 times with exponential backoff (2s, 4s, 8s) before being marked Failed And retries do not incur additional charges Given a non-retryable error occurs Then the image is marked Failed immediately with an error code and human-readable message When I choose "Retry failed images" for the job Then only Failed images are re-enqueued for processing And previously Completed images are not reprocessed or recharged And the per-image history logs all attempts with timestamps, attempt number, and error codes
Idempotent Submission and Billing Protection
Given I resubmit an identical batch (same selection and parameters) using the same idempotency key within 24 hours due to a network retry or page refresh Then the system returns the original Job ID, does not create a new job, does not duplicate processing tasks, and does not create additional charges Given I submit a new batch that differs by selection, parameters, or idempotency key Then a new Job ID is created And the submission confirmation displays the Job ID and idempotency key for auditability
Completion Notifications and Summary
Given a batch job reaches 100% (Completed + Failed equals total images) Then an in-app notification appears within 60 seconds with job summary (Job ID, counts by status, total charge, processing duration) and a link to results/export And if email or webhook notifications are enabled for the workspace, a notification with the same summary is sent within 60 seconds When I click the notification Then I am taken to the job results view filtered to that Job ID
Consistent Composition Across Variants
Given a Scene Kit with defined camera angle preset, crop rule, and subject scale is applied across images of the same SKU Then the resulting images meet the following consistency thresholds within each SKU: - Camera angle variance <= 2° - Subject bounding box area variance <= 5% - Crop center offset <= 2% of the shorter image dimension - Horizon tilt <= 1° And the same Scene Kit background and lighting profile are used across all processed images And any image exceeding thresholds is flagged as Inconsistent in the per-image status with the specific metric that failed
Marketplace Compliance Validator & Auto-Fix
"As a seller, I want the system to flag and auto-fix images that may violate marketplace rules so that my listings are approved without rework."
Description

Implement a rule engine that validates generated images against marketplace guidelines (e.g., aspect ratios, minimum resolution, safe margins, background brightness thresholds, prohibited text/logos/watermarks, brand marks). Support marketplace- and region-specific rule sets with ongoing updates. Provide inline warnings, suggested fixes, and automatic remediation where possible (e.g., re-crop, adjust margins, remove overlays) before export. Maintain an audit log of checks and fixes applied for each image.

Acceptance Criteria
Validate and Auto-Fix Aspect Ratio and Resolution
Given a user selects a marketplace-region profile and queues images for export When the compliance validator runs Then each image must match a profile-configured aspect ratio and meet or exceed the minimum resolution And images that do not comply are auto-fixed by non-destructive re-crop and/or resize within the maximum upscaling limit defined by the profile And images that still cannot comply are flagged with inline warnings and are excluded from export And the run summary displays counts of Passed, Auto-Fixed, and Blocked images
Enforce Subject Safe Margins
Given the primary subject bounding box is detected for each image When compliance checks evaluate safe margins Then the distance from the subject bounding box to each canvas edge must be at least the configured safe-margin percentage And if not, the system auto-adjusts crop or adds padding to achieve compliance without reducing subject coverage below the configured minimum And if compliance cannot be achieved, the image is flagged with an inline warning and blocked from export
Background Brightness and Main Image Background Rules
Given an image is designated as a main image for a profile that requires a white or bright background When the background brightness and uniformity are evaluated Then the background luminance and saturation must meet the configured white-point and uniformity thresholds And if thresholds are not met, the system auto-adjusts background brightness or replaces the background to comply And for non-main images, the background must meet the profile’s minimum brightness threshold And images that cannot be auto-fixed are flagged with inline warnings and blocked from export
Detect and Remove Overlays (Text/Logos/Watermarks)
Given overlay detection is enabled for the selected profile When the validator scans an image Then any text, logos, watermarks, or brand marks above the detection confidence threshold are identified with bounding boxes And the system attempts overlay removal via masking/inpainting and re-validates the result And if residual overlays remain above the failure threshold or a prohibited brand mark is detected, the image is blocked from export with an inline warning And the UI displays before/after previews of the fix with the detected regions
Apply Region-Specific Rule Sets and Versioning
Given a user selects a marketplace and region When validation begins Then the latest published rule set for that marketplace-region is applied and its version and effective date are shown And rule parameters (e.g., aspect ratio, resolution, margins, background thresholds, overlay policies) are sourced from that version And if the rule set is updated, subsequent validations use the new version while prior audit logs preserve the original version reference
Per-Image Audit Log and Export Gating
Given validation and auto-fix steps have completed for a batch When a user opens the audit log for any image Then the log lists every rule evaluated with status (Pass/Fail/Auto-Fixed), timestamps, input and output parameters, auto-fix operations applied, the marketplace-region profile, and the rule set version And the final export decision (Allow/Block) is recorded with reason codes And the audit log is exportable and included in the export bundle metadata
A/B Variant Generation & Management
"As a growth-focused seller, I want multiple scene variations per product so that I can run A/B tests to improve CTR."
Description

Generate multiple controlled variations per selected Scene Kit (e.g., prop density, shadow softness, camera height, color temperature) with deterministic naming and metadata. Allow users to set variant count caps, pin a control variant, and select which variants to keep. Group variants per SKU for easy comparison and export. Store variant lineage to reproduce or iterate later, and ensure all variants remain marketplace-safe.

Acceptance Criteria
Controlled Variant Generation per Scene Kit
Given I select a Scene Kit and one or more SKUs And I configure allowed parameter values for prop_density, shadow_softness, camera_height, and color_temperature And I request N variants per SKU with no variant cap set When I click Generate Variants Then exactly N variants are created per SKU And each variant’s applied parameter values fall within the configured allowed sets And no two variants for the same SKU share an identical parameter combination And generated variants are grouped under their parent SKU for side-by-side comparison
Variant Count Cap Enforcement
Given I set a variant count cap of M for selected SKUs And I request N variants per SKU where N > M When I click Generate Variants Then no more than M variants are created for each SKU And the UI indicates variants were capped at M per SKU And if existing variants for a SKU already equal M, no additional variants are generated for that SKU And API responses include the applied cap value per SKU
Control Variant Pinning and Persistence
Given at least one variant exists for a SKU When I pin a specific variant as the Control Then the variant is labeled "CONTROL" and sorted first within the SKU’s variant group And subsequent variant generation operations do not modify or replace the pinned Control image And the Control flag persists across sessions and exports And only one Control variant can be pinned per SKU at a time (pinning another unpins the prior)
Keep/Discard Selection and Grouped Export
Given a SKU with multiple generated variants And I mark some variants as Keep and others as Discard When I view the SKU’s variant list Then only kept variants appear in the Active list and are eligible for export And discarded variants are moved to an Archive/Trash state and excluded from default views and exports When I export variants for multiple SKUs Then only kept variants are exported, grouped by SKU, preserving Control-first ordering
Deterministic Naming and Rich Metadata
Given Scene Kit ID, Scene Kit version, SKU ID, parameter set, engine version, and seed are defined When variants are generated for that SKU and Scene Kit Then each variant name conforms to the configured deterministic naming schema and is unique within the SKU And regenerating with identical inputs (including seed and engine version) produces the same variant names and metadata keys/values And metadata stored for each variant includes: SKU ID, Scene Kit ID + version, full parameter values, engine version, seed, generation timestamp, control flag (true/false), compliance status, and lineage IDs
Lineage Storage, Reproduce, and Iterate
Given a variant exists with stored parent_lineage_id, parameters, seed, and engine version When I click Reproduce using the same engine version Then the reproduced image is pixel-identical to the original and receives a new lineage_id referencing the same parent When I click Iterate and adjust one or more parameters Then a new child variant is created with parent_lineage_id set to the original variant’s lineage_id And the lineage graph for the SKU shows correct parent-child relationships for all variants
Marketplace Safety Validation and Enforcement
Given marketplace-safety validation is enabled When variants are generated Then each variant is automatically evaluated against the safety ruleset And variants that fail validation are flagged with rule identifiers and reasons in metadata And failed variants cannot be marked as Keep or exported (UI and API enforce this) And only safety-passed variants appear in the default Active list and in exports
Export Presets & Bulk Download for Marketplaces
"As a seller, I want to export optimized images in the correct formats for each marketplace in bulk so that I can upload faster and avoid formatting errors."
Description

Provide export presets for major marketplaces (Amazon, Etsy, eBay, Walmart, Shopify) with correct dimensions, file types, compression, color profile (sRGB), and filename templating. Support bulk export by SKU, by variant group, and by marketplace preset into organized ZIPs. Include optional direct upload integrations or pre-signed links where APIs are available, plus webhooks/email notifications on completion. Validate against compliance rules pre-export and surface any blocking issues.

Acceptance Criteria
Preset Library Coverage for Major Marketplaces
Given the user opens Export Presets When viewing available presets Then the system provides built-in presets for Amazon, Etsy, eBay, Walmart, and Shopify And each built-in preset includes configured dimensions (width, height, fit mode), file format, compression/quality, color profile=sRGB, and a filename template And built-in presets are selectable during export flows
Apply Marketplace Export Preset
Given a SKU with one or more source images And a marketplace preset is selected with width, height, file_format, compression_quality, color_profile=sRGB, and filename_template When the user starts export Then each exported image is resized to the configured dimensions using the preset's fit/crop rules And each exported image is encoded in the configured file format with the configured compression quality (±1 quality point) And each exported image is converted/embedded to sRGB And each exported image is named exactly per the filename_template with resolved variables And the count of exported files equals the count of source images selected for export
Bulk Export by SKU and Variant Group to Organized ZIPs
Given the user selects multiple SKUs and/or a variant group for export And a marketplace preset is selected When the user initiates a bulk export Then the system produces one ZIP archive per marketplace preset selection And the ZIP contains folders organized as {marketplace}/{sku}/{variant}/ And files inside each folder follow the preset's filename template And the ZIP is available to download via a secure link visible on the job details page And the job details show total files exported and per-SKU/variant counts
Pre-Export Compliance Validation
Given images are selected and a marketplace preset is chosen When the user clicks Export Then the system validates each item against preset compliance rules (dimensions, file format support, sRGB requirement, filename template variable resolvability) And any blocking failures are listed per SKU/image with the failed rule and remediation hint And items with blocking failures are excluded from starting the export until resolved And items that pass validation proceed to export
Direct Upload to Marketplace via API or Pre-Signed Links
Given a marketplace integration (e.g., Shopify or Amazon) is connected and enabled for the workspace And the user selects Direct upload as the delivery option When the export job completes asset rendering Then the system uploads each exported file using the marketplace API or pre-signed link flow when available And for each file the system records and returns the remote asset identifier/URL in the job results And transient upload failures are retried per retry policy; non-transient failures are reported with error codes and human-readable messages And if any uploads fail, a fallback ZIP containing failed files is offered for manual upload
Export Job Completion Notifications (Webhook and Email)
Given a webhook endpoint is configured and email notifications are enabled for the workspace When an export job finishes with status Success, Partial, or Failed Then the system sends a webhook POST containing job_id, status, marketplace, counts (total, succeeded, failed), and delivery links/IDs And the webhook is signed with an HMAC using the workspace secret and includes a timestamp for replay protection And the account email receives a summary message with the same status and links And notification delivery status is logged and visible on the job details page
Filename Templating and Sanitization
Given a filename template using variables such as {sku}, {variant}, {sequence}, {marketplace} When exporting images with any marketplace preset Then variables resolve correctly for each file and sequences are zero-padded to maintain ordering And filenames are sanitized to remove/replace illegal characters per OS and marketplace constraints And filenames are unique within each SKU/variant export set

EdgeBlend Pro

Advanced edge refinement for fine details—hair, fur, glass, and translucent plastics—plus halo removal and micro‑shadow feathering. Produces crisp, native‑looking composites that survive high‑zoom scrutiny on Amazon and Shopify PDPs.

Requirements

Subpixel Hair/Fur Matting
"As a solo e‑commerce seller, I want hair and fur edges to look natural at high zoom so that my product photos appear premium and trustworthy on marketplace PDPs."
Description

Deliver a next‑gen, edge‑aware matting engine that preserves flyaway hairs, fur, and fine filaments at subpixel precision for images up to 8K. Output includes a 16‑bit alpha matte plus an uncertainty map to guide downstream cleanup. Integrate as the default cutout stage in PixelLift’s background removal pipeline with automatic fallback to the legacy model for low‑complexity edges. Optimize for high‑zoom scrutiny (200–400%) on Amazon/Shopify PDPs, eliminating jaggies and stair‑stepping. Target performance ≤3s per 2000×2000 px on a T4‑class GPU, with batch streaming and deterministic results for reproducibility.

Acceptance Criteria
High-Zoom Hair/Fur Edge Fidelity (200-400% PDP)
Given the PixelLift Hair/Fur QA Set v1 (>=200 images, 2000x2000 px) and EdgeBlend Pro enabled as the default cutout When each image is processed and composited onto a neutral gray (#BFBFBF) background Then boundary F-score on annotated hair/fur regions >= 0.92 And mean edge stair-stepping index <= 0.05 measured at 400% zoom And color fringing within 2 px of the alpha boundary has deltaE00 <= 1.0 And at least 2 of 3 human raters mark "no visible jaggies or halos at 200-400% zoom" for >= 90% of images
16-bit Alpha Matte and Uncertainty Map Output
Given any input image (up to 8K) processed via API or UI with default settings When outputs are exported to PNG/TIFF Then the primary output contains a linear, unpremultiplied 16-bit alpha channel aligned 1:1 to RGB And a separate 16-bit single-channel uncertainty map is written (filename suffix "_uncertainty") with identical dimensions and pixel alignment to the alpha And both files embed XMP metadata: model_version, commit_sha, engine="subpixel_matting_v1", seed And validation tool pl-verify-matte reports alpha_bit_depth=16, uncertainty_bit_depth=16, aligned=true
T4 Performance and Batch Streaming (2000x2000 px)
Given an NVIDIA T4 GPU (16 GB), CUDA 11+, and a batch of 50 images at 2000x2000 px submitted via the batch API When batch streaming is enabled Then p95 per-image end-to-end latency <= 3.0 seconds And first streamed result is emitted within 1.0 second of batch start And outputs are bit-identical to single-image processing mode And peak GPU memory usage <= 14.0 GB during the run
Automatic Legacy Fallback on Low-Complexity Edges
Given a mixed set of 200 images with varying edge complexity When processed with EdgeBlend Pro as the default stage Then images with edge_complexity_score < 0.35 are routed to the legacy cutout model And a decision log entry is recorded per image with fields {image_id, score, threshold, chosen_model} And on the low-complexity subset, boundary F-score difference (legacy vs pipeline output) is within ±0.01 And setting force_subpixel=true routes all images to the subpixel model regardless of score
8K Tiled Processing Without Seams
Given a 7680x4320 px image containing hair/fur or fine filaments When processed with automatic tiling enabled Then output alpha and uncertainty dimensions exactly equal input dimensions And no tile seam artifacts: the absolute difference between tiled and untiled outputs within a 16 px band around tile boundaries has mean <= 1/65535 and max <= 3/65535 And no out-of-memory or crashes occur across 3 consecutive runs
Default Pipeline Integration and Marketplace Readiness
Given PixelLift default background removal pipeline in the UI and API without overrides When a seller processes product images containing hair/fur or translucent plastics and exports Amazon/Shopify-ready PNGs Then the subpixel matting engine is invoked by default for images with edge_complexity_score >= 0.35 And exported composites show no visible halos > 1 px width or dark/light fringing > deltaE00 1.0 at 200-400% zoom on white background And Amazon/Shopify preview emulators render crisp edges with pass rate >= 95% in a 100-image sample per marketplace And A/B variation generations preserve alpha and uncertainty outputs for each variant
Deterministic, Reproducible Outputs Across Runs
Given the same input image set, configuration, model version, and hardware When processing is repeated 3 times in single-image mode and once in batch mode Then alpha and uncertainty outputs are bit-identical across all runs And the run metadata contains a fixed rng_seed value and reproducible hash of the model and pre/post-processing And changing batch size does not change any output bits
Translucency‑Aware Compositing
"As a catalog manager, I want glass and semi‑transparent items to retain their natural look when placed on white backgrounds so that listings don’t appear fake or cut‑out."
Description

Accurately handle glass and translucent plastics by estimating per‑pixel transmission and refraction, producing realistic composites on configurable backgrounds (e.g., pure white #FFFFFF). Generate layered outputs (foreground RGB, alpha, transmission layer) for PNG/TIFF export and preserve soft internal edges without banding. Integrate with PixelLift presets so Amazon/Shopify templates apply the correct background while retaining translucency. Provide controls for background color, translucency strength, and refraction blur with safe defaults for marketplace compliance.

Acceptance Criteria
Amazon White Background Compositing for Translucent Products
Given a translucent glass or plastic product photo and the Amazon preset is selected And Background Color is #FFFFFF (default) When the image is composited and exported as PNG and TIFF Then 100% of pixels where alpha == 0 are exactly RGB(255,255,255) And at least 99.9% of pixels in the designated background region (outside the object mask) are RGB(255,255,255), allowing up to 5 stray pixels per megapixel And translucent regions contain non-binary alpha values (0 < alpha < 1) covering at least 2% of the object area And in a 3-px band outside the object contour, the maximum channel deviation from 255 is <= 2 (no color fringing/halos) And the white-background compliance check passes with >= 99.9% background coverage
Layered PNG/TIFF Export with Transmission Channel
Given Export Format is TIFF or PNG and Include Transmission Layer is enabled (default) When exporting Then TIFF contains channels: RGB, Alpha (soft), Transmission (grayscale), all 16-bit, with embedded sRGB IEC 61966-2-1 profile And PNG contains RGBA 16-bit with soft alpha and a transmission asset provided as either an embedded grayscale channel or a separate file named <basename>_transmission.png And opening the TIFF in Photoshop shows named Alpha and Transmission channels; opening the PNG or companion file shows the transmission as grayscale And re-compositing the layered outputs over black and white backgrounds matches the in-app preview within <= 1% RMS pixel error
Translucency Strength and Refraction Blur Controls with Safe Defaults
Given Translucency Strength ranges 0–200% (default 100%) and Refraction Blur ranges 0.0–5.0 px (default 1.5 px, step 0.1 px) And Background Color accepts HEX input with validation When the user adjusts each control Then the preview updates within 200 ms of interaction and the exported result reflects the settings And at 0% strength, the mean transmission over translucent regions decreases by >= 90% vs default And at 150% strength, the mean transmission over translucent regions increases by >= 30% vs default (clamped to [0,1]) And at 0.0 px refraction blur, no added blur is measured; at 5.0 px, added Gaussian blur sigma is within 10% of 5.0 px on refracted features And invalid HEX input is rejected with inline error and no change applied And defaults comply with marketplace settings (Background Color = #FFFFFF)
Preset Integration Retains Translucency on Marketplace Templates
Given an Amazon or Shopify preset is selected in PixelLift When Translucency-Aware Compositing is applied Then the preset enforces Background Color to the template-required value (Amazon: #FFFFFF) And translucency is retained (no flattening): translucent regions in the output have non-binary alpha and non-zero transmission And preset-locked settings prevent noncompliant choices (e.g., background cannot be changed off-white under Amazon) And exports inherit preset naming, sRGB profile, and dimensions while preserving RGB, Alpha, and Transmission availability
Soft Internal Edges Preserved Without Banding at High Zoom
Given a translucent product with soft internal gradients and edges When exported as PNG and TIFF Then bit depth is 16-bit per channel for all outputs And the alpha histogram contains at least 128 distinct levels within the [0.05, 0.95] range (no contour banding) And within any 100-px sliding window inside translucent regions, no intensity step exceeds 1 DN in 8-bit equivalent (no gradient banding) And within the object mask, there are fewer than 10 isolated alpha == 0 pixels per megapixel (no pinholes)
API and UI Parameter Parity for Translucent Compositing
Given a batch job created via API with background_color (HEX), translucency_strength_percent (0–200), and refraction_blur_px (0.0–5.0) And an equivalent job run in the UI with the same values When both jobs complete Then TIFF outputs are bitwise identical and PNG outputs match within <= 1 L2 pixel error And the job response includes URLs for: composite (PNG/TIFF), transmission asset, and JSON metadata listing color profile, bit depth, and control values And omitting parameters uses defaults (background_color = #FFFFFF, translucency_strength_percent = 100, refraction_blur_px = 1.5) And invalid parameter values return HTTP 400 with descriptive error codes and do not start processing
Halo & Color Decontamination
"As a marketplace seller, I want halos and color fringing removed from edges so that my products look clean and professional against pure white backgrounds."
Description

Detect and remove background halos and color spill along edges using edge‑aware decontamination that shifts contaminated pixels toward true subject colors while preserving metallic highlights and micro‑contrast. Run automatically after matting with adjustable radius/strength and guardrails to prevent over‑desaturation. Validate results via delta‑E thresholds against sampled foreground regions. Integrate into both batch and API flows with per‑preset tuning for Amazon and Shopify requirements.

Acceptance Criteria
Automatic Post‑Matting Decontamination in Pipeline
Given a product image has been successfully matted and an alpha mask exists When the processing pipeline advances to post‑matting steps Then the Halo & Color Decontamination step executes automatically before any global color/grading operations And the step is not executed if matting status is not "success" And the operation writes a metadata record including step="decontamination", algorithm_version, and applied parameters
Edge Color Spill Reduction Meets delta‑E Thresholds
Given a foreground reference color is sampled from an inner band 3–10 px inside the subject boundary And the edge band is defined as 1–R px wide where R is the selected decontamination radius When decontamination is applied to the edge band Then the 90th percentile ΔE00 between edge pixels and the foreground reference decreases by at least 30% versus pre‑step And the final 90th percentile ΔE00 in the edge band is ≤ 4.0 And the final median ΔE00 in the edge band is ≤ 2.5 And these measurements are recorded in QC metrics per image
Metallic Highlights and Micro‑Contrast Preservation
Given highlight regions within the edge band are detected via luminance/specular mask When decontamination is applied Then the mean L* in the highlight mask changes by ≤ 3 units And the 95th percentile L* in the highlight mask changes by ≤ 5 units And local micro‑contrast in the edge band (high‑frequency energy) remains within ±10% of pre‑step And no pixel values are newly clipped (below 1 or above 254 in 8‑bit) due to this step
Saturation Guardrails Prevent Over‑Desaturation
Given the inner foreground reference colorfulness C* is computed per local sector around the edge When decontamination proposes a correction for an edge pixel Then the resulting C* reduction for that pixel is limited to ≤ 15% relative to the local foreground reference for at least 95% of edge pixels And corrections exceeding the guardrail are clamped and counted in guardrail_clamp_count And images with any guardrail activations still satisfy the final ΔE00 thresholds or are flagged in QC metrics
Adjustable Radius and Strength Controls
Given the UI exposes radius [1..20 px] and strength [0.0..1.0] controls and the API accepts the same parameters When a user sets radius and strength and runs processing Then the effective processed edge band width equals the selected radius ± 1 px And average correction magnitude increases monotonically with strength across test points 0.2, 0.5, 0.8 (Spearman ρ ≥ 0.9) And saved presets persist radius/strength and re‑apply them on subsequent runs And the API echoes applied radius/strength in the job result payload
Batch and API Flows with Marketplace Presets
Given two presets exist: Amazon_PDP and Shopify_PDP, each with distinct default radius/strength values And a batch of at least 200 images is submitted via UI with Amazon_PDP, and 200 via API with Shopify_PDP When processing completes Then decontamination is executed in both flows using the preset defaults unless overridden And job‑level overrides for radius/strength are honored and reflected in outputs and metadata And for each flow, the final 90th percentile edge ΔE00 ≤ 4.0 and median ≤ 2.5 across ≥ 95% of images And the response includes preset_id, applied parameters, and QC metrics for each image
Fine Detail Edge Integrity for Hair/Fur/Glass/Translucent
Given a test set containing hair, fur, glass, and translucent plastics with valid mattes When decontamination is applied Then alpha boundary SSIM vs pre‑step within the edge band is ≥ 0.98 And gradient connectivity in the thinnest 10% edge strands changes by ≤ 5% And in glass/translucent regions, background spill ΔE00 reduces by ≥ 30% while tint chroma (C*) changes by ≤ 10% And no new bright rims > 1 px are introduced (edge ring ΔL* ≤ 5)
Micro‑Shadow Feathering
"As a store owner, I want subtle grounding shadows under products so that images feel realistic without violating marketplace guidelines."
Description

Synthesize soft, feathered contact micro‑shadows to visually ground cutouts while meeting marketplace shadow rules. Estimate object footprint and approximate light direction to generate a physically plausible shadow layer with controls for intensity, falloff, blur radius, and offset. Export shadow as a separate layer or baked‑in, and enable/disable per marketplace preset and A/B variant. Ensure consistency across batches by seeding shadow generation for deterministic results.

Acceptance Criteria
Adjustable Shadow Controls: Intensity, Falloff, Blur, Offset
Given a cutout product image and Micro‑Shadow enabled, When the user sets Shadow Intensity to X% (0–100), Then the rendered shadow opacity at the contact zone scales linearly with X and clamps at 0% and 100%. Given Blur Radius is set to R px (0–100), When the shadow is rendered, Then the measured 10–90% edge transition width equals R ±10% and increases monotonically with R. Given Falloff is set to F% of the object bounding‑box diagonal (0–50), When the shadow is rendered, Then the shadow intensity at distance F% of the diagonal is 50% ±5% of the contact intensity and intensity decreases monotonically with distance. Given Offset is set to (dx, dy) px, When the shadow is rendered, Then the shadow centroid shifts by dx ±2 px horizontally and dy ±2 px vertically relative to the object footprint. Given all four controls are adjusted and applied, When the user resets to preset defaults, Then all values revert to the preset’s defined defaults and the preview updates accordingly.
Export Modes: Separate Shadow Layer or Baked-In
Given export format is PSD or TIFF and Shadow Mode is Separate Layer, When exporting, Then the file contains a distinct layer named "Micro Shadow" beneath the subject layer with blend mode Multiply and the subject layer contains no baked‑in shadow. Given export format is PNG or JPEG and Shadow Mode is Baked‑in, When exporting, Then the output contains the shadow composited into the base image per current settings and no separate shadow asset is produced. Given export format is PNG and Shadow Mode is Separate Layer, When exporting, Then two files are produced: one image without the shadow and one grayscale shadow matte (alpha premultiplied) aligned pixel‑perfect to the base image. Given any export format and Shadow Mode is Separate Layer, When the exported assets are re‑imported, Then toggling visibility of the shadow layer (or matte) removes all shadow pixels with no residual artifacts in the subject layer. Given a batch export of N images, When Shadow Mode is Separate Layer, Then each output includes a correctly named shadow layer/matte and maintains per‑image parameterization.
Marketplace Preset Shadow Enablement and Parameter Caps
Given a marketplace preset is selected, When the preset is applied, Then the Micro‑Shadow enabled state and default parameter values match the preset specification. Given a marketplace preset defines parameter caps, When a user attempts to exceed a cap (e.g., Intensity > cap), Then the UI clamps the value to the cap and the render honors the clamped value. Given a marketplace preset disables Micro‑Shadow, When exporting, Then no shadow pixels are present in the output (difference vs. same export with Micro‑Shadow enabled is zero within 1 LSB across all pixels). Given the user switches presets, When the new preset is applied, Then Micro‑Shadow parameters update to the new preset’s defaults unless explicitly overridden by the user’s saved custom preset.
A/B Variant-specific Shadow Configuration and Export
Given Variant A and Variant B are configured with different Micro‑Shadow settings, When exporting A/B variants, Then each variant’s output reflects its own settings and filenames include the variant suffix (e.g., _A, _B). Given Micro‑Shadow is disabled for Variant B and enabled for Variant A, When exporting, Then Variant B contains no shadow while Variant A contains the configured shadow and all other settings remain identical. Given the user duplicates Variant A to create Variant B and modifies only Shadow Intensity, When exporting, Then the only pixel differences between A and B are attributable to the intensity change (no unintended parameter drift). Given batch export with K SKUs and A/B variants, When completed, Then K×2 outputs are produced with correct variant mapping and metadata includes the per‑variant shadow parameters.
Deterministic Seeding for Repeatable Shadow Generation
Given a fixed input image, fixed Micro‑Shadow parameters, and seed S, When rendering/exporting multiple times (including across machines/OS), Then the shadow pixels are bit‑for‑bit identical and file hashes (SHA‑256) match. Given the seed is changed from S1 to S2 with all else fixed, When rendering, Then at least 5% of shadow pixels differ by ≥2 grayscale levels between outputs, indicating a non‑trivial change in shadow synthesis. Given a batch of images processed with seed S, When re‑processing the same batch with seed S, Then all shadow outputs match prior outputs bit‑for‑bit. Given no explicit seed is provided, When rendering, Then a default deterministic seed derived from image content and parameters is used so that re‑runs reproduce identical results.
Physical Plausibility: Footprint Anchoring, Light Direction, and Halo-Free Output
Given an object mask with estimated footprint and a detected light direction vector v, When Micro‑Shadow is generated, Then the shadow extends on the side opposite v with angular error ≤15° relative to v. Given the rendered shadow, When inspecting the contact region, Then the shadow begins at the object’s contact boundary with no gap >2 px and peak opacity occurs at contact. Given radial distance from contact increases, When measuring intensity, Then the shadow intensity decreases monotonically with no secondary bright bands. Given the composite image, When inspecting a 3‑px ring around object edges, Then no positive halo is present (mean brightness increase ≤1% relative to background). Given items with translucent materials (e.g., glass/plastic) are detected, When generating the shadow with identical settings to an opaque control item, Then peak shadow opacity is at least 30% lower for the translucent item while maintaining the same blur and falloff parameters.
Batch Consistency and Scale Normalization Across Resolutions
Given two versions of the same product image at 1500 px and 3000 px long edge and identical parameters/seed, When Micro‑Shadow is rendered, Then measured blur radius as a percentage of object diagonal matches within ±0.5 percentage points and mean contact opacity within ±2 percentage points. Given a batch of N images of similar object scale processed with identical parameters/seed, When analyzing outputs, Then the coefficient of variation of mean shadow opacity across the batch is ≤5%. Given the "Normalize to Object Scale" behavior is enabled by preset, When object size varies across images, Then falloff distance and blur radius scale with object diagonal so that visual appearance remains consistent across resolutions. Given batch processing is resumed after interruption using the same seed and parameters, When remaining images are processed, Then previously completed outputs are unchanged and newly generated shadows match the deterministic pattern for that seed.
High‑Zoom Artifact QA & Auto‑Correct
"As a busy seller, I want PixelLift to catch and fix edge artifacts automatically so that I don’t have to manually inspect every image."
Description

Implement an automated quality gate that evaluates outputs at 200–400% zoom for artifacts (fringes, halos, pinholes, jaggies). Compute edge smoothness, residual fringe ratio, and hole count to produce a quality score. If below threshold, auto‑tune decontamination/matting parameters and re‑process once within a strict time budget, then flag for review if still failing. Surface the score and reasons in UI, job logs, and metadata to support auditability and continuous improvement.

Acceptance Criteria
High-Zoom Metrics and Score Computation
Given an EdgeBlend Pro output image When QA runs at 200% and 400% zoom Then it computes edgeSmoothness ∈ [0,1] with 3-decimal precision And computes residualFringeRatio ∈ [0,1] with 3-decimal precision And computes pinholeCountPerMP as a non-negative integer And produces qualityScore ∈ [0,1] as a weighted composite of the metrics And persists all values to job logs and output metadata And repeated runs on the same input and seed yield metric deltas ≤ 0.005 and identical qualityScore when rounded to 3 decimals
Threshold Evaluation and Pass/Fail Decision
Given thresholds minEdgeSmoothness=0.85, maxResidualFringeRatio=0.02, maxPinholeCountPerMP=3, minQualityScore=0.90 When QA evaluates an image Then result=Pass only if all threshold conditions are met And result=Fail if any threshold is violated And reasons include each violated metric, its measured value, and the threshold it failed
Auto-Tune Reprocess Attempt Within Time Budget
Given an initial QA result=Fail and qa.timeBudgetMs=1200 When auto-correct runs Then it adjusts decontaminationStrength, colorDecontamRadius, mattingEps, and edgeRefineIterations based on failed metrics And reprocesses the image at most once And completes the retry cycle within qa.timeBudgetMs from QA start to retry end And re-evaluates metrics and qualityScore And if now Pass, sets passReason=AutoCorrected and attemptCount=1 And if still Fail or time budget exceeded, sets needsReview=true, attemptCount=1, and timeoutExceeded=true when applicable
UI, Logs, and Metadata Surfacing
Given a completed QA cycle (initial or after retry) When the user opens job details, inspects job logs, and reads output metadata Then the UI displays qualityScore, edgeSmoothness, residualFringeRatio, pinholeCountPerMP, pass/fail, reasons, and attemptCount And the job log contains a structured qaResult event with the same fields And the output metadata includes pl.quality.score, pl.quality.edgeSmoothness, pl.quality.residualFringeRatio, pl.quality.pinholeCountPerMP, pl.quality.pass, pl.quality.reasons[], pl.quality.attemptCount And values match across UI, logs, and metadata with a tolerance of ±0.001 for floating-point metrics
Batch Isolation and Summary Reporting
Given a batch of images including passes and expected fails When QA and auto-correct run Then each image is processed independently with at most one retry per image And failures do not block other images and do not increase median processing time of passing images by more than 5% And a batch summary reports counts for passInitial, passAutoCorrected, failNeedsReview and lists IDs for failed items And per-image attemptCount ∈ {0,1} and never exceeds 1
Edge Cases: Hair, Fur, Glass, Translucent Plastics
Given a labeled reference set containing hair, fur, glass, and translucent plastic subjects When QA evaluates EdgeBlend Pro outputs Then for hair/fur items, average edgeSmoothness ≥ 0.82 And for glass/translucent items, average residualFringeRatio ≤ 0.03 And for all items, pinholeCountPerMP ≤ 2 And for the full set, 95% of items achieve qualityScore ≥ 0.88 And items failing any condition are listed with reasons including metric deltas to thresholds
Batch & API Controls for EdgeBlend
"As a power user, I want to configure EdgeBlend settings in bulk via presets and API so that I can process large catalogs consistently and run A/B variants quickly."
Description

Expose EdgeBlend Pro parameters (edge sensitivity, translucency strength, decontam radius, micro‑shadow controls) in both the batch UI and public API. Provide marketplace‑ready presets (Amazon, Shopify) and allow workspace‑level defaults with per‑job overrides. Support A/B variant generation (e.g., with vs. without micro‑shadow) and attach quality scores to each variant. Ensure throughput of ≥120 images/hour/GPU at 2000×2000 px with structured logs, metrics, and idempotent job retries for reliable scale operations.

Acceptance Criteria
Batch UI parameter controls with presets and overrides
Given a workspace with EdgeBlend Pro enabled When a user opens the Batch UI for a new job Then controls are visible for edge sensitivity, translucency strength, decontam radius, and micro-shadow (enable/disable, intensity) And each control enforces documented min/max ranges and step sizes And selecting the "Amazon" preset auto-populates the controls to the predefined values And selecting the "Shopify" preset auto-populates the controls to the predefined values And changing any control updates the pending job payload preview And choosing "Set as workspace default" persists values for the workspace And launching the job applies per-job overrides over workspace defaults
API parameterization and idempotent job creation/retries
Given valid API credentials and workspace defaults When the client POSTs /v1/batches with edgeBlend parameters overriding defaults Then the response includes job_id, normalized parameter values, and preset (if any) And GET /v1/batches/{job_id} reflects the requested parameters exactly And subsequent POSTs with the same Idempotency-Key within 24h return the same job_id without creating duplicate jobs And only one invoiceable job record exists for that idempotency window And re-submitting the same image payload with the same Idempotency-Key yields the same image_result_id without duplicate outputs
Marketplace presets parity (UI and API)
Given the "Amazon" or "Shopify" preset is selected in UI or specified via API When a batch is created Then the batch metadata includes preset_name and preset_version And the resolved parameter bundle matches the preset definition checksum And UI and API produce identical resolved parameters for the same preset and workspace And users can reset to "Custom" to clear preset linkage
A/B variant generation and labeling for micro-shadow
Given a batch with variants configured as "micro-shadow:on" and "micro-shadow:off" When N images are processed Then exactly 2 outputs per input image are generated and labeled with variant keys And both variants share identical crop, scale, and alignment metadata And filenames and exports include variant labels using pattern {basename}__{variant}.png And the job summary reports counts per variant and per-image mapping And each variant includes a quality_score field
Quality scoring availability and stability
Given completed variants When retrieving results via UI and GET /v1/images/{image_id}/variants Then each variant has a numeric quality_score in range 0–100 with two-decimal precision And quality_score is deterministic for identical image+parameters within ±0.1 across re-fetches And quality_scoring_model_version is included in metadata And quality scores are included in CSV/JSON bulk downloads
Throughput performance at 2000×2000 px
Given a single GPU worker processing a mixed-detail 2000×2000 test set of ≥600 images When EdgeBlend Pro is enabled with the Amazon preset Then sustained throughput is ≥120 images/hour/GPU measured over a continuous 60-minute window And no more than 1% of images exceed 60s end-to-end processing time And system logs start/end timestamps per image for audit
Structured logs and metrics for batch operations
Given any batch job and its images When processing starts, retries, and completes Then JSON logs include: timestamp, level, job_id, image_id, workspace_id, preset_name, parameters_hash, variant_key, attempt, timings_ms{download,segment,refine,compose,upload}, gpu_id, status, quality_score And metrics are exported via OpenTelemetry with counters (jobs_created, images_processed, retries) and histograms (latency per stage) And dashboards expose throughput per GPU and error rates And logs are retained ≥14 days and are searchable by job_id

SceneSafe Filter

Pre‑flight scene compliance checks that flag disallowed elements for main images (people, logos, text props) and suggest compliant alternatives. Category‑aware rules keep lifestyle flair for secondary images while avoiding policy rejections and rework.

Requirements

Category-aware Rules Engine
"As a seller, I want marketplace- and category-specific rules applied to my main and secondary images so that my listings pass moderation on the first submission."
Description

Centralized, versioned policy rule sets per marketplace, category, and image role (main vs secondary) that map visual detections to pass/fail outcomes and remediation actions. Supports remote updates, locale variants, and workspace-level overrides, with defaults aligned to major marketplaces. Provides rule IDs, severity levels, and auto-fixability flags consumed by the pre-flight scan and suggestion engine. Ensures lifestyle elements remain allowed for secondary images while enforcing stricter standards for main images, reducing policy rejections and rework across PixelLift’s batch workflows.

Acceptance Criteria
Marketplace-Category-Role Rule Evaluation Matrix
Given a pre-flight scan for marketplace=Amazon, category=Apparel, imageRole=Main with detections {person:true, logo:true, text:false, background:"non-white"} When the rules engine evaluates the image Then it returns decision="Fail" and includes triggered ruleIds, severity levels, and remediation actions for each violation And the response includes rulesetVersion, rulesetHash, marketplace, category, imageRole, and evaluation timestamp And detections with no matching active rule do not affect pass/fail And for a batch of 1000 images the average evaluation latency <= 30ms/image and P95 <= 75ms/image on a standard worker
Main vs Secondary Lifestyle Policy Differentiation
Given detections {person:true, room:true, product:true, logo:false, text:false} When imageRole=Main for marketplace=Amazon, category=Apparel Then decision="Fail" with violations referencing person/scene rules (severity=High) and remediation suggestions (e.g., switch to product-only image, auto background removal) When the same detections are evaluated with imageRole=Secondary Then decision="Pass" and the response includes a matched lifestyle-allowed ruleId with severity=Info When category=Electronics Accessories, imageRole=Secondary, and detections include text props area=8% Then decision="Fail" if locale threshold is 5% (limit exceeded) and the message includes the quantitative limit and measured value
Versioned Rule Set Selection and Traceability
Given a workspace pins rulesetVersion="2025.09.1" When a batch is evaluated Then each item response includes rulesetVersion="2025.09.1" and a rulesetHash that matches the registry entry Given no version pin is set When evaluated Then the latest stable version for the marketplace/category/role is used and included in the response Given a triggered ruleId (e.g., "PL-AMZ-APP-MAIN-001") When inspecting the response Then it includes ruleId, title, severity, autoFixable flag, and remediation hint And the job audit export (CSV/JSON) maps imageIds to triggered ruleIds and rulesetVersion
Remote Rule Update Rollout and Rollback
Given a new rulesetVersion "2025.10.0" is published When rollout is configured at 10% canary for marketplace=Amazon Then 10% ±1% of eligible evaluations use the new version and the remainder use the previous stable Given rollback is triggered When executed Then 100% of evaluations revert to the previous stable within 5 minutes and responses reflect the reverted rulesetVersion Given rollout progresses to 100% When completed Then >=99.5% of new evaluations use the new ruleset within 10 minutes and adoption is visible in telemetry
Locale Variant Resolution and Fallback
Given marketplace=Amazon, locale=DE, category=Apparel, imageRole=Main When evaluated Then DE locale variant rules are applied; for any rule without a DE variant the marketplace default is used for that rule only And the response includes localeApplied="DE" and localeFallback=[list of ruleIds that used default] Given an unsupported locale (e.g., "SE") When evaluated Then the engine uses the marketplace default locale and sets localeFallback=true in response metadata Given thresholds differ by locale (e.g., text_area_max=5% US vs 3% DE) When evaluated Then the comparison uses the locale-specific threshold and the violation message includes the applied threshold and measured value
Workspace-level Overrides with Inheritance and Precedence
Given a workspace overrides rule "PL-AMZ-APP-MAIN-003" to severity="Warning" and autoFixable=false When evaluated Then the override is applied and the response flags effectiveSource="workspaceOverride" for that ruleId Given a workspace attempts to disable a critical baseline rule When saving the override Then the save is rejected with validation error code and message explaining critical rules cannot be disabled Given overrides exist alongside locale variants and marketplace defaults When evaluated Then precedence is workspaceOverride > localeVariant > marketplaceDefault and the response lists effectiveSource per evaluated rule And an audit log records actor, timestamp, oldValue, newValue, and reason for each override change
Auto-fixability Flags and Remediation Suggestions
Given violations with autoFixable=true (e.g., background not pure white) When evaluated Then the response includes an autoFix plan with ordered steps and parameters; applying the plan and re-evaluating results in those violations no longer triggering Given a mix of auto-fixable and non-fixable violations When "Apply all auto-fixes" is invoked Then only auto-fixable issues are modified and the response lists remaining non-fixable violations with guidance links Given an auto-fix would breach category constraints (e.g., crop below min edge length) When planning Then the engine marks autoFixable=false with reason and does not propose the conflicting fix And the job summary includes counts: violationsFound, autoFixesApplied, issuesResolvedByAutoFix, issuesRemaining
Visual Element Detection
"As a seller, I want the system to automatically detect disallowed elements like people or logos so that I don’t need to manually inspect every image."
Description

High-precision detection and classification of people, faces, brand logos, text overlays, watermarks, QR codes, phone numbers, and props using GPU-accelerated models, returning bounding boxes/masks and per-object confidence. Handles large resolutions with tiling and batch throughput, supports transparent PNGs and background-removed images produced by PixelLift, and outputs normalized labels compatible with the rules engine. Exposes saliency heatmaps to aid human review and drives reliable pre-flight decisions at scale.

Acceptance Criteria
High-Precision People and Face Detection
- On a labeled marketplace test set of ≥1,000 images, person AP@0.5 ≥ 0.95 and recall ≥ 0.92; face AP@0.5 ≥ 0.97 and recall ≥ 0.94. - Correct detections require bbox IoU ≥ 0.50 with ground truth; if masks are produced, mask IoU ≥ 0.80. - Confidence calibration Expected Calibration Error (ECE) ≤ 0.05 for person and face. - Per-image false positive rate for person/face when absent ≤ 2%.
Logo, Text Overlay, Watermark, QR, and Phone Number Detection
- On a labeled test set of ≥1,000 images with mixed overlays, logo AP@0.5 ≥ 0.90 and recall ≥ 0.88; text_overlay F1 ≥ 0.95; watermark F1 ≥ 0.95; prop F1 ≥ 0.90. - QR_code: recall ≥ 0.98 and precision ≥ 0.98. - phone_number: F1 ≥ 0.92 on Latin-script overlays; per-image false positive rate ≤ 2% when no phone number is present. - Each detection includes class label and confidence ∈ [0,1].
Bounding Boxes and Segmentation Masks Output Integrity
- Each detection returns: id, label ∈ {"person","face","logo","text_overlay","watermark","qr_code","phone_number","prop"}, confidence ∈ [0,1], bbox normalized to original image coordinates {x,y,w,h} ∈ [0,1], and optional mask (polygon or RLE) in original image space. - Bbox IoU with ground truth ≥ 0.50 for correct detections; mask IoU ≥ 0.80 where masks are provided. - Non-maximum suppression reduces duplicate detections to ≤ 1% of total objects. - 100% of responses conform to the published detection JSON Schema.
Large-Resolution Tiling and Cross-Tile Stitching
- Inputs up to 12000×12000 px are processed via tiling with overlap; all outputs are in original image coordinates. - On an edge-case set with objects within 32 px of tile boundaries, recall ≥ 0.98 and duplicate rate ≤ 1% after cross-tile NMS. - Coordinate mapping error from tile space to image space ≤ 1 px at 100% scale. - Tile size and overlap are configurable; default settings keep peak memory ≤ 12 GB during inference.
GPU-Accelerated Batch Throughput
- On NVIDIA T4 16 GB with FP16, 4K (≈3840×3840) images at batch size 16: throughput ≥ 6 images/sec and p95 latency ≤ 500 ms per image; single-image mode p95 latency ≤ 800 ms. - Warm-up to steady-state ≤ 10 s. - During a 100-image run, GPU memory ≤ 12 GB and CPU utilization ≤ 400% on an 8 vCPU host.
Transparent PNG and Background-Removed Image Support
- No detections occur in fully transparent regions; on 500 PixelLift background-removed PNGs, false positives entirely within alpha=0 areas = 0. - At alpha edges, per-image false positive rate ≤ 1%; ≥ 95% of each bbox area overlaps non-transparent pixels. - Detection quality on transparent PNGs is within ΔF1 ≤ 0.02 of opaque JPEG baseline across classes.
Normalized Labels, API Contract, and Saliency Heatmaps
- API response includes version (semver), image_id, detections[], and optional saliency_heatmap; 100% of responses validate against the published JSON Schema. - Labels map 1:1 to {"person","face","logo","text_overlay","watermark","qr_code","phone_number","prop"} with no out-of-vocabulary values; unknowns are mapped to "prop". - When saliency_heatmap is requested, it is returned as base64 PNG or float array, aligned to the input image (or documented downsample factor), values ∈ [0,1]. - For ≥ 95% of detections, max heatmap value within the bbox ≥ 0.7; ≤ 5% of heatmap area above 0.5 lies outside the union of detected bboxes dilated by 10 px.
Upload Pre-flight Scan
"As a user, I want immediate compliance feedback at upload so that I can fix problems before I export or schedule listings."
Description

Integrates scene compliance checks into the upload and batch pipeline, returning instant results in the UI and via API/webhook callbacks. Produces per-image pass/fail outcomes, flagged elements, rule references, and recommended actions, with configurable blocking for main images and warn-only for secondary images and A/B variants. Scales to thousands of images per job with progress reporting, retries, and timeouts, ensuring PixelLift users catch issues before export.

Acceptance Criteria
UI Upload Instant Compliance Feedback
- Given a user uploads a batch of up to 200 images via the PixelLift UI with roles assigned (main, secondary, A/B), When the upload completes, Then the first 10 per-image compliance results render in the UI within 5 seconds and each result includes pass/fail, flagged_elements, rule_refs, and recommended_actions. - Given an image contains disallowed elements (people, logos, text props), When its result renders, Then the UI displays each flagged element type with count and clickable bounding-box overlays. - Given an image passes all applicable rules, When its result renders, Then pass=true and flagged_elements is empty. - Given recommended_actions are provided for a failed image, When a user clicks Apply on a recommendation, Then the image is queued for reprocessing and an updated result replaces the prior one within 15 seconds or a visible error message is shown. - Given a batch is processing, When viewing the results list, Then newly completed image results stream in without requiring a page refresh and maintain stable ordering (by upload order) with a visible processing indicator for pending items.
API/Webhook Compliance Result Delivery
- Given a client submits a job via POST /jobs with a valid callback_url, When the job completes, Then a webhook POST is delivered within 60 seconds containing job_id, status=completed, and an array of per-image results (image_id, role, pass, flagged_elements[], rule_refs[], recommended_actions[]). - Given at least one image in the job has completed processing, When callbacks are enabled, Then at least one partial webhook is delivered within 10 seconds of the first image completing with status=processing and results for images completed so far. - Given the webhook receiver responds with a non-2xx status, When delivery is attempted, Then the system retries up to 5 times with exponential backoff starting at 5 seconds and stops on the first 2xx response. - Given a job was submitted without a callback_url, When the client performs GET /jobs/{job_id}, Then the response includes status, progress (processed_count, passed_count, failed_count, total_count, eta_seconds), and per-image results accumulated so far in the same schema as the webhook payload. - Given payloads are emitted, When validated against the API schema, Then all required fields are present and types conform, otherwise the job is marked delivery_failed and an error is logged with a correlation_id.
Configurable Blocking by Image Role
- Given the blocking policy is configured as main=block, secondary=warn, ab_variant=warn, When any main image result has pass=false, Then the Export action in UI is disabled and a banner lists the count of blocking images and their rule_refs. - Given a secondary or A/B variant image has pass=false, When viewing Export, Then Export remains enabled and a non-blocking warning lists counts by role and links to affected images. - Given an admin updates the blocking policy to block secondary images, When a secondary image fails, Then Export becomes disabled and the UI reflects the updated policy within 10 seconds without requiring a page reload. - Given an export is attempted via API while blocking failures exist under current policy, When POST /exports is called, Then the API responds 422 with a body containing blocking=true and an array of blocking image_ids with rule_refs. - Given all blocking failures are resolved (pass=true) for the roles configured to block, When re-checking Export, Then Export is enabled and the warning banner (if any) persists only for warn-only roles.
Category-Aware Rule Application
- Given product category metadata is provided as Apparel, When evaluating images, Then main images flag presence of people as disallowed (pass=false) and secondary images allow people (no failure on people) with rule_refs specific to category and role. - Given product category metadata is provided as Electronics, When evaluating images, Then main images flag logos and text props as disallowed, and secondary images may warn (not block) according to configured policy, with rule_refs indicating category-specific rules. - Given no category metadata is provided, When evaluating images, Then default marketplace baseline rules are applied (main: disallow people, logos, text; secondary/A/B: warn-only for lifestyle elements). - Given mixed-category batches, When results are generated, Then each image’s rules are applied according to its own category metadata and role, and the payload includes the category used in evaluation for traceability. - Given rule references are emitted, When compared with the documentation index, Then rule_refs use stable codes (e.g., SCN-<CAT>-<RULE>-<ROLE>) that map to human-readable titles and remediation guidance.
Batch Scale and Progress Reporting
- Given a job containing 5,000 images is submitted, When processing begins, Then time-to-first-result is <= 10 seconds and at least 50 image results are available within 30 seconds under nominal load. - Given any job is processing, When querying GET /jobs/{job_id} or viewing the UI, Then progress shows processed_count, passed_count, failed_count, total_count, and eta_seconds and updates at least every 5 seconds. - Given a large job (>= 5,000 images), When measured under nominal load, Then average throughput is >= 15 images/second over any 2-minute interval, excluding retries for permanently failing images. - Given progress is displayed, When processing completes, Then status transitions to completed and counts reconcile exactly with the sum of per-image outcomes (passed + failed + skipped = total). - Given pagination is required for large result sets, When requesting results via API, Then the endpoint supports cursor or page/limit parameters and returns consistent ordering by upload time.
Retry and Timeout Handling
- Given image-level processing timeout_ms is configured to 45000 and retry_count to 2, When an image attempt exceeds 45 seconds, Then the attempt is aborted and retried up to 2 additional times with exponential backoff (>= 2s, 4s). - Given all retries are exhausted for an image, When reporting results, Then the image is marked pass=false with error_code=PROCESSING_TIMEOUT and includes attempt_count and last_error in the payload. - Given a transient 5xx error occurs during fetch or store of an image, When detected, Then the operation is retried according to retry_count and backoff policy; if still failing, the image is marked pass=false with error_code=TRANSIENT_FAILURE. - Given webhook delivery receives non-2xx responses repeatedly, When retries exceed the maximum, Then delivery_status=failed is recorded with next_retry_at=null and the job remains accessible for manual redelivery via POST /jobs/{job_id}/redeliver. - Given a job-level timeout_minutes is configured (e.g., 60), When exceeded, Then remaining images are marked pass=false with error_code=JOB_TIMEOUT and a final webhook with status=timed_out is sent.
Export Gating According to Compliance
- Given a batch contains one or more failed main images, When the user opens the Export panel, Then the Export button is disabled and a banner shows the number of blocking images with direct links to each. - Given a batch has no failed main images, When opening the Export panel, Then Export is enabled even if warnings exist on secondary or A/B variants. - Given the user fixes issues and reprocesses failed main images to pass, When all blocking failures are cleared, Then the Export button becomes enabled within 10 seconds without a full page reload. - Given an export is initiated via API while blocking failures exist, When POST /exports is called, Then the response is 422 with details of blocking image_ids and rule_refs; when no blocking failures exist, Then the response is 201 with export_id.
Compliant Alternative Suggestions
"As a seller, I want one-click compliant alternatives for flagged images so that I can resolve issues quickly without re-editing from scratch."
Description

Contextual, one-click suggestions for each violation, including smart crop to exclude logos/people, background swap to compliant solid colors, text overlay removal, selective blur of brand marks, and auto-reassignment of lifestyle shots to secondary slots. Generates before/after previews and allows bulk-apply across similar images while preserving brand style settings and visual quality. Provides fallback guidance when automated fixes risk degrading the asset.

Acceptance Criteria
One‑Click Composition Fixes (Crop/Background) for Main Image Violations
Given a selected marketplace policy profile and a main image flagged for disallowed logo/person/background When the user clicks "Suggest Fix" Then the system returns at least 2 and at most 3 fix variants within 2 seconds for a 2048px image And at least one variant uses smart crop that removes 100% of the violation mask while retaining ≥95% of the detected product bounding box pixels And any cropped result has longest side ≥1600 px and aspect ratio preserved within ±5% And at least one variant uses background swap to the policy target color (e.g., pure white or seller default), with background ΔE ≤ 2 from target and matte edge leakage ≤1% of object perimeter And applying any variant and re-running SceneSafe on the image yields zero violations of the original rule type
Text Overlay Removal for Main Image Compliance
Given a main image flagged for text/graphic overlay When the user selects "Remove Text" suggestion and applies it Then residual text pixels (as detected by OCR + glyph mask) cover ≤0.05% of total image area And no edge halo artifacts exceed 2 px in width along removed regions And SSIM within a 10 px band around product edges is ≥0.92 compared to pre-fix image And re-running SceneSafe returns zero text/graphic overlay violations And the operation completes within 2 seconds for a 2048px image
Selective Blur of Brand Marks Without Product Degradation
Given an image where removing brand marks would materially damage product detail and the policy allows obfuscation When the user accepts the "Selective Blur" suggestion Then blur is applied strictly within the detected brand-mark mask with zero spillover pixels outside the mask And OCR confidence for the blurred text/mark drops to <0.20 while PSNR on non-masked regions is >35 dB versus the original And re-running SceneSafe returns zero brand-mark visibility violations And the resulting image maintains export resolution constraints (≥1600 px longest side)
Auto‑Reassignment of Lifestyle Shots to Secondary Slots
Given a batch where an image flagged as non-compliant for main due to lifestyle context has an available compliant alternative When the user accepts the "Move to Secondary" suggestion Then the flagged image is moved to a secondary slot and a compliant image is assigned to the main slot for that listing And the export manifest reflects the new slot mapping with no duplicate mains and preserves the original secondary order where possible And re-running SceneSafe on the new main image returns zero main-image violations And audit logs capture the reassignment (image ID, from→to, user, timestamp)
Before/After Previews with Change Highlights
Given any suggested fix is available When the user opens the preview Then side-by-side before/after renders within 500 ms for 2048px images And a toggle to show change heatmap accurately highlights ≥95% of modified pixels And a 200% zoom is available with synchronized pan And when the user clicks "Revert", the image returns to its exact pre-fix state and SceneSafe re-analysis reverts to the prior findings
Bulk‑Apply Across Similar Images While Preserving Brand Style
Given a set of images and an accepted fix on a source image When the user selects a similarity cluster with cosine similarity ≥0.85 and clicks "Bulk Apply" Then the fix is applied to all N selected images with a success rate ≥98%, and a skip report lists any failures with reason codes And brand style settings (tone curve, white balance, saturation) deviate by ≤1% from the project’s style baseline on all processed images And throughput is ≥30 images per minute on the standard plan environment And an "Undo Bulk" action fully restores all affected images and metadata within 1 click
Fallback Guidance When Automated Fix Risks Degradation
Given an image where predicted quality after fix falls below thresholds (e.g., SSIM <0.92 near product edges or crop would retain <95% product area) When the user opens suggestions Then auto-apply is disabled for that fix and a guidance panel shows a risk score, reason code, and at least 3 actionable alternatives (e.g., choose another shot, reshoot tips, manual retouch steps) And the image is auto-excluded from bulk-apply by default until risk is acknowledged And the user can mark the item as "Manual Required" to clear it from the automated queue, with the status reflected in exports and audit logs
Confidence Scoring & Review Queue
"As a seller, I want uncertain cases highlighted with explanations so that I can make informed decisions and avoid unnecessary rejections."
Description

Configurable confidence thresholds per rule and marketplace route borderline detections to a human review queue. Presents annotated thumbnails, heatmaps, and plain-language rationales; supports user overrides with justification and logs decisions to improve models and rules over time. Includes notifications, SLA indicators, and batch-level summaries to minimize false positives while maintaining high compliance.

Acceptance Criteria
Per-Rule, Per-Marketplace Confidence Thresholds
Given I have admin permissions When I set the confidence threshold for rule "No People in Main Image" to 0.92 for marketplace "Amazon" Then the configuration is saved and immediately applied to new jobs for that marketplace and rule And the saved value persists across sessions and API restarts And marketplace-specific thresholds override global defaults for the same rule And an audit log entry is created capturing user, timestamp, rule, marketplace, old value, and new value And values must be between 0.00 and 1.00 inclusive or the save is rejected with a validation error
Borderline Detections Routed to Review Queue
Given a threshold T and a review band B are configured for a rule and marketplace When the model returns a confidence score C for that rule on an image Then if C >= T + B the image is auto-flagged as non-compliant And if C <= T - B the image is auto-cleared as compliant And if C > T - B and C < T + B the image is routed to the human review queue And queue items include image ID, rule ID, marketplace, C, T, B, and detection snippets And SLA countdown starts when the item is enqueued
Reviewer UI: Annotated Evidence and Rationales
Given a queued item is opened in the review UI When the image and detections load Then an annotated thumbnail displays bounding boxes for detected elements with labels and confidence scores And a heatmap overlay can be toggled on/off and opacity adjusted And zoom and pan are available up to 200% without loss of annotation alignment And evidence and rationale render within 2 seconds at the 95th percentile for batches up to 10k images And a plain-language rationale explains which rule triggered, where in the image, and why it may violate marketplace policy
Override With Justification and Audit Trail
Given an item is in the human review queue When a reviewer overrides the system decision to Approve or Reject Then a justification text of at least 10 characters is required before submission And the final decision, justification, reviewer ID, timestamp, image ID, rule ID, marketplace, model confidence, threshold, and prior decision are recorded immutably And the item is removed from the queue and downstream workflow is updated within 5 seconds And an audit log entry is visible in the image’s history
Decision Logging for Model and Rule Improvement
Given a decision (auto or human) is finalized When the logging pipeline runs Then a labeled record containing image ID, rule ID, marketplace, decision, label source (auto/human), confidence, threshold, and rationale presence is stored in the analytics datastore And any personal data is excluded or redacted according to data policy And logging succeeds within 5 minutes with retry on failure up to 5 attempts with exponential backoff And failures are surfaced as operational alerts without blocking the user workflow
Notifications and SLA Indicators for Pending Reviews
Given an SLA of 4 hours is configured for the review queue When an item reaches 75% of its SLA time without a decision Then an in-app notification appears for assigned reviewers and an email is sent to the reviewers group And items display a countdown timer and can be sorted and filtered by time remaining and SLA breached And items breaching SLA trigger an escalation notification to admins And notification preferences can be configured per user
Batch-Level Compliance Summary and Export
Given a batch of images has completed processing When I view the batch summary Then I see counts and percentages for auto-cleared, auto-flagged, queued, human-approved, human-rejected, and overrides by rule and marketplace And I can export the summary and per-image decisions as CSV and JSON with consistent field names And the summary is available within 2 minutes of batch completion and remains stable under refresh And totals reconcile with the number of images in the batch
Compliance Report & Audit Trail
"As a shop owner, I want downloadable compliance reports so that I can document adherence and troubleshoot any marketplace rejections."
Description

Automatically generates per-image and per-batch compliance reports containing flags, rule IDs, actions taken, model/rule versions, timestamps, and final status. Exports as CSV/JSON/PDF, attaches optional summaries to export bundles for marketplaces, and stores reports for 12 months with search and filters. Exposes an API endpoint for retrieval to support appeals, partner audits, and internal QA.

Acceptance Criteria
Auto-Generate Per-Image and Per-Batch Compliance Reports
Given a batch of images is processed by SceneSafe Filter When processing completes for the batch Then a per-batch compliance report is generated containing fields: batch_id, workspace_id, created_at (ISO 8601 UTC), model_version, ruleset_version, total_images, compliant_count, non_compliant_count, overridden_count, final_status And a per-image compliance record is generated for every image containing: image_id, flags [rule_id, description, severity], actions_taken [action_type, outcome], timestamps [detected_at, resolved_at], final_status And all IDs are unique and cross-referenced (per-image records reference batch_id) And final_status values are restricted to one of: Compliant, Non-Compliant, Overridden
Multi-Format Export: CSV, JSON, PDF
Given a completed batch compliance report exists And the user selects an export format of CSV, JSON, or PDF When the user requests an export Then the system produces a downloadable file named {batch_id}_compliance_report.{ext} And CSV exports include header columns for all batch-level fields and per-image rows with required fields And JSON exports conform to the documented schema with batch and images sections And PDF exports include a human-readable summary, counts by status, and a per-image table of flags and final_status
Attach Summary to Marketplace Export Bundles
Given a user is exporting an image bundle to a marketplace And the user enables "Include compliance summary" When the export bundle is generated Then the bundle includes compliance_summary.json and compliance_summary.pdf at the bundle root And compliance_summary.json matches the JSON schema for batch reports And each exported image is listed in the summary with its final_status and primary flags And if "Include compliance summary" is disabled, no compliance summary files are added
12-Month Retention with Search and Filters
Given compliance reports and audit logs exist across multiple months When a user searches the Reports view using filters for date range, batch_id, image_id, rule_id, final_status, and marketplace Then the result set only includes records that match the filters and are within the last 12 months And results are returned with pagination metadata (total, page, page_size) And reports older than 12 months are no longer retrievable via UI or API And a nightly retention job purges reports older than 12 months and logs the purge count
Reports Retrieval via API Endpoint
Given a client has a valid API token with scope reports:read When the client requests GET /v1/reports/batches/{batch_id} Then the server returns 200 with the batch report JSON, including links to CSV and PDF exports And when the client requests GET /v1/reports/images?batch_id={batch_id}&image_id={image_id} Then the server returns 200 with the per-image compliance record And requests without a valid token return 401 Unauthorized And requests for non-existent resources return 404 Not Found And requests with invalid parameters return 400 Bad Request
Audit Trail Completeness and Versioning
Given an image has one or more compliance-related actions (auto-fix applied, auto-flag, manual override, dismissal) When each action is performed Then an audit trail entry is appended capturing: entry_id, image_id, batch_id, actor (system|user), action_type, details, timestamp (ISO 8601 UTC), model_version, ruleset_version, previous_status, new_status And the image's final_status equals the new_status of the latest audit entry And audit entries are immutable; any correction is recorded as a new entry referencing prior entry_id in a previous_entry_id field

CapGuard

Set precise spend caps per workspace, client, or user with soft warnings and hard stops. Choose daily/weekly/monthly resets and approval flows for over-cap requests—eliminating surprise bills while keeping essential batches moving under controlled limits.

Requirements

Hierarchical Spend Caps & Inheritance
"As a workspace owner, I want to set different spend limits for each client and their users so that I can control costs per relationship without micromanaging every batch."
Description

Enable admins to define monetary spend caps at workspace, client, and user levels with clear precedence and inheritance rules (most restrictive takes effect unless an explicit override is set). Support per-period caps (amount per day/week/month), per-entity defaults, and currency alignment with the billing account. Caps apply to all PixelLift actions that accrue cost (e.g., background removal, pro edits, A/B variants, bulk exports). Changes to caps take effect immediately and are versioned. Display remaining budget and next reset inline where users launch batches. Ensure compatibility with pricing changes and per-image rate cards without requiring manual recalc.

Acceptance Criteria
Effective Cap Selection Across Hierarchy
Given a workspace daily cap of 100 USD and a client daily cap of 80 USD under that workspace, and no user-level cap for User A When User A initiates a job estimated to cost 10 USD Then the effective cap is 80 USD/day and the job is allowed if remaining ≥ 10 USD Given a user-level explicit cap of 120 USD/day is set for User A When User A initiates a new job Then the effective cap becomes 120 USD/day regardless of ancestor caps Given the user-level cap is removed Then the effective cap reverts to 80 USD/day
Period Types and Reset Calculation
Given a daily cap of 100 USD and 30 USD spent on 2025-09-26 23:59 in the billing timezone When the time reaches 2025-09-27 00:00 in the billing timezone Then the spent amount resets to 0 and remaining becomes 100 USD Given a weekly cap of 700 USD with 400 USD spent before the calendar week boundary When the billing timezone reaches the start of a new calendar week Then the spent resets to 0 and remaining becomes 700 USD Given a monthly cap of 3000 USD with 2990 USD spent on the last day of the month When the billing timezone reaches the first day of the next calendar month at 00:00 Then the spent resets to 0 and remaining becomes 3000 USD
Immediate Effect and Versioning of Cap Changes
Given a client cap v1 of 100 USD/day is active at 14:00 with 20 USD remaining When an admin updates the client cap to 60 USD/day at 14:35:20 Then all cost accrual requests created at or after 14:35:20 are evaluated against 60 USD/day And the version history records v2 with effective_from=2025-09-26 14:35:20, changed_by, previous_value=100, new_value=60 And the inline remaining immediately reflects the new cap (remaining = max(0, new_cap − spent_today))
Pricing Changes Compatibility Without Manual Recalculation
Given a user daily cap of 100 USD and a per-image price of 0.05 USD has resulted in 1000 images processed (spent 50 USD) by 12:00 When the per-image price changes to 0.08 USD at 12:01 Then remaining budget is 50 USD and all new cost estimates use the 0.08 USD rate And the estimated cost for a new 700-image batch is 56 USD and is blocked because it exceeds the remaining 50 USD And previously recorded spend remains at 50 USD without retroactive recalculation
Caps Apply Across All Billable Actions
Given a daily cap of 100 USD and prior spend of 70 USD across background removal (30 USD) and pro edits (40 USD) When the user attempts a bulk export estimated at 35 USD Then the request is blocked because it would exceed the remaining 30 USD And cumulative spend across background removal, pro edits, A/B variants, and bulk exports cannot exceed 100 USD per day under the active cap
Inline Display of Remaining Budget and Next Reset
Given a user is on the batch launch screen and a cap applies When the screen loads Then the UI displays Remaining (e.g., "30 USD of 100 USD left") and Next reset timestamp in the billing currency and timezone And the remaining value updates within 2 seconds after a batch submission or cap change And if no cap applies, the UI displays "No cap set" and no remaining amount
Currency Alignment and Per-Entity Defaults
Given a billing account currency of EUR When an admin sets a workspace default daily cap of 120 EUR and creates a new client and user without explicit caps Then the new client and user inherit a daily cap of 120 EUR And attempts to set caps in a currency other than EUR are rejected with a validation error And all cap values, remaining, and version history entries display in EUR
Reset Schedules & Timezone Anchors
"As a finance admin, I want caps to reset on my billing timezone and cadence so that reporting and invoices align with my accounting periods."
Description

Provide configurable reset cadences for caps (daily/weekly/monthly) with admin-selected reset day and time anchored to a chosen timezone per workspace. Handle daylight saving transitions, proration when cadence or timezone is changed mid-cycle, and backfill of remaining budget accordingly. Surface the next reset timestamp and period-to-date usage in UI and API. Maintain historical usage integrity across cadence changes.

Acceptance Criteria
Daily Reset at Workspace Timezone Anchor
Given workspace timezone = Europe/Berlin And cadence = Daily; reset_time_local = 02:00 And current period started at 2025-09-25T02:00+02:00 And PTD usage before reset = 475 credits When the clock reaches 2025-09-26T02:00+02:00 Then PTD usage resets to 0 within 5 seconds And next_reset_at = 2025-09-27T02:00+02:00 (ISO 8601 with offset) And API GET /caps/{id} returns period_start_at = 2025-09-26T02:00+02:00 and period_end_at = 2025-09-27T02:00+02:00 And UI shows "Resets daily at 02:00 (Europe/Berlin)" and "Next reset: Sep 27, 02:00 CEST" And post-reset batches debit against the new period only
Weekly Reset with Custom Anchor and DST Handling
Given workspace timezone = America/Los_Angeles And cadence = Weekly; anchor_day = Sunday; anchor_time_local = 02:30 When the week includes spring forward on 2025-03-09 (02:00 → 03:00) Then the reset executes at 2025-03-09T03:00-07:00 (next valid local time) And the effective week duration is shortened accordingly And effective_cap_for_week = full_week_cap × (actual_week_duration / 7 days) When the week includes fall back on 2025-11-02 (01:59 → 01:00) Then if the anchor time is an ambiguous local time, the reset executes once at the first valid occurrence and does not execute twice And an audit log entry records executed_at_local, executed_at_utc, timezone, and dst_rule_applied
Monthly Reset Day Handling for Short Months
Given workspace timezone = Asia/Tokyo And cadence = Monthly; reset_day_of_month = 31; reset_time_local = 23:59 When the current month has 30 days (2025-04) Then the reset occurs at 2025-04-30T23:59+09:00 When the current month has 28 days (2025-02) Then the reset occurs at 2025-02-28T23:59+09:00 And API and UI label this as "Last day of month at 23:59 (Asia/Tokyo)" And next_reset_at always points to the last calendar day at 23:59 local time
Cadence Change Mid-Cycle: Monthly to Weekly with Prorated Transition
Given workspace timezone = Europe/London And old cadence = Monthly with cap_limit = 1000 credits; old period = 2025-09-01T09:00+01:00 → 2025-10-01T09:00+01:00 And usage from 2025-09-01T09:00+01:00 to change_time = 620 credits When admin changes cadence to Weekly with full_week_cap = 300 credits; anchor_day = Monday; anchor_time_local = 09:00 at change_time = 2025-09-18T15:00+01:00 (Thursday) Then the old period closes at 2025-09-18T15:00+01:00 with PTD = 620 credits (immutable) And a transition period opens from 2025-09-18T15:00+01:00 to next anchor 2025-09-22T09:00+01:00 And effective_cap_for_transition = 300 × (duration(2025-09-18T15:00+01:00 → 2025-09-22T09:00+01:00) / 7 days) = 300 × (3d18h / 7d) = 162.86 credits (rounded half-up to 2 decimals) And PTD_usage for the transition equals usage between 2025-09-18T15:00+01:00 and now And remaining_budget = max(effective_cap_for_transition − PTD_usage, 0) And next_reset_at = 2025-09-22T09:00+01:00
Timezone Change Mid-Cycle Re-anchors Reset with Proration
Given cadence = Daily; reset_time_local = 01:00; current timezone = UTC; current period = 2025-09-26T01:00Z → 2025-09-27T01:00Z And change_time = 2025-09-26T12:00Z; new timezone = America/New_York When admin confirms timezone change at change_time Then the UTC-anchored period closes at 2025-09-26T12:00Z (immutable) And a transition period opens from 2025-09-26T12:00Z to next local anchor 2025-09-26T21:00-04:00 (which is 2025-09-27T01:00Z) And effective_cap_for_transition = full_daily_cap × (duration(12:00Z → 21:00-04:00) / 24h) And PTD_usage for the transition equals usage between 12:00Z and now And subsequent periods reset daily at 01:00 America/New_York; next_reset_at reflects the local offset including DST
Surface Next Reset and PTD in UI and API
Given a cap with cadence, anchor, and timezone configured When calling GET /caps/{cap_id} Then response includes: cadence, timezone, anchor (day/time), period_start_at, period_end_at, next_reset_at, cap_limit, effective_cap, ptd_usage, remaining_budget And all timestamps are ISO 8601 with timezone offset; next_reset_at == period_end_at And values update within 5 seconds after a reset event And the UI displays "Next reset" and "Usage this period" matching API (±1s) and the workspace timezone label
Historical Usage Integrity Across Changes
Given historical usage totals saved for prior periods A and B When cadence is changed (e.g., Monthly → Weekly) and timezone is changed (e.g., UTC → America/New_York) Then GET /usage?start=periodA.start&end=periodA.end returns the same totals as before the changes And period boundaries and totals for A and B remain unchanged and queryable And audit logs capture before/after cadence, timezone, anchors, and migration steps And no historical invoices or PTD totals are recomputed retroactively
Soft Warnings, Thresholds & Hard Stops
"As an operator, I want early warnings before hitting a cap and a clear stop when I do so that I can avoid interruptions and know exactly how to proceed."
Description

Offer configurable warning thresholds (e.g., 50%, 80%, 100%) with in-app banners, email, and Slack notifications, including per-entity recipients. Enforce hard stops when a cap is reached by preventing new batch submissions and queuing, with clear error messaging and guidance to request approval or wait for reset. Optionally allow essential tagged jobs a limited safety allowance with rate-limited usage to keep critical operations moving. Automatically resume normal operation after reset or approved top-up. Deduplicate alerts to avoid spam and localize user-facing copy.

Acceptance Criteria
Configurable Threshold Warnings per Entity
Given thresholds are configured at the entity scope (workspace/client/user) with defaults [50%, 80%, 100%] and an active reset cadence, When cumulative billable spend for that entity crosses a configured threshold upward, Then display an in-app warning banner to active session users within 5 seconds containing entity name, current spend, cap, percent used, time to reset, and a link to CapGuard settings, And send one email and one Slack notification to the entity’s configured recipients within 60 seconds. Given a threshold has already triggered for an entity in the current period, When further spend occurs without crossing a new threshold, Then no additional notifications are sent for that threshold. Given a user locale is available, When rendering banners and notifications, Then all numbers, currency, dates, and copy are localized to that locale with verified translation keys.
Alert Deduplication and Suppression
Given multiple events attempt to emit the same threshold or hard-stop alert for the same entity, period, and channel, When deduplication rules execute, Then at most one notification per channel per threshold per entity per reset period is sent; subsequent duplicates are suppressed and logged. Given threshold values are edited mid-period, When recomputing notification eligibility, Then notifications are only emitted for thresholds newly crossed upward since the last send; previously sent thresholds are not resent. Given recipient lists overlap across scopes, When resolving recipients, Then addresses/webhooks are deduplicated per channel before send.
Hard Stop Enforcement on Cap Reached
Given the entity’s actual spend is greater than or equal to its cap, When a user submits a new batch via UI or API, Then the submission is blocked before entering the queue, the API returns error code=CAP_EXCEEDED within 2 seconds, and the UI shows a blocking modal/banner with localized guidance to request approval or wait for reset and a link to settings. Given a hard stop is active for an entity, When viewing queues, Then no new jobs for that entity appear in pending or processing; already-running jobs continue unaffected. Given an administrator reduces a cap below current spend, When the next submission attempt occurs, Then the hard stop applies immediately with the same behavior.
Essential-Tagged Safety Allowance with Rate Limiting
Given safety allowance is enabled for an entity and a batch is tagged as essential, When the cap is reached, Then allow submissions up to the configured allowance (amount or percent of cap) and enforce rate limits (e.g., ≤ N images/minute and ≤ M batches/day) per entity. Given the remaining allowance is insufficient for an attempted batch, When submission occurs, Then reject with error code=SAFETY_ALLOWANCE_EXCEEDED and show localized guidance. Given the rate limit is exceeded while allowance remains, When submission occurs, Then reject with error code=SAFETY_RATE_LIMIT and include time-until-available; all allowance usage (amounts, actor, tag, timestamp) is recorded in the audit log.
Reset Cadence and Automatic Resume
Given reset cadence is configured (daily/weekly/monthly) with timezone T, When the reset boundary occurs, Then entity spend counters reset to zero within 60 seconds, threshold state clears, banners dismiss, and hard stops lift automatically without manual intervention. Given a top-up is approved and applied, When the cap increases mid-period, Then hard stops lift immediately, thresholds recompute against the new cap, and new submissions succeed; previously blocked submissions are not auto-enqueued and must be resubmitted by users. Given a reset or top-up has occurred, When the next submission is attempted, Then no residual blocks or stale warnings persist beyond the current state.
Per-Entity Recipient Routing and Fallbacks
Given recipients are configured per entity and channel (in-app roles, email addresses, Slack webhooks/channels), When a notification is emitted, Then recipients resolve by precedence (user-level > client-level > workspace-level), duplicates are removed, invalid endpoints are skipped, and delivery status is logged per recipient. Given no recipients are configured for a channel at any scope, When a notification would be emitted for that channel, Then the system does not attempt delivery on that channel, records a configuration warning, and still displays the in-app banner. Given an admin sends a test notification from settings, When delivery completes, Then the UI surfaces success/failure with provider response details (e.g., SMTP accepted, Slack 2xx).
Approval Request CTA and Hand-off
Given a hard stop is shown, When the user clicks Request Approval, Then the approval module/endpoint opens with entity, current spend, cap, and a default requested top-up prefilled, and a single request is created; repeat clicks within 5 minutes do not create duplicates. Given approval functionality is disabled for the tenant, When a hard stop occurs, Then the Request Approval CTA is hidden and the message instructs users to contact an admin, with a deep link to CapGuard settings. Given an approval decision is returned by the approval system, When approved, Then the cap update is applied within 60 seconds and the requester is notified; When denied, Then the requester is notified with the denial reason; all state changes are captured in the audit log.
Over-cap Approval Workflow & Escalations
"As a project lead, I want a fast approval path when a batch is critical so that work can continue without compromising cost controls."
Description

Implement an approval flow for over-cap requests where users submit a reason, requested amount or temporary cap change, and urgency. Allow configuration of approver roles and chains, SLAs, and auto-escalation if SLAs are breached. Support partial approvals, one-time top-ups, time-bound increases with auto-expiry, and recurring cap adjustments. Provide in-app and email/Slack approval actions, comment threads, and full auditability. Apply approved changes immediately with safe rollback if approvals are revoked or expire.

Acceptance Criteria
Submit Over-Cap Request with Required Fields and SLA Assignment
Given a user exceeds or anticipates exceeding a cap, When they open the over-cap request form, Then the form requires a reason (>= 10 characters), a requested amount or temporary cap target (one must be provided), and an urgency (Low/Medium/High) before submission. Given any required input is missing or invalid, When the user attempts to submit, Then inline validation messages are displayed and the Submit action remains disabled. Given all required inputs are valid, When the user submits, Then a request is created with a unique ID, timestamp, scope (workspace/client/user), current cap/balance, and computed SLA deadline, and the API responds within 2 seconds. Given a request is created, Then the requester sees it in Pending Approvals with status "Pending" and the SLA deadline visible. Given notification channels are configured, When the request is created, Then approvers in the first step are notified via in-app and email/Slack.
Configure Approver Roles, Approval Chains, and SLA/Escalation Policies
Given an admin with permissions, When configuring CapGuard, Then they can create approver roles and assign users or groups per scope (workspace/client/user). Given a multi-step approval chain is defined, When a request is submitted, Then steps execute in the configured order and later steps are not notified until the prior step reaches an approve/deny/timeout outcome. Given a step SLA (e.g., 4 business hours) is configured, When a request enters that step, Then an SLA timer starts using the configured business calendar. Given the SLA timer expires without action, When escalation is configured, Then the request auto-escalates to the next approver/escalation group and sends escalation notifications; otherwise it auto-fails. Given escalation or auto-fail occurs, Then the event is recorded in the audit log with step, timestamp, and actor = system.
Partial Approvals and Amount Adjustments
Given a request for an amount X, When an approver selects Partial Approve, Then they must enter an approved amount Y such that 0 < Y <= X and add a comment. Given a partial approval is saved, Then the request status becomes "Partially Approved", the remaining amount R = X - sum(approved partials) is shown, and the requester is notified. Given cumulative partial approvals reach X, Then the request transitions to "Approved" and proceeds to apply the change. Given cumulative partial approvals exceed X due to a race, Then the last action is rejected and the approver sees an error explaining the current approved total. Given policy requires multiple approvers, When a partial approval is recorded, Then subsequent steps evaluate against the remaining amount.
One-Time Top-Ups and Time-Bound Increases with Auto-Expiry
Given a request is marked as One-Time Top-Up for amount A, When final approval occurs, Then the cap increases by A immediately and the top-up balance is decremented by spend until it reaches $0 or expires. Given a request is marked as Time-Bound Increase from baseline B to temporary T for duration D, When approved, Then cap T is enforced only between start and end timestamps (timezone-aware). Given a top-up or time-bound increase expires or is fully consumed, Then the cap reverts to baseline B automatically within 60 seconds and a notification is sent to the requester and approvers. Given a top-up or time-bound increase is pending expiry within 24 hours, Then reminder notifications are sent per notification policy. Given a running batch would exceed the cap after reversion, Then it is soft-stopped or queued per workspace policy and the event is visible in activity log.
Recurring Cap Adjustments
Given an approver approves a recurring adjustment rule (RRULE), When the schedule triggers, Then the specified cap adjustment is applied at the scheduled time with success/failure logged. Given a scheduled occurrence falls on a configured blackout date, When the adjustment time arrives, Then it is skipped or shifted per configuration and the outcome is recorded. Given multiple recurring adjustments overlap for the same scope, Then precedence rules apply (most recent approval wins unless explicitly additive) and conflicts are flagged to admins. Given a recurrence series reaches its end count or end date, Then no further occurrences are created and the requester is notified of completion. Given a recurrence is cancelled by an approver, Then future occurrences are removed within 60 seconds and a cancellation entry is added to the audit trail.
Omni-Channel Approvals, Comments, and Audit Trail
Given a pending request, When an approver acts via in-app UI, secure email link, or Slack interactive message, Then the action is authenticated, idempotent, and the request status updates within 2 seconds. Given a secure link is used after the request is resolved or expired, Then the action is rejected with an explanation and a link to the current request state. Given any participant posts a comment, When the comment is saved, Then it appears in a threaded timeline with author, timestamp, and edit history; @mentions trigger notifications. Given any state change, Then the audit log records actor, action, previous and new values, timestamp, source IP/client, and correlation ID; audit entries are immutable and exportable (CSV, JSON).
Immediate Application of Approved Changes and Safe Rollback
Given a request reaches final approval, When the approval event is processed, Then approved cap changes take effect across all enforcement points within 60 seconds and are visible in dashboards and APIs. Given an approval is revoked by an authorized approver or an approved change expires, When the revocation/expiry event is processed, Then the system rolls back to prior cap values atomically within 60 seconds and links the rollback to the original approval in the audit trail. Given in-flight batches that would breach after rollback, Then the system completes the current atomic work unit and pauses further processing or soft-stops per policy, notifying the requester. Given a rollback fails at any enforcement point, Then the system retries with exponential backoff up to 5 minutes and surfaces a "syncing" state until consistency is restored or manual intervention is required.
Real-time Cost Estimation & Budget Reservation
"As a power user, I want to know if my planned batch will fit under the cap before I launch it so that I can adjust scope or request approval proactively."
Description

Calculate pre-flight cost estimates for batches based on image count, selected enhancements, marketplace presets, and current pricing. Compare estimates against remaining budget and warn or block as configured. On job start, place budget holds to prevent race conditions and overspend from concurrent batches; release unused holds upon completion or failure. Handle retries, partial completes, and price updates atomically. When estimation uncertainty is high, require explicit confirmation or approval. Persist estimates and final actuals on job records for reconciliation.

Acceptance Criteria
Pre-flight Estimate Accuracy and Completeness
Given a batch has N images, selected enhancements, and a marketplace preset with a published pricing_version When the user requests a pre-flight cost estimate via UI or API Then the system returns an estimate containing total_estimated_cost, currency, per_unit_breakdown, pricing_version_id, computed_at, and inputs_hash And total_estimated_cost equals the sum of (unit_price x quantity) for all selected enhancements and images in the pricing_version And if no job settings, quantities, or pricing_version change before start, the final_actual_cost differs from total_estimated_cost by <= max(1% of estimate, 0.01 currency units) And the estimate is stored on the pending job draft for later comparison
Budget Check and Soft/Hard Cap Enforcement Across Cycles
Given a workspace/client/user has budget policies with cycle in {daily, weekly, monthly}, soft_warn_threshold, and hard_cap And remaining_budget is computed for the active cycle at request time When an estimate is produced Then if projected_spend + estimate >= soft_warn_threshold x hard_cap and < hard_cap, show a non-blocking warning and require user acknowledgment to proceed And if projected_spend + estimate >= hard_cap, block submission and offer a Request Approval flow when enabled And if an approval is granted by an authorized approver within policy, allow submission overriding the block; otherwise keep blocked And all warnings, blocks, and approvals are logged with policy_id and cycle window
Atomic Budget Hold on Job Start and Concurrency Protection
Given an estimate E exists and sufficient remaining_budget exists When the user starts the job Then the system creates an atomic budget_hold for amount E against the budget scope and pricing_version and transitions the job to "started" in the same transaction And the remaining_budget reflects the hold immediately, preventing concurrent jobs from exceeding the cap And if hold creation fails, the job remains not started and the user receives an actionable error with retry_id And each hold has a unique hold_id and is idempotently creatable using an idempotency_key
Hold Release and Charge Reconciliation on Completion/Failure/Partial
Given a job with an active budget_hold When the job completes successfully Then convert the hold to a charge equal to final_actual_cost and release any unused portion within 1 minute And when the job fails before processing any image, release 100% of the hold within 1 minute And when the job partially completes, charge for processed units and either maintain a reduced hold for pending retries or release the remainder And all charge and release events are idempotent, auditable, and reflected in remaining_budget within 1 minute
Atomic Handling of Retries, Partials, and Mid-Run Price Updates
Given a job started with pricing_version_id V and a budget_hold amount H When individual items are retried or resumed Then all retries are priced using V and cannot cause total charges to exceed H unless the hold is atomically increased after a fresh estimate and recheck against remaining_budget And if a new pricing_version V2 is published during execution, the job continues to use V for all items already estimated/held And adjustments to holds (increase or decrease) and state changes are performed in a single atomic transaction with a write lock per budget scope
High-Uncertainty Estimation Requires Confirmation or Approval
Given an estimate has estimation_uncertainty = high due to variable-priced components or missing inputs When the user attempts to start the job Then the system blocks start and requires explicit user confirmation acknowledging the uncertainty or an approval per policy And the confirmation or approval record is linked to the job and captured in the audit log And without confirmation or approval, API and UI both return HTTP 409 Conflict with details
Persist Estimates, Holds, and Actuals for Reconciliation
Given any job that has been estimated and/or executed When querying the job record via UI or API Then the job contains persisted fields: total_estimated_cost, per_unit_breakdown, pricing_version_id, computed_at, estimation_uncertainty, budget_scope, hold_id (if started), final_actual_cost, actual_breakdown, reconciliation_status, and audit trail of estimate/hold/charge/release/approval events And a reconciliation report can match estimates to actuals 1:1 using job_id and pricing_version_id And historical records remain immutable except for appended audit entries
Audit Logs, Insights & Export
"As a compliance and finance stakeholder, I want a full history of CapGuard events and usage so that I can audit decisions and optimize budgets."
Description

Record immutable, timestamped events for cap configuration changes, warnings, stops, approvals, estimate-to-actual deltas, and budget holds/releases. Provide searchable views and filters by workspace, client, user, job, and timeframe. Visualize usage versus cap with burn-rate forecasts and days-to-cap projections. Offer CSV/JSON export and API access with role-based permissions. Apply data retention policies and redact PII where not required for compliance (SOC 2-ready).

Acceptance Criteria
Immutable Event Logging for Cap Lifecycle
Given a workspace with CapGuard enabled When any of the following occurs: cap configuration change, soft warning triggered, hard stop enforced, over-cap approval requested/approved/denied, estimate-to-actual delta posted, budget hold placed/released Then an audit event is written with fields: event_id (ULID), timestamp (UTC ISO 8601 with ms), actor_type, actor_id, actor_ip (redacted per policy), scope_type (workspace/client/user/job), scope_id, event_type, prev_value, new_value, reason_code (nullable), correlation_id And events are append-only; any update/delete attempts via UI or API return 403 and create a security_access_denied audit event And event timestamps are monotonic within a single correlation_id And system clocks are synchronized to keep inter-service skew ≤ 200 ms
Search and Filter Audit Events
Given a user with Audit.Read permission When they filter by any combination of workspace, client, user, job, event_type, and timeframe (absolute or relative) Then only matching events are returned, and non-matching events are excluded And default sort is timestamp desc; user can toggle asc; sort stability is guaranteed for equal timestamps via event_id And first page (50 items) responds in ≤ 2 seconds for datasets up to 100k events; cursor pagination supports next/prev And responses include page_size, has_more, and a stable cursor token; total_count is returned up to a cap of 10k
Usage vs Cap Visualization and Forecasts
Given a cap with a daily, weekly, or monthly reset When viewing Usage vs Cap for a selected period Then the chart displays consumed, remaining, cap line, and soft/hard threshold markers for that period And burn rate is calculated as a trailing 7-day (or period-adjusted) average and shown numerically And days-to-cap projection is computed from current burn rate, refreshed at least every 15 minutes And for a constant-burn test dataset, next-period projection error is within ±10% And hover/tooltip reveals timestamp, usage, cap, burn rate values
CSV/JSON Export and Audit API
Given a user with Audit.Export permission When exporting with selected filters and fields Then CSV is UTF-8 with header row and RFC 4180 quoting; JSON is line-delimited NDJSON And exports include columns: event_id,timestamp,workspace_id,client_id,user_id,job_id,event_type,prev_value,new_value,reason_code,actor_id,actor_type,correlation_id And exports over 1,000,000 rows are streamed and complete without timeout within 10 minutes; progress status is visible And the API exposes GET /audit/events with filter params, cursor pagination, ETag, and rate limits; exceeding limits returns 429 and logs an audit event And time filters use inclusive start and exclusive end semantics
Role-Based Access Control for Logs, Insights, and Exports
Given roles Owner, Admin, Billing Manager, Analyst, and Member When accessing audit logs, insights, or exports Then Owner/Admin/Billing Manager can read all scopes in the workspace; Analyst can read assigned clients; Member can read only self user/job scope And only Owner/Admin/Billing Manager can export; others receive 403 And scope boundaries are enforced at API and UI; unauthorized attempts return 403 and generate an RBAC_DENY audit event
Data Retention and PII Redaction Policy Enforcement
Given workspace retention settings (e.g., 90/180/365 days) and legal_hold flag When events exceed retention and are not under legal_hold Then events are purged within 24 hours by scheduled jobs; daily tombstone counts are maintained per scope for reporting And PII (email, IP, raw file names) is redacted or SHA-256 hashed with workspace salt at write-time unless compliance_required=true; redaction flags are stored per field And exports/API never return unredacted PII to roles below Owner unless legal_hold AND compliance_required are true And purge and redaction actions generate audit events; a SOC 2 evidence report of retention jobs is available for the last audit period
Over-cap Approval Flow Auditability and Delta Tracking
Given an over-cap request on a batch When the request is submitted, approved, denied, or times out Then audit events capture: request_id, requested_amount, justification, requester_id, approver_id, outcome, timestamps for request/decision, and SLA duration And approvals older than SLA (e.g., 4 hours) trigger a warning audit event And at job completion, estimate-to-actual delta is logged with estimate_amount, actual_amount, delta_abs, delta_pct, and variance_reason tags And insights list the top 10 jobs by positive delta for a selectable timeframe with links to underlying events And correlation_id links the request, decision, stop/warning, and completion events

Smart Top-Ups

Automate credit refills with rule-based triggers (balance threshold, time windows, or forecasted depletion). Prioritize payment methods, require approvals above set amounts, and pause/resume rules in one tap—preventing downtime without overspending.

Requirements

Balance Threshold Top-Up Rules
"As a PixelLift seller, I want my credits to auto-refill when my balance drops below a set level so that my image processing never pauses."
Description

Enable configurable auto-refill rules that trigger when an account’s credit balance falls below a defined threshold. Rules support per-workspace scoping, currency awareness, minimum/maximum top-up amounts, per-day caps, idempotency to prevent duplicate purchases, and retry/backoff on transient failures. Integrates with the billing service and balance ledger to create and settle top-up transactions, and exposes a UI for creating, testing (simulation), activating, and deleting rules. Ensures continuous processing of photo jobs by preventing balance-related downtime while providing guardrails against overspend.

Acceptance Criteria
Threshold Trigger Executes Top-Up
Given a workspace with currency USD, a current balance of 45 USD, and an active threshold rule set to 50 USD with a top-up amount of 100 USD When the balance update event is processed or the periodic evaluator runs (interval <= 60 seconds) Then exactly one top-up transaction for 100 USD is created, authorized, settled, and the ledger balance increases to 145 USD within 30 seconds And Given the balance equals the threshold (50 USD) When the evaluator runs Then no top-up is created And Given the balance drops below the threshold, rises above it due to a top-up, and later drops below again When the evaluator runs Then a new top-up is created for each distinct breach separated by a recovery above threshold And Given active photo job processing and the balance is insufficient for the next job When the evaluator runs Then the top-up completes before the next job charge is attempted, resulting in zero "insufficient funds" job failures during the event
Per-Workspace Rule Scoping
Given two workspaces W1 and W2 with independent balances and rules When W1 breaches its threshold Then a top-up is created only for W1 and W2 remains unchanged (no transaction, no ledger entry) And Given a user is an admin in W1 but not W2 When they view or manage rules Then they can only see and modify rules for W1 And Given daily caps are configured per workspace When W1 reaches its daily cap Then W2 can continue to top-up unaffected
Min/Max Amounts and Daily Cap Guardrails
Given a rule with min amount 20 USD, max amount 200 USD, and daily cap 500 USD (UTC) When a top-up triggers Then the purchase amount must be between 20 and 200 USD inclusive And Given cumulative settled top-ups for the rule reach 500 USD since 00:00:00 UTC today When another breach occurs Then no purchase is made, an audit log entry is created, and the rule status displays "Daily cap reached" And Given the day rolls over to 00:00:00 UTC When the next breach occurs Then top-ups resume and the cap counter resets And Given the configured amount violates processor or system minimums/maximums When attempting to save the rule Then the UI blocks save with a clear validation message
Idempotency and Concurrency Safety
Given multiple concurrent evaluators detect the same threshold breach When they attempt to create a top-up Then at most one settled transaction is created and the ledger reflects a single credit And Given duplicate requests are sent with the same idempotency key for a rule and breach window When processed Then subsequent requests return the original transaction reference without side effects And Given transient network errors occur after authorization but before acknowledgement When retries occur Then the system correlates by idempotency key and does not double-charge
Transient Failure Retry and Backoff
Given the billing service returns a retryable error (HTTP 429 or 5xx) When a top-up attempt fails Then the system retries up to 5 times with exponential backoff of approximately 10s, 30s, 60s, 120s, and 300s And Given all retries fail When the final attempt completes Then the rule records a "Top-up failed" event with error details and the user is notified via in-app alert and email And Given a retry later succeeds within the attempt limit When settlement completes Then only one ledger entry exists and the rule shows the final success with the total elapsed time
Billing and Ledger Integration Accuracy
Given a successful top-up When the transaction settles Then the billing system shows a paid charge/invoice matching the configured amount and currency, and the ledger records a credit entry with ruleId, transactionId, currency, amount, timestamp, and actor=system And Given refunds or voids are not part of auto top-ups When inspecting the ledger Then no debit reversal is created for a successful top-up And Given the workspace currency is EUR When a top-up occurs Then the transaction and ledger entries are in EUR and no cross-currency conversion is performed
Rule Management UI: Create, Simulate, Activate, Delete
Given a user creates a rule When required fields (threshold, amount, currency, min/max, daily cap, workspace) are valid Then the rule saves and appears with the selected Active/Inactive state and visible status And Given a user clicks "Simulate" with a hypothetical balance value When the simulation runs Then it returns whether a top-up would fire, the amount, and the post-top-up balance, and no transaction or ledger change occurs And Given a rule is toggled to Paused When a breach occurs Then no top-up is created until the rule is reactivated And Given a rule is deleted When confirmed Then it no longer appears in active rules, no further triggers occur, and historical events remain viewable in audit logs And Given the workspace currency is set When creating a rule Then the currency field defaults to the workspace currency and cannot be set to a different currency
Scheduled Top-Up Windows
"As a cost-conscious seller, I want top-ups to occur only within my designated time windows so that I can control cash flow and availability for approvals."
Description

Allow users to define allowed and quiet-hour windows during which auto top-ups may execute. Support multiple weekly schedules, account-level time zone selection, and holiday exceptions. When a trigger occurs outside an allowed window, queue the top-up until the next eligible window, with an option to override if depletion would occur before then. Display next run window and queued state in the UI, and ensure all scheduling respects rule priority and approval requirements.

Acceptance Criteria
Allowed and Quiet Hours Enforcement
Given a Smart Top-Up rule with allowed window Mon–Fri 09:00–18:00 and quiet hours 12:00–13:00 in the account’s time zone When a trigger occurs Wednesday at 10:30 local time Then the top-up executes immediately Given the same configuration When a trigger occurs Wednesday at 12:15 local time Then the top-up does not execute during quiet hours Given the same configuration When a trigger occurs Wednesday at 18:05 local time Then the top-up does not execute outside the allowed window
Multiple Weekly Schedules and Overlaps
Given allowed windows Mon 09:00–12:00, Mon 10:00–14:00, and Thu 15:00–18:00 When a trigger occurs Mon 10:30 local time Then one and only one execution occurs Given the same configuration When a trigger occurs Thu 16:00 local time Then the top-up executes within that window Given a day has no allowed windows When a trigger occurs on that day Then no execution occurs at that time
Account Time Zone and DST Handling
Given the account time zone is America/Los_Angeles and an allowed window 09:00–11:00 local time When a trigger occurs at 09:15 local time Then the top-up executes and audit logs include both UTC and local timestamps Given a DST start where 02:00–03:00 local time does not exist and an allowed window spans 01:30–03:30 When the window opens Then execution is permitted 01:30–01:59 and 03:00–03:30; no attempts occur in the nonexistent interval Given the account time zone is changed to America/New_York When the change is saved Then next-run window and any queued ETAs are recalculated to the new local time within 60 seconds
Holiday Exceptions Block Execution
Given a holiday exception is configured for 2025-12-25 (account local date) When a trigger occurs on 2025-12-25 at 10:00 within an otherwise allowed window Then no automatic execution occurs due to the holiday exception Given consecutive holiday exceptions for 2025-12-25 and 2025-12-26 When a trigger occurs on 2025-12-25 outside allowed windows Then the next eligible window is computed on the next non-exception day (e.g., 2025-12-27)
Queueing Outside Windows and UI Visibility
Given an out-of-window trigger occurs When the system evaluates the rule Then a queued top-up record is created with reason "OutOfWindow" and fields next_run_window_start and next_run_window_end set in the account’s local time Given a queued top-up exists When the user opens the Smart Top-Ups rule details Then the UI displays state "Queued" and a Next Run window matching the queued record Given time advances to next_run_window_start When the window opens Then the queued top-up becomes eligible to execute subject to priority and approval checks Given a queued record’s ETA changes When the dashboard is refreshed Then the UI reflects the updated state within 15 seconds
Depletion Override Before Next Window
Given override-before-depletion is enabled for the rule and the forecasted depletion time is before next_run_window_start When an out-of-window trigger occurs Then the system attempts immediate execution, subject to approval thresholds and payment method availability, and logs reason "OverrideDueToDepletion" Given override-before-depletion is disabled or forecasted depletion is after the next eligible window When an out-of-window trigger occurs Then no override occurs and the top-up remains queued until the next eligible window
Priority and Approvals Respect Scheduling
Given two rules A and B are eligible to run at the same time and A has higher priority than B When both are evaluated at window open Then A executes before B and both execute if approvals and funds permit Given a rule requires approval because the top-up amount exceeds the approval threshold When a trigger occurs within an allowed window Then the top-up enters state "AwaitingApproval" and does not execute until approved; after approval it executes at the earliest eligible time respecting windows Given a queued item’s window opens while it is still awaiting approval When approval is granted outside an allowed window Then execution is scheduled for the next eligible window
Forecasted Depletion Trigger
"As a campaign-focused seller, I want PixelLift to predict when credits will run low so that refills happen proactively before high-traffic spikes."
Description

Introduce a predictive trigger that initiates a top-up when projected usage indicates the balance will hit a configurable threshold within a chosen horizon (e.g., 6–24 hours). Use recent consumption rates, queued jobs, and marketplace sync schedules to compute a forecast with confidence bands. Provide a simple moving-average model initially with a pluggable strategy for future models. Surface forecast rationale in the UI (inputs, horizon, confidence) and allow per-rule configuration of forecast sensitivity and minimum top-up amount.

Acceptance Criteria
Trigger Initiation on Forecasted Threshold Breach
Given a rule with balance_threshold = 50 credits and horizon = 12 hours and model = "SMA" with window = 12h And current balance = 140 credits And the forecast at configured sensitivity projects balance <= 50 within 9 hours When the forecast evaluation job runs Then a top-up is created for this rule within 1 minute of evaluation completion And the top-up amount is >= the rule's minimum_top_up_amount And the trigger event stores a snapshot of inputs, window, horizon, sensitivity, confidence band, and projected depletion time with a UTC timestamp
Forecast Inputs Integration
Given consumption history covering the past 48 hours and SMA window = 12h And queued_jobs = 200 images at 0.6 credits/image And upcoming marketplace syncs at t+2h (100 images) and t+8h (300 images) When the forecast is computed Then the projected depletion time reflects the combined load of history, queued jobs, and scheduled syncs And removing each input in isolation changes the forecast by the expected delta (±1% tolerance)
Confidence Bands and Sensitivity Control
Given forecast sensitivity = 90% (confidence band) And mean forecast does not cross threshold within horizon But the 90% lower-balance band does cross within horizon When evaluation runs Then the trigger fires And when sensitivity is set to 80% under the same data Then the trigger decision updates accordingly based on the 80% band And the used sensitivity value is recorded with the trigger event
Configurable Forecast Horizon and Threshold
Given allowed forecast horizon range is 6–24 hours with default = 12 When a user saves a rule with horizon = 24 and threshold = 100 credits Then the values persist and are used on the next evaluation And attempts to save horizon outside 6–24 are rejected with inline validation errors And the trigger only considers breaches predicted within the configured horizon
Minimum Top-Up Amount Enforcement
Given minimum_top_up_amount = $20 When a trigger condition is met and the calculated needed amount = $12 Then the system creates a $20 top-up And when the needed amount = $45 Then the system creates a $45 top-up
UI Forecast Rationale Display
Given a user opens Rule Details or a Trigger Event modal When the most recent evaluation exists Then the UI shows: model name, SMA window, inputs summary (consumption window, queued job count and credits, scheduled syncs), configured threshold, horizon, sensitivity, confidence percentage, projected depletion time, and last evaluation timestamp (UTC) And values displayed match the corresponding evaluation log payload
Pluggable Forecast Strategy Interface
Given strategy options are pluggable via a named interface and configured per rule When "MockStrategy" is selected for a test rule Then the system invokes MockStrategy to compute the forecast And switching to "SMA" requires no code changes and succeeds on the next run And if a non-existent strategy is configured, SMA is used as fallback and a warning is logged
Payment Method Priority & Failover
"As a seller with multiple payment options, I want PixelLift to try my preferred method first and fall back automatically so that refills succeed without interruptions."
Description

Support an ordered list of payment methods per account with automatic failover. Attempt the primary method first, then cascade to backups on failure according to user-defined priority. Validate methods upfront, handle SCA/3DS challenges via secure links, and respect regional compliance. Provide UI to add, verify, reorder, and disable methods. Log failure reasons, apply smart retry rules, and prevent infinite retries by locking a failing method until user action. Ensures high top-up success rates without manual intervention.

Acceptance Criteria
Primary Method Success and Priority Order Honored
Given an account with prioritized payment methods [Primary, Backup] and an auto top-up job for $50 USD is triggered When the primary method is valid and authorization is approved Then the system charges only the primary method And the backup method is not attempted And the top-up job status is Success with failover_used=false And the attempt log records timestamp, masked_method_id, amount=50, currency=USD, response_code=approved, and priority_index=1
Automatic Failover on Decline with Lockout and Logging
Given prioritized methods [M1, M2] and an auto top-up job for $50 is triggered When M1 returns a hard decline (e.g., do_not_honor) on first attempt Then the system immediately attempts M2 within the same job And M1 is locked for auto-retries until the user updates or re-enables it And no further attempts are made on M1 in subsequent auto top-ups while locked And the job status is Success if M2 succeeds, with failover_used=true And the log includes M1 decline reason code, M2 success code, and the failover chain [M1 -> M2]
Upfront Payment Method Validation on Add
Given a user adds a new payment method in the Payment Methods UI When the user saves the method Then the system tokenizes the method with the PSP and performs a $0 or $1 auth-and-void per PSP/region capability And the method status becomes Verified on successful auth, or Needs Attention on failure with surfaced reason And AVS/ZIP checks are stored when available And no CVV is stored and PAN is masked to last 4 in storage and logs
SCA/3DS Challenge via Secure Link
Given an auto top-up job requires SCA/3DS for the selected method When the PSP requests a challenge Then the system generates a single-use, expiring secure link valid for 15 minutes and sends it via the configured notification channel And upon successful challenge completion within validity, the charge is captured and the job marked Success And on challenge failure or timeout, the method is not retried; the system fails over to the next eligible method And the log records challenge_started_at, completed_at, outcome, and challenged_method_id (masked)
UI to Add, Reorder, Disable Methods
Given the user has at least two payment methods When they drag-and-drop to reorder and click Save Then the new priority order is persisted and used by the next auto top-up And when the user toggles a method to Disabled, it is excluded from auto attempts immediately And when a method is deleted, it is removed and cannot be attempted And all changes are audit-logged with actor_id, timestamp, action, and previous/new values And the UI prevents saving duplicate priorities and shows inline validation errors on failure
Smart Retry Rules and Infinite Retry Prevention
Given an auto top-up attempt returns a soft decline (e.g., network_error) for the current method When smart retry rules are applied Then the system schedules up to 3 retries with exponential backoff at approximately 5m, 30m, and 2h And if any retry succeeds, the job is marked Success and no further retries are scheduled And if retries are exhausted without success, the method is locked and the system fails over to the next eligible method And hard declines are not retried and trigger immediate failover And within a single job, no method is attempted more than once; no retry loop occurs
Spend Limits & Approval Workflow
"As an account owner, I want large top-ups to require my approval so that we avoid unexpected or excessive charges."
Description

Introduce spend controls with thresholds that require approval for top-ups above specified amounts and caps for per-transaction, daily, and monthly spend. Define approver roles and fallback approvers, send actionable approval requests via email/in-app, and auto-expire requests after a configurable time. Include approval comments, amount details, trigger reason, and projected impact. Block execution until approved when thresholds are met; otherwise proceed automatically. Maintain a complete audit trail and integrate with role-based access control.

Acceptance Criteria
Top-Up Above Threshold Requires Approval
Given a Smart Top-Up rule with an approval threshold of $X and a pending top-up amount $Y where Y > X And the rule is active When the top-up trigger is fired for amount $Y Then the system must block execution and create an approval request assigned to the configured approver role(s) And the approval request must include: top-up amount, threshold, trigger reason, projected impact, rule identifier, requester, and expiry timestamp And the requester must receive an in-app notification and email with the approval request And the top-up must not execute until the request is approved And if the approver approves, the top-up executes and the approval decision is linked in the audit trail And if the approver rejects, the top-up does not execute and the requester is notified
Auto-Proceed When Below Threshold
Given a Smart Top-Up rule with an approval threshold of $X and a pending top-up amount $Y where Y ≤ X And the rule is active When the top-up trigger is fired for amount $Y Then the system must execute the top-up without creating an approval request And the audit trail must record that no approval was required due to threshold And the requester receives a confirmation of execution
Per-Transaction, Daily, and Monthly Caps Enforcement
Given per-transaction cap = PT, daily cap = D, and monthly cap = M configured for a Smart Top-Up rule And cumulative spend for the current day is Sd and for the current month is Sm When a top-up request for amount $Y is evaluated Then the request must be blocked if Y > PT or (Sd + Y) > D or (Sm + Y) > M And the system must present a clear error indicating which cap(s) were violated And no approval can override a violated cap And the audit trail must record the attempted amount, cap values, current spend counters, and the block reason And daily counters reset at 00:00 in the account time zone; monthly counters reset at 00:00 on the first day of the calendar month in the account time zone
Approval Request Content and Actionability
Given an approval request is created for a top-up When the request is sent to approvers Then the email and in-app request must include: amount requested, remaining daily/monthly budget, threshold that triggered approval, trigger reason, projected impact, rule name/ID, requester identity, and expiry countdown And both channels must provide Approve and Reject actions And the Approve/Reject actions must require authenticated access and record the actor’s identity and timestamp And a comment is optional on approval and required on rejection And upon action, the requester is notified and the request status updates immediately
Approval Timeout, Expiry, and Fallback Routing
Given an approval request with expiry T and a primary approver role with fallback approver(s) configured When no primary approver acts within the escalation window S (< T) Then the request must reassign to fallback approver(s) and notify them When no approver acts before T elapses Then the request status becomes Expired, the top-up is not executed, and the requester and approver(s) are notified of expiry And the audit trail must capture timestamps for assignment, reassignment, and expiry events And expired requests cannot be actioned further and require a new trigger to re-initiate
Role-Based Access Control for Spend Controls and Approvals
Given RBAC is enabled with roles: Owner, Billing Admin, Approver, Requester, Viewer When a user without Billing Admin or Owner role attempts to configure thresholds, caps, or approver roles Then the action must be denied with an authorization error and no changes saved When a user with Approver role views or acts on assigned approval requests Then they can Approve/Reject but cannot modify thresholds, caps, or approver mappings When a Viewer accesses approvals Then they can view status and read-only details but cannot act And all authorization decisions must be captured in the audit trail
End-to-End Audit Trail Integrity
Given any event related to spend limits or approvals occurs (configuration change, threshold evaluation, approval creation, action, expiry, cap block, execution) When the audit log is queried for the rule or request Then it must show an immutable, time-ordered record including: actor identity, role, action, previous values, new values, request/approval IDs, timestamps, notification channels, and outcomes And audit records must be non-editable and only appendable And audit logs must be filterable by date range, rule ID, approval status, actor, and outcome And export must include all displayed fields and preserve ordering
One-Tap Pause/Resume Rules
"As a seller, I want to pause auto top-ups with one tap during audits so that I can temporarily halt spend without deleting my rules."
Description

Provide a single-action control to pause or resume individual rules or all Smart Top-Ups from the dashboard and mobile views. Include optional timed pause (e.g., pause for 1/4/24 hours), automatic resume, and confirmations with depletion risk warnings. While paused, queue triggers and display clear status indicators and the queued count. Ensure safe concurrency so pausing mid-execution does not interrupt in-flight transactions.

Acceptance Criteria
One-Tap Pause/Resume Control (Dashboard and Mobile)
Given an authenticated user with Manage Top-Ups permission And the user is on the Smart Top-Ups list, Rule Detail, or Mobile Rules screen When the user taps/clicks Pause on a specific rule Then the rule state changes to Paused within 1 second And the control label updates to Resume and the status chip shows Paused And a confirmation message is shown When the user taps/clicks Resume Then the rule state changes to Active within 1 second And the status chip shows Active and the control label updates to Pause And the control is operable via keyboard (Enter/Space) and exposes aria-pressed, meeting WCAG 2.1 AA
Timed Pause Options and Auto-Resume
Given a user initiates a pause on a rule or All Rules When the user selects a timed pause of 1h, 4h, or 24h and confirms Then the selected scope enters Paused (Timed) with a visible countdown (HH:MM) And the countdown accuracy is within ±1 minute And at expiration the scope resumes automatically within 60 seconds And if the user resumes manually before expiration, auto-resume is canceled And an audit log records pause start, selected duration, early/manual resume (if any), and auto-resume (actor=system)
Depletion Risk Confirmation Warning
Given the system forecasts credit depletion from recent usage When the user attempts a manual or timed pause And forecasted depletion falls within the selected pause window Then a confirmation modal displays a warning with estimated depletion timestamp (user timezone) and estimated impacted jobs/images And the primary action defaults to Cancel; the user must explicitly Confirm to proceed And if no risk is detected, the modal states No immediate depletion risk detected (no warning icon)
Queueing While Paused with Visible Count
Given a rule is in Paused (Manual or Timed) state When a top-up trigger occurs for that rule Then the trigger is queued and not executed; no authorization attempt is made And the UI displays a Queued: <count> badge for the rule And the queued count updates within 2 seconds of each new trigger When the rule resumes Then queued triggers begin processing FIFO within 30 seconds And the queued count decrements as items are processed until zero
Safe Concurrency on Mid-Execution Pause
Given a top-up transaction is in-flight for a rule (authorization initiated) When the user pauses that rule or activates Global Pause Then the in-flight transaction is allowed to complete or roll back per gateway outcome without force-cancel And no duplicate authorizations/captures occur (idempotency enforced) And the rule transitions to Paused immediately after the in-flight completes And an audit log captures pause request time, in-flight outcome, and effective pause time
Global Pause/Resume Precedence and Cross-Device Consistency
Given at least one rule is Active When the user activates Global Pause from any device Then all Active rules display Paused (Global) on the initiating device within 1 second and on other active sessions within 5 seconds And individual rule Pause controls are disabled or indicate Paused (Global) while Global Pause is active When the user invokes Global Resume Then only rules that were Active before Global Pause return to Active; rules previously paused individually remain Paused (Manual/Timed) And all sessions reflect the new state within 5 seconds
Notifications & Audit Logging
"As an account admin, I want clear alerts and a detailed audit trail for all top-ups so that I can monitor spend and quickly resolve issues."
Description

Deliver real-time notifications for key events (top-up executed, failed, awaiting approval, queued by schedule, SCA required, rule paused/resumed) via email, in-app, Slack, and webhooks. Allow per-event and per-channel preferences. Record immutable audit logs capturing rule ID, trigger type, amounts, approvers, timestamps, payment method used, and IP/device metadata. Provide searchable history, CSV export, and API access to support finance reviews, compliance, and incident resolution.

Acceptance Criteria
Real-time notification: Top-up executed
Given an enabled Smart Top-Ups rule triggers a successful top-up And the account owner has enabled "Top-up Executed" notifications for email, in-app, Slack, and webhook When the transaction is confirmed by the payment processor Then one notification is delivered per enabled channel within 10 seconds And each message includes: event_type="topup.executed", rule_id, trigger_type, amount, currency, payment_method, transaction_id, timestamp_utc, actor, ip_address, device And the Slack message posts to the configured workspace/channel, and the webhook responds 2xx or is retried with exponential backoff for up to 24 hours with an idempotency_key And the in-app notification appears in the Notification Center and increments the unread counter by 1 And duplicate notifications for the same transaction_id are not generated
Real-time notification: Top-up failed
Given a Smart Top-Ups rule attempts a top-up that fails And the user has enabled "Top-up Failed" notifications for any channel When the failure is returned by the payment processor Then a notification is delivered per enabled channel within 10 seconds And the payload includes: event_type="topup.failed", rule_id, trigger_type, amount, currency, payment_method, failure_code, failure_message, transaction_id, timestamp_utc, ip_address, device And webhook delivery uses at-least-once semantics with idempotency_key and exponential backoff up to 24 hours And the in-app notification labels the event as Failed and links to the audit log entry
Real-time notification: Strong Customer Authentication required
Given a top-up attempt requires Strong Customer Authentication (SCA) And the approver has enabled "SCA Required" notifications When the SCA challenge is requested by the payment processor Then a notification is delivered per enabled channel within 5 seconds And the notification includes: event_type="topup.sca_required", rule_id, amount, currency, payment_method, sca_deadline_utc, approval_url, transaction_id, timestamp_utc And following the approval_url launches the SCA flow and succeeds when completed within the deadline
Approval workflow notification: Awaiting approval
Given an organization has a top-up approval threshold configured And a Smart Top-Ups rule triggers a top-up above that threshold And approvers are configured for the workspace When the top-up enters Awaiting Approval state Then approvers receive notifications per their channel preferences within 10 seconds And the message includes: event_type="topup.awaiting_approval", rule_id, amount, currency, requested_by, payment_method, approver_list, approval_deadline_utc, approval_link, deny_link, timestamp_utc And selecting Approve authorizes the top-up and records an audit log entry; selecting Deny cancels the request and records an audit log entry
Lifecycle notifications: Rule queued, paused, resumed
Given a Smart Top-Ups rule is scheduled and enters queued state When the rule is queued by schedule Then a notification is sent per enabled channels within 10 seconds including: event_type="rule.queued", rule_id, schedule_window, timestamp_utc And when a user pauses or resumes the rule Then a notification is sent per enabled channels within 10 seconds including: event_type in {"rule.paused","rule.resumed"}, rule_id, changed_by, reason (optional), timestamp_utc, ip_address, device And an audit log entry is created for each state change
Preferences: Per-event, per-channel opt-in/out honored
Given a user configures notification preferences per event and per channel When any notification-eligible event occurs Then notifications are only sent for the channels enabled for that specific event And disabling a channel for an event prevents delivery for subsequent occurrences, effective within 1 second of saving the preference And preferences persist across sessions and are retrievable via GET /v1/notification-preferences reflecting current values
Audit logging and access: Immutable records, search, CSV, API
Given any notification-eligible event is processed When the system records the audit log Then an append-only entry is created with fields: event_type, rule_id, trigger_type, amount, currency, payment_method, approver_ids (if any), timestamp_utc (server-generated), actor, ip_address, device, transaction_id (if any) And there is no API to update or delete audit entries; such requests return 405/403 And each entry has a stable audit_id and checksum; GET /v1/audit/verify returns valid for the current chain And audit history can be searched by event_type, rule_id, transaction_id, date range, payment_method, and ip_address, returning results within 2 seconds for up to 10k records with pagination And CSV export returns the filtered results with a header row, UTF-8 encoding, comma-separated, UTC timestamps, and a signed download URL that expires in 24 hours And API access via GET /v1/audit-logs supports pagination (limit, cursor), filtering as above, sorting by timestamp, and enforces 60 requests/minute per token

Client Buckets

Allocate prepaid credit buckets to clients, brands, or projects and lock queues/presets to the right bucket. Track burn rate, set expiries, attach PO numbers, and export billable usage summaries—making agency pass-through billing effortless and accurate.

Requirements

Bucket Creation & Ownership Model
"As an agency admin, I want to create and manage prepaid buckets tied to clients or projects so that my team can route costs correctly and maintain financial separation."
Description

Enable creation and management of prepaid credit buckets scoped to a tenant, with attributes including bucket name, type (client/brand/project), owner organization, default currency, current balance, status (active/archived), and audit trail. Provide CRUD via UI and API with multi-tenant isolation, optimistic concurrency for balance-affecting updates, uniqueness constraints per tenant, and soft-delete/archival for compliance. Integrate with the billing ledger for balance source of truth and expose bucket selection in relevant workflows. Migrate/import initial balances from CSV for onboarding, and log all changes with actor, timestamp, and reason.

Acceptance Criteria
UI Bucket Creation with Tenant-Scoped Uniqueness
Given I am an authenticated tenant admin When I create a new bucket via UI with a unique name within my tenant, a valid type (client|brand|project), an owner organization, a default currency from the supported list, and status set to Active Then the bucket is created with a generated ID, persisted attributes, and visible in the tenant’s bucket list And the name is unique within the tenant and rejected with a clear error if duplicated And required fields are validated with inline error messages And the bucket does not appear to users outside the tenant
API Bucket Creation with Multi-Tenant Isolation
Given I have a valid OAuth token scoped to tenant T When I POST /api/buckets with a payload containing name, type, ownerOrganizationId, defaultCurrency, and status Then I receive 201 Created with the bucket resource (including id, createdAt, status) and Location header And the bucket is accessible only with tenant T credentials; cross-tenant access returns 404/403 And duplicate name within tenant returns 409 Conflict; invalid fields return 422 Unprocessable Entity; missing/invalid auth returns 401 And responses include an ETag for subsequent conditional operations
Balance Updates via Ledger with Optimistic Concurrency
Given a bucket with a current balance derived from the billing ledger When a debit or credit is requested via API using If-Match with the latest ETag (or version) Then a corresponding immutable ledger entry is recorded with amount, currency, actor, timestamp, and reason And the computed balance reflects the new entry without rounding errors beyond currency minor units And concurrent conflicting updates return 409 Conflict with no double-application of entries And debits that would drive balance below zero are rejected with 422 unless overdraft is explicitly enabled (if supported), otherwise 403 And idempotency-key prevents duplicate application of the same balance change within 24 hours
Audit Trail on Bucket CRUD and Balance Changes
Given auditing is enabled system-wide When a bucket is created, updated (metadata), archived/restored, or has balance-affecting ledger entries Then an immutable audit record is written capturing actor (user/service), action, timestamp (UTC ISO-8601), target bucket ID, and reason/comment And metadata updates include before/after values for changed fields And audit records are queryable by bucket ID and date range via API and UI export (CSV) And audit records cannot be edited or deleted by tenant users; only appended
Soft-Delete/Archive and Restore Behavior
Given a bucket in Active status When a user with proper permissions archives the bucket Then the bucket status changes to Archived without physical deletion, and it is excluded from selection in workflows And new debits are blocked while credits/refunds remain allowed for reconciliation And name uniqueness remains enforced across Active and Archived buckets within the tenant And when restored, the bucket returns to Active and becomes selectable again, with full history intact
CSV Import of Initial Balances for Onboarding
Given I upload a CSV containing columns: name, type, ownerOrganizationId, defaultCurrency, initialBalance, status When I run a dry-run validation Then the system reports row-level errors (invalid currency, duplicate name within tenant, missing required fields, non-numeric balance) and a summary When I confirm import with a client-supplied idempotency key Then valid rows are created as buckets and initial balances are recorded as ledger credits And invalid rows are skipped with an error report downloadable as CSV; the operation is atomic per-row, not all-or-nothing And re-submitting the same file with the same idempotency key does not create duplicates
Bucket Selection in Workflows (Queues/Presets Locking)
Given I am configuring a processing queue or preset within a tenant When I select a bucket from a tenant-scoped list Then only Active buckets from the same tenant are selectable; Archived buckets are hidden/disabled And the selection is saved with the queue/preset and used to attribute all resulting debits to the selected bucket And attempts to use a bucket from another tenant are rejected with 403, and attempts to save without a bucket return a validation error And UI displays current balance and currency next to the selected bucket
Prepaid Credit Funding & Top-ups
"As a finance manager, I want to fund buckets and see a clear ledger so that balances are accurate and auditable for client billing."
Description

Support adding funds to buckets via card, ACH/bank transfer (with reconciliation), and coupon/credit notes. Capture mandatory funding metadata (amount, currency, tax breakdown, funding source, PO number optional), create immutable ledger entries, and update bucket balance atomically with idempotent operations. Handle multi-currency with real-time FX rates and reporting currency normalization. Include validations (minimum top-up, limits), failure rollbacks, receipts, webhooks for finance systems, and permissions to restrict who can fund. Trigger low-balance alerts thresholds and optional auto-top-up rules where permitted.

Acceptance Criteria
Card Top-up with Mandatory Metadata and Atomic Balance Update
Given a user with buckets:fund permission and access to Bucket B And a valid card payment method is provided When the user submits a top-up request for amount A in currency C with tax_breakdown, funding_source=card, and optional po_number Then the request is validated and processed in a single atomic transaction And on success the bucket balance of B increases by the credited amount And exactly one immutable ledger entry is appended capturing bucket_id, amount, currency, tax_breakdown, funding_source, po_number (if provided), created_by, created_at, balance_before, balance_after, and a unique ledger_reference And the API responds 201 with ledger_reference, balance_before, and balance_after And if any payment step fails, no ledger entry is created, the balance remains unchanged, and the API responds with a non-2xx error And if the same request is retried with the same Idempotency-Key within 24 hours, the API returns 201 with the original ledger_reference and no additional charge or credit is made
ACH Top-up with Bank Reconciliation and Idempotency
Given an authorized user initiates an ACH top-up intent for Bucket B with amount A and currency C When the intent is created Then the API responds 201 with status=pending and no change to the bucket balance And a funding intent record is created with an external_reference and idempotency key When a bank reconciliation event is received with matching external_reference, settled amount A, and currency C Then the bucket balance increases by the credited amount And exactly one immutable ledger entry is appended with funding_source=ach and includes external_transaction_id And the reconciliation processing is idempotent so duplicate settlement events for the same external_transaction_id do not create additional credits or ledger entries And if the settlement amount or currency does not match the intent, the event is rejected and flagged for manual review with no balance change
Coupon/Credit Note Funding with Ledger Immutability
Given a valid coupon or credit note code with remaining value ≥ A and permitted for Bucket B When a user applies the code to fund Bucket B for amount A Then the bucket balance increases by A And the coupon or credit note balance decreases by A and is marked exhausted if remaining value is 0 And exactly one immutable ledger entry is appended with funding_source=coupon (or credit_note), capturing code, amount, currency, created_by, created_at, balance_before, balance_after, and a unique ledger_reference And subsequent attempts to modify or delete this ledger entry via API or UI are rejected with 403 and leave the entry unchanged And reapplying the same code with the same Idempotency-Key returns the original ledger_reference and does not create additional credits
Multi-Currency Top-up with FX Conversion and Reporting Normalization
Given Bucket B base currency is EUR and a user funds it with 100 USD by card When the top-up is processed Then the system retrieves the FX rate USD→EUR from the configured provider at authorization time And the credited amount in EUR is computed using the retrieved rate and standard rounding rules And the ledger entry stores source_amount=100, source_currency=USD, fx_rate, provider, fx_timestamp, credited_amount, credited_currency=EUR, and reporting_amount in the system reporting currency And Bucket B balance increases by the credited EUR amount And reporting endpoints aggregate top-ups using the stored reporting_amount so historical reports do not change if market FX rates update later And if the FX provider is unavailable, the top-up is rejected with 503 and no balance or ledger change And retries with the same Idempotency-Key return the same fx_rate and credited_amount as the original successful attempt
Validations, Limits, and Failure Rollback
Given a funding request for Bucket B When the amount is below the configured minimum, above the configured maximum, currency is unsupported, required tax_breakdown fields are missing, or funding_source is not allowed for B Then the API responds 422 with field-level error codes And no ledger entry is created and the bucket balance remains unchanged When concurrent funding requests are submitted against Bucket B Then exactly the successful requests each append one unique ledger entry And the final balance equals initial_balance plus the sum of credited amounts from successful requests And no duplicate credits are created due to race conditions
Receipts and Finance Webhooks
Given a successful top-up of Bucket B When the transaction completes Then a receipt is generated with ledger_reference, bucket_id, amount, currency, tax_breakdown, funding_source, po_number (if provided), created_by, and timestamps And the receipt is emailed to configured recipients and retrievable via API And a signed webhook event (type=funding.posted) is delivered to each configured finance endpoint within 10 seconds including the full funding metadata And webhook deliveries use at-least-once semantics with unique event_id, 3 retries with exponential backoff, and idempotency headers so receivers do not duplicate the entry And failures to send email or deliver webhooks do not roll back the successful top-up; undelivered events are retried and visible in delivery logs
Permissions, Low-Balance Alerts, and Auto-Top-up
Given a user without buckets:fund permission or without access to Bucket B When they attempt to fund Bucket B Then the API responds 403 and no ledger or balance change occurs Given Bucket B has a low-balance threshold T and auto-top-up disabled When the balance drops below T due to usage Then a low-balance alert is sent to the configured channels within 5 minutes and not more than once per 60 minutes per bucket Given Bucket B has auto-top-up enabled with permitted funding source, amount A, and cooldown C When the balance drops below T Then a top-up is automatically initiated for amount A, subject to the same validations, idempotency, ledger, receipt, and webhook rules as manual top-ups And concurrent triggers within cooldown C do not create duplicate top-ups And if permissions or policy prohibit auto-top-up for the configured source, no top-up occurs and an alert is logged
Queue and Preset Locking to Buckets
"As a project manager, I want to lock my team’s queues and presets to the right bucket so that usage is billed to the correct client without manual steps."
Description

Allow users to bind processing queues and editing presets to a specific bucket so that all jobs submitted through them automatically debit the correct balance. Enforce bucket selection at job submission with permission checks, default bucket per workspace, and API overrides when allowed. On insufficient funds, pause or reject jobs per policy with actionable messaging and links to top-up. Prevent cross-bucket mixing within a job batch, and record bucket ID on every job/item for traceability. Support bulk reassignment tools with safety checks.

Acceptance Criteria
Queue/Preset Bucket Binding Enforcement
Given a workspace admin binds Queue Q1 to Bucket B1 When any user submits a job via Q1 Then the bucket selector is disabled and the job is assigned to B1 only And when Preset P1 is bound to B1 and applied to a job in any queue Then the job inherits B1 unless the queue is bound to a different bucket, in which case the queue binding prevails And changing Q1/P1 binding affects only jobs created after the change; existing jobs retain their original bucket_id And only users with Manage Queues/Presets permission can create or change bindings; others receive a 403 in API and a disabled UI control
Bucket Selection at Submission (Unbound Sources)
Given Queue Q2 and Preset P2 are unbound and Workspace W has Default Bucket B2 When a user with access to B2 and B3 starts a job Then the bucket selector defaults to B2 and allows choosing among B2 and B3 only And if the user has access to no buckets Then the Submit action is disabled and an error explains "No accessible buckets" And if the user leaves the selector blank Then submission is blocked with a validation message And API submissions without bucket_id default to W's default bucket if caller has access; otherwise return 400 BUCKET_REQUIRED
Debit Correct Balance and Traceable Records
Given Bucket B1 has 1,000 credits and a job of 10 items costing 5 credits each is submitted via a source bound to B1 When items are processed Then B1 is debited 50 credits in total and the remaining balance reflects 950 within 5 seconds of completion And each job and item record stores bucket_id=B1, visible in UI detail and returned by API (job.bucket_id, item.bucket_id), and cannot be null And failed items do not debit credits; retried items debit only once And an audit event is written for each debit with job_id, item_id, amount, timestamp, and actor=system
Insufficient Funds Policy Handling
Given Bucket B1 has fewer credits than required for the submitted batch When policy is PauseOnInsufficientFunds Then the job enters Paused with reason=InsufficientFunds, displays a Top Up link to B1's billing page, and no items start until balance is sufficient and a user resumes And when policy is RejectOnInsufficientFunds Then submission fails with HTTP 402 and an in-app error, including bucket name, required credits, available credits, and a Top Up link And after top-up increases balance to meet the requirement Then paused jobs can be resumed successfully without re-queueing; the resume action is logged
Prevent Cross-Bucket Mixing Within a Batch
Given a user attempts to submit a single batch with sources that imply different buckets (e.g., queue bound to B1 and override B2) When the submission is validated Then the system blocks the submission with error BUCKET_CONFLICT and requires choosing a single bucket And when adding items to an existing batch Then all items must share the same bucket_id as the batch; otherwise the add action is rejected And bulk upload CSV/API that includes mixed bucket_ids within one batch is rejected with line-level errors referencing the conflicting rows
API Bucket Override with Permissions
Given an API client with scope bucket.override and access to Bucket B2 submits to Queue Q1 bound to B1 with override bucket_id=B2 When Q1 is configured to allow overrides Then the job uses B2 and an audit event records the override (from=B1,to=B2, client_id) And when Q1 disallows overrides Then the request is rejected with 403 BUCKET_OVERRIDE_FORBIDDEN And when the client lacks access to B2 Then the request is rejected with 403 BUCKET_ACCESS_DENIED And overrides cannot cross workspaces; if B2 belongs to a different workspace than Q1 Then the request is rejected with 400 BUCKET_WORKSPACE_MISMATCH
Bulk Reassignment with Safety Checks
Given an operator selects N jobs in statuses Queued or Paused and runs Bulk Reassign from Bucket B1 to B2 When the dry-run executes Then the system returns a summary of eligible jobs, ineligible jobs with reasons, and the estimated credit impact And when applying Then only eligible jobs are updated to bucket_id=B2; ineligible ones remain unchanged and are reported And reassignment requires the operator to have access to both buckets and Manage Billing permission; otherwise the action is blocked And reassignment is blocked for jobs that have started processing or completed And an audit log is recorded for each updated job with previous_bucket_id, new_bucket_id, user_id, timestamp
Burn Rate Tracking and Depletion Alerts
"As an account manager, I want burn-rate forecasts and alerts so that I can top up or adjust workloads before a client’s bucket runs out."
Description

Calculate per-bucket usage velocity (images/day and cost/day) from recent debits, forecast depletion dates, and display trends on a bucket dashboard. Provide configurable alert thresholds (percentage remaining, days remaining) and send notifications via email/Slack with batching and quiet hours. Store time-windowed metrics, exclude trial/promotional credits from forecasts where configured, and surface suggested top-up amounts. Include APIs to retrieve burn rate and forecast data for external dashboards.

Acceptance Criteria
Burn Rate Calculation (Images/Day, Cost/Day)
Given a bucket with non-excluded debit transactions in the last 7 full days and a defined bucket time zone When burn rate is computed Then images/day equals total images debited in the last 7 days divided by 7, and cost/day equals total monetary debits in the last 7 days divided by 7, both rounded to two decimals in the bucket currency Given refunds, credit grants, or adjustments exist When burn rate is computed Then only debit transactions that reduce balance are included in the calculation; credits/refunds are excluded Given less than 24 hours of debit history or zero debits in the last 7 days When burn rate is computed Then images/day and cost/day equal 0 and burnRateStatus is set to "insufficient_data" Given new debits post to the bucket When the computation job runs hourly Then the burn rate updates within 15 minutes and reflects transactions with createdAt less than or equal to the computation time
Depletion Forecast Date and Edge Cases
Given burn rate cost/day is greater than 0 and remaining paid credits are greater than 0 When forecasting depletion Then depletionDateTime equals now plus (remainingPaidCredits divided by costPerDay) in the bucket time zone and is displayed with date and time precision Given burn rate cost/day equals 0 When forecasting depletion Then daysRemaining is null and depletionDateTime is null Given remaining paid credits are less than or equal to 0 When forecasting depletion Then daysRemaining equals 0 and depletionDateTime equals now Given new debits change the burn rate or remaining paid credits When the forecast recalculates Then values update within 15 minutes and daysRemaining never returns a negative value
Bucket Dashboard Trends and Time-Windowed Metrics
Given the bucket dashboard is loaded When viewing trends Then users can switch time windows between 24h, 7d, and 30d and see charts for daily images processed and daily spend with per-point tooltips Given metrics are stored hourly When chart aggregations are computed Then daily totals are accurate within ±1 image and ±$0.01 and cover at least the last 30 days Given there are no debits in a period When rendering charts Then zeros are shown for those intervals without gaps Given the trends view is requested for the last 30 days When loading the dashboard Then p95 chart load time is under 2 seconds Given a bucket configuration excludes promo credits from forecasts When displaying any forecast overlay or summary on the dashboard Then values reflect paid credits only and the UI indicates promo-excluded forecasting
Alerts: Threshold Configuration, Triggering, Quiet Hours, and Batching
Given a bucket owner configures alert thresholds for percent remaining and days remaining and sets a bucket time zone with quiet hours (start and end) When thresholds are saved Then values are validated (0 < percent < 100, days >= 0) and persisted per bucket Given a configured threshold When remaining paid credits percent or computed daysRemaining crosses below that threshold for the first time Then a single alert event is created Given an alert was already sent for a threshold When the bucket balance rises above the threshold by at least 5% and later drops below again Then the alert becomes eligible to send again; otherwise no duplicate alerts are sent within 24 hours Given quiet hours are configured (e.g., 22:00–07:00 in the bucket time zone) When a threshold is crossed during quiet hours Then alert delivery is queued and sent at quietHoursEnd Given multiple alert events for the same recipient within a 30-minute window When delivering alerts via email or Slack Then events are batched into a single message listing each bucket and threshold crossed Given an alert is delivered When the recipient opens the message Then it includes bucket name, remaining paid credits, percent remaining, estimated days remaining if available, forecast depletion date/time, suggested top-up amount, and a deep link to the bucket
Exclude Promotional/Trial Credits from Forecasts (Configurable)
Given a bucket has both paid credits and promo/trial credits and the "Exclude promo/trial from forecasts" setting is enabled When computing the forecast Then remainingPaidCredits equals totalRemaining minus promoRemaining and only remainingPaidCredits is used for daysRemaining and depletionDateTime Given the exclusion setting is disabled When computing the forecast Then totalRemaining (paid plus promo) is used for daysRemaining and depletionDateTime Given the exclusion setting is toggled When the dashboard or API is refreshed Then burn rate values remain unchanged and forecast values update within 15 minutes, and the UI/API indicates whether promo credits are excluded
Suggested Top-Up Amounts
Given burn rate cost/day is computed and remainingPaidCredits is known When calculating the default suggestion Then suggestedTopUp30d equals max(0, roundUpCurrency(30 times costPerDay minus remainingPaidCredits)) in the bucket currency Given alternative horizons are requested When viewing suggestions Then suggestedTopUp7d and suggestedTopUp14d are computed using the same formula with horizons 7 and 14 Given remainingPaidCredits already cover the selected horizon When presenting suggestions Then the suggested amount equals 0 and the UI displays a Covered state Given suggestions are shown in alerts and on the dashboard When rendering values Then currency matches the bucket currency and amounts are rounded up to the nearest whole currency unit
Burn Rate and Forecast APIs for External Dashboards
Given an authenticated user with read access to a bucket When calling GET /api/buckets/{id}/burn-rate Then the response includes imagesPerDay, costPerDay, currency, windowDays, status, computedAt and returns 200 OK within p95 <= 500 ms Given the same user When calling GET /api/buckets/{id}/forecast Then the response includes remainingPaidCredits, percentRemaining, daysRemaining (nullable), depletionDateTime (nullable, ISO 8601), suggestedTopUp30d, promoExcluded (boolean) and returns 200 OK within p95 <= 500 ms Given an unauthorized or forbidden request When calling either endpoint Then 401 or 403 is returned with an error body and no sensitive data Given the bucket is configured to exclude promo/trial credits from forecasts When calling the forecast endpoint Then promoExcluded equals true and remainingPaidCredits excludes promo amounts; otherwise promoExcluded equals false Given API rate limiting of 60 requests per minute per token When the limit is exceeded Then the API returns 429 with a Retry-After header
Credit Expiry Rules and Grace Periods
"As an administrator, I want to set and manage credit expiries with reminders so that unused funds don’t linger indefinitely and clients are informed in advance."
Description

Support optional credit expiry per bucket and per funding lot, using FIFO consumption across lots. Expose configurable expiry dates, reminder cadence (e.g., 30/7/1 days before), and an optional grace period after expiry where jobs are allowed but flagged. Automatically mark expired lots as unavailable, adjust available balance, and log expirations in the ledger. Provide UI indicators for expiring credits and APIs to query remaining pre-expiry amounts. Allow enterprise overrides to extend or waive expiry with auditability.

Acceptance Criteria
FIFO Consumption Across Expiring Lots
Given a bucket with multiple funding lots [L1, L2, L3] with ascending creation timestamps, varying expiry dates, and non-zero balances When a job costing X credits is processed Then credits are deducted FIFO by lot creation timestamp from the earliest available lot, skipping lots marked expired/unavailable unless a grace period is active And if deduction spans multiple lots, separate ledger entries are created per lot, and their amounts sum exactly to X And no credits are deducted from lots that are expired without an active grace period And the bucket’s available balance equals the sum of remaining balances of all non-expired lots after consumption
Configurable Lot Expiry and Bucket Default
Given I create or edit a funding lot within a bucket When I set an explicit expiry date/time or leave it unspecified Then the lot stores expiry as an ISO 8601 UTC datetime or null if unspecified And if unspecified, the lot inherits the bucket’s default expiry policy value at creation time And validation prevents setting expiry in the past or earlier than the lot’s creation timestamp And changing the bucket default later does not retroactively alter lots that have an explicit expiry set
Expiry Reminder Cadence (30/7/1 Days)
Given a bucket with a reminder cadence configured for [30, 7, 1] days before expiry And a funded lot with an expiry date T and a non-zero remaining balance When the notification scheduler runs Then reminders are dispatched at T-30d, T-7d, and T-1d via the bucket’s configured channels And if the lot is created after T-30d, only the remaining future reminders are scheduled And reminders are suppressed if the lot balance reaches zero before the send time And each reminder emission is logged with lot_id, bucket_id, reminder_offset_days, and timestamp
Grace Period After Expiry With Flagged Jobs
Given a bucket with a grace period G (in days) enabled And a funding lot that expires at time T with remaining credits R > 0 When a job is submitted between T and T+G Then the job is allowed, credits are deducted from the expired lot, and the job and ledger entries are flagged "grace_period" And the lot remains marked expired but temporarily consumable during [T, T+G) And jobs submitted at or after T+G are blocked from consuming that lot and proceed to the next available lot per FIFO And UI and API surfaces display a warning badge for grace-period consumption
UI Indicators and API for Expiring Balances
Given a user views a bucket with one or more lots expiring within N days (configurable threshold) When the dashboard loads Then the UI shows an "Expiring" indicator with days remaining and per-lot remaining credits And a details view shows lot_id, expires_at, grace_until (if any), and remaining credits And the endpoint GET /buckets/{bucket_id}/pre-expiry-amounts returns JSON items with fields: lot_id, expires_at, grace_until, remaining_usable_before_expiry, remaining_total And the API supports query parameters window_days and include_grace and totals reconcile with ledger balances at query time
Enterprise Override: Extend/Waive Expiry With Audit Trail
Given an enterprise admin with override permission When they extend the expiry date or waive expiry for a specific lot Then the system requires a reason and reference (e.g., PO number), and records an immutable audit entry with actor_id, timestamp, before/after values, and IP And a ledger event EXPIRY_OVERRIDDEN is created and the new expiry/grace rules take effect immediately And pending reminders are recalculated/canceled and new reminders are scheduled per the updated expiry And UI/API display an override badge with who/when/why for the lot
Automatic Expiration and Ledger Logging
Given a funding lot with expiry timestamp T and a non-zero remaining balance When the expiration job runs at or after T Then the lot is marked expired and unavailable for consumption (unless grace is active) And a ledger event LOT_EXPIRED is recorded with lot_id, expired_amount (remaining balance), and occurred_at And the bucket’s available balance is reduced by the expired_amount And the process is idempotent; retries do not create duplicate events or further reduce balances And usage exports include an "expired_amount" and "expired_at" for the lot
PO Number and Billing Metadata Attachment
"As a finance ops user, I want to attach PO numbers and client codes to usage so that invoices reconcile cleanly with our clients’ procurement systems."
Description

Allow attachment of PO numbers, client codes, and campaign tags at the bucket level and per top-up. Validate formats, prevent edits after billing lock, and propagate metadata to ledger entries, invoices, and exports. Provide filters and search on metadata in the UI and API for reconciliation. Support CSV import of metadata for historical records and ensure all downstream reports include these fields for pass-through billing accuracy.

Acceptance Criteria
Bucket-level metadata attachment at creation
Given a user creates a new Client Bucket When they enter a valid PO number, client code, and campaign tags and click Save Then the metadata is persisted at the bucket level and visible in the bucket summary and details views And the same metadata is retrievable via the GET /buckets/{id} API response And an audit log entry records the creator, timestamp, and values set Given the bucket is created with metadata When a new top-up is later initiated without specifying metadata Then the top-up inherits the bucket-level metadata by default without mutating the bucket defaults
Top-up-level metadata attachment and override
Given a bucket exists with default metadata When a user performs a top-up and supplies a PO number, client code, and/or campaign tags Then the top-up record stores the provided metadata values And any field not supplied inherits from the bucket-level metadata And overriding a field on the top-up does not change the bucket-level defaults Given a top-up has stored metadata When retrieving ledger entries created by that top-up Then the ledger entries reference the top-up’s metadata values
Metadata format validation rules
Rule (PO number): Must match regex ^[A-Z0-9-]{3,32}$; no spaces; uppercase enforced; stored uppercase Rule (Client code): 2–24 characters; allowed [A-Za-z0-9-_]; stored uppercase; leading/trailing whitespace trimmed Rule (Campaign tags): 0–10 tags; each tag 1–32 chars; allowed [A-Za-z0-9-_]; duplicates removed; stored lowercase; order preserved as entered Given a user enters values violating any rule When they attempt to save Then the save is blocked with inline error messages per field and a 422 response via API Given values meet the rules When the user saves Then the save succeeds and the normalized values are persisted
Billing lock prevents metadata edits
Given a bucket or top-up is in a billing-locked state (e.g., included in a finalized invoice period) When a user attempts to edit PO number, client code, or campaign tags via UI or API Then the update is rejected with HTTP 409 and a clear error message indicating billing lock And no changes are written to storage And an audit log entry records the blocked attempt Given an unlocked bucket or top-up When a user edits metadata and saves valid values Then the update succeeds and is recorded in audit logs
Propagation to ledger, invoices, and exports
Given a top-up or bucket-level metadata exists When image processing jobs consume credits and ledger entries are created Then each ledger entry row includes PO number, client code, and campaign tags from the top-up if present, else from the bucket Given an invoice is generated for a period When the invoice PDF/HTML and line-item CSV are produced Then each line item includes PO number, client code, and campaign tags fields (empty if not set) And campaign tags are exported as a semicolon-delimited list in a single column Given a usage export is downloaded via UI or API Then the export includes columns: bucket_id, top_up_id, po_number, client_code, campaign_tags And the values match the source records exactly
UI and API filtering/search by metadata
Given the buckets and usage list views in the UI When a user filters by PO number (exact), client code (exact), or campaign tag (contains, case-insensitive) Then the results are narrowed accordingly and the filter chips reflect the active filters And the total count updates to reflect filtered results Given the API endpoints GET /buckets and GET /usage When called with query params poNumber, clientCode, campaignTag (repeatable) Then the response contains only records matching all supplied filters And responses include filter echo in metadata and are performant (p95 < 800ms for 10k records)
CSV import for historical metadata and report backfill
Given a CSV file with headers [bucket_id, top_up_id (optional), po_number, client_code, campaign_tags] When a user runs a dry-run import Then the system validates formats, detects billing-locked records, and returns a summary: total, valid, invalid, locked, duplicate-key rows with line numbers and reasons Given a production import is executed When rows are valid and not billing-locked Then metadata is upserted on the targeted bucket or top-up without altering unrelated fields And locked targets are skipped with errors; others proceed And a summary report is downloadable and an audit log entry is created Given the import completes successfully When the next invoice/export is generated Then imported metadata appears on all relevant ledger entries, invoices, and exports
Billable Usage Summary and Export
"As a billing analyst, I want accurate usage summaries I can export and schedule so that I can invoice clients quickly with defensible detail."
Description

Generate time-bounded, per-bucket usage summaries aggregating image counts, costs, taxes, and breakdowns by preset/queue/project. Provide on-demand and scheduled exports in CSV and XLSX, plus an API endpoint for automated retrieval. Include client metadata and PO numbers, apply timezone/UTC controls, and maintain a history of generated reports with checksums. Ensure figures reconcile to the underlying ledger and include explanatory footers and rounding rules suitable for agency pass-through billing.

Acceptance Criteria
On-Demand Per-Bucket Usage Summary Generation
Given an authenticated user with access to a specific client bucket and usage exists within a selected window When the user selects the bucket, specifies start and end datetimes, chooses a timezone, and requests a usage summary Then the system generates a summary scoped only to that bucket, filtering usage by the selected timezone with an inclusive start and exclusive end [start, end) And the summary includes window_start_local, window_end_local, window_start_utc, window_end_utc, timezone, currency, image_count, subtotal_cost, tax_amount, and total_cost fields And monetary fields are computed from underlying usage events before rounding is applied And the on-screen totals exactly match the totals of any file exported with the same parameters
CSV and XLSX Export Parity and Timezone Controls
Given a generated usage summary for a bucket and time window When the user exports the report as CSV and as XLSX Then both files contain the same rows, columns, and totals with identical numeric values and column order And CSV is UTF-8, RFC4180-compliant with quoted fields as needed and a single header row And XLSX contains a single worksheet named Usage Summary with the same header row And each file name includes bucket_id (or name), window_start_utc, window_end_utc, timezone, and format extension And all datetime fields in the export include both UTC and localized values per the selected timezone
Scheduled Exports with Recurrence and Windowing
Given a user creates a schedule for a bucket with recurrence (daily, weekly, monthly), run_time, timezone, and a relative window (e.g., previous day/week/month) When the schedule triggers at the configured run_time in the specified timezone Then a report is generated using the defined relative window with the same columns and totals as on-demand reports And the generated file is persisted to report history with a unique report_id and checksum And the schedule status is updated to Success or Failed with an error message if generation fails And pausing the schedule prevents further generations until resumed
API Endpoint for Automated Retrieval
Given an API client with valid authentication and permission to a bucket When the client requests GET /api/v1/reports/usage?bucket_id={id}&start={iso8601}&end={iso8601}&tz={IANA}&format={csv|xlsx} Then the API validates parameters and returns 200 with the report file stream and headers Content-Type, Content-Disposition, X-Report-Id, and X-Report-Checksum when synchronous generation is possible And if generation is asynchronous the API returns 202 with a job_id and the same parameters echoed; a subsequent GET to /api/v1/reports/{job_id} returns 200 with the file when ready And invalid or unauthorized parameters return 400/401/403 with a machine-readable error code and message And the data in the API-delivered file matches the UI export for identical parameters
Ledger Reconciliation, Rounding, and Explanatory Footers
Given a bucket’s underlying ledger entries for the selected window When a usage summary is generated Then the sum of line-level amounts in the report equals the sum of corresponding ledger fields for image_count, subtotal_cost, tax_amount, and total_cost (difference = 0.00) And monetary values are rounded half-up to 2 decimals at line level; totals are recomputed from rounded lines And if rounding introduces a non-zero delta at the grand total, a Rounding Adjustment line is included so reported totals equal ledger totals And the report footer states the tax basis, rounding rule, timezone boundary definition [start, end), and currency used
Breakdown by Preset, Queue, and Project
Given usage across multiple presets, queues, and projects within the selected window When a usage summary is generated Then the report includes grouped breakdown sections for preset, queue, and project with columns: id, name, image_count, subtotal_cost, tax_amount, total_cost And each group subtotal reconciles to the corresponding detailed rows and the grand totals reconcile to the ledger totals And groups with zero usage are omitted And groups are sorted by total_cost descending, with ties broken by name ascending
Report History, Checksums, and Reproducibility
Given reports have been generated for a bucket When a user views report history and filters by bucket and date range Then the system lists reports with report_id, created_at_utc, creator (or API), parameters (bucket_id, start, end, timezone, format), and checksum (SHA-256) And downloading any historical report returns the exact file originally generated And re-generating a report with identical parameters against an unchanged ledger yields the same checksum; if the ledger has changed, the new checksum differs and the UI marks the earlier report as superseded And history entries are immutable; deletion (if allowed) only removes the file but retains metadata and checksum for audit

Overrun Shield

Protect in-flight batches from stalling when a cap is hit. Choose to finish with a small grace allowance, auto-split the batch at the cap, or pause and notify the approver—keeping SLAs intact and avoiding half-processed deliverables.

Requirements

Real-Time Cap Detection & Grace Window
"As a workspace admin, I want in-flight batches to continue within a small, controlled grace window when caps are hit so that SLAs are met without manual intervention."
Description

Continuously monitor batch execution against workspace limits (credits, file count, runtime quotas) and trigger Overrun Shield when thresholds are reached. Provide a configurable grace allowance (e.g., up to N images or N% of batch) to finish in-flight work units without leaving partial images unprocessed. Ensure safe stop points, idempotent retries, and per-image atomicity so no file is double-billed or half-edited. Persist cap events, applied grace, and outcomes for reporting and downstream reconciliation.

Acceptance Criteria
Credit Cap Detected Mid-Batch with Grace Count Applied
Given a running batch with Overrun Shield enabled, a workspace credit cap of X, and a configured grace allowance of up to G images or P% of the batch And N images are currently in flight When cumulative credits consumed reach X Then Overrun Shield triggers within 2 seconds of the threshold crossing And completes all in-flight images and up to the configured grace allowance without starting any additional images beyond that allowance And sets batch status to CompletedWithGrace and records processedImageCount, graceImagesApplied, and totalCreditsConsumed And ensures no image is double-billed and no output is emitted for any uncompleted image And emits a CapEvent with capType=credits and outcome=graceApplied
Auto-Split Continuation Batch at Cap
Given Overrun Shield mode = Auto-Split and a batch is approaching any cap (credits, file count, or runtime) When the cap is reached Then the system completes only in-flight images and any configured grace allowance in the parent batch And creates exactly one child batch within 5 seconds containing all remaining unprocessed images with identical processing configuration And links parent and child via parentBatchId and childBatchId metadata And sets parent status to CompletedSplit and child status to Pending And guarantees idempotency such that retries or duplicate triggers do not create additional child batches
Pause and Notify at Cap (Approver Workflow)
Given Overrun Shield mode = Pause+Notify and notification channels (email and/or webhook) are configured for an approver When any cap is reached Then the system completes in-flight images only and transitions the batch to PausedAtCap within 2 seconds And sends notifications within 30 seconds containing batchId, capType, threshold, consumed, completed, remaining, mode, and next actions And resuming the batch processes only remaining images without reprocessing completed ones or double billing And the audit trail records the pause, notification dispatch, and resume events with timestamps
Per-Image Atomicity and Idempotent Retries During Cap Events
Given each image work item carries a stable idempotency key assigned at enqueue time When a cap event triggers and any image task is retried, duplicated, or a worker crashes and restarts Then exactly one final output artifact is stored per image and at most one billing record is created And duplicate submissions with the same idempotency key do not create additional outputs or charges And partial intermediate artifacts are cleaned up, leaving no half-edited files visible to users or APIs And the image state machine transitions are atomic (e.g., from Processing to Succeeded or Failed) with no dangling intermediate states
Persist Cap Events and Reconciliation Data
Given a cap is detected during batch execution When persisting event data Then the system writes a CapEvent with fields: eventId, batchId, workspaceId, capType, thresholdValue, triggerValue, triggerTime (UTC ISO 8601), graceType, graceConfigured, graceApplied, inFlightCompleted, outcome, parentBatchId, childBatchId (nullable) And each processed image is tagged with capContextId referencing the CapEvent And the Reporting API exposes CapEvents and aggregates by day, capType, and outcome within 60 seconds of event creation And CapEvent records are immutable; queries filtered by time range, batchId, and workspaceId return consistent, complete results And a reconciliation report can reproduce billed totals for the batch from CapEvents and image records without manual adjustments
Runtime Quota Cap with Safe Stop Points for Long-Running Edits
Given a runtime quota T is configured and the batch includes long-running edit operations When elapsed runtime reaches T Then Overrun Shield triggers within 2 seconds, allows completion of in-flight images only, and starts no new images And the runtime overrun beyond T is limited to finishing the in-flight set and is less than or equal to (P95 per-image processing time × number of in-flight images) And no image is left partially edited; completed images have terminal status and artifacts, and incomplete images have no artifacts or charges And a CapEvent is recorded with capType=runtime and outcome documenting the safe stop
Auto-Split Batch at Cap
"As a seller, I want my batch to auto-split at the cap so that I receive a clean, deliverable set now and the rest resumes after I top up."
Description

When a cap is reached and auto-split mode is enabled, finalize the completed portion as a closed sub-batch and create a new continuation batch for the remainder. Preserve processing settings, A/B variants, export presets, and marketplace mappings across both parts. Generate distinct identifiers, delivery artifacts, and notifications for the completed sub-batch while queuing the remainder for later execution once limits reset or credits are topped up.

Acceptance Criteria
Auto-split triggers at cap and creates sub- and continuation batches
Given a batch in progress with Auto-Split enabled and a configured processing cap And the batch has remaining unprocessed items when the cap is reached When the cap is reached Then the system finalizes all processed items into a closed sub-batch And creates a new continuation batch containing exactly the remaining unprocessed items And assigns distinct identifiers to the sub-batch and continuation batch And the item counts satisfy: sub-batch count + continuation batch count = original batch count And the original batch record references both resulting batches
Preserve settings and mappings across split
Given a source batch with defined processing settings, A/B variants, export presets, and marketplace mappings When the batch is auto-split at the cap Then those configurations are identically persisted on both the closed sub-batch and the continuation batch And settings parity check passes (checksum or version match across all entities) And no fields are missing or defaulted compared to the source batch
Delivery artifacts and notifications for completed sub-batch
Given the batch auto-splits When the sub-batch is finalized Then delivery artifacts (exports, download packages, webhooks) are generated only for the sub-batch items And recipients receive notifications that include sub-batch ID, item count, completion timestamp, and artifact links And the continuation batch emits no delivery artifacts and no completion notification until it finishes processing And the continuation batch status is Queued with reason "Cap Reached"
No reprocessing and order preservation across split
Given an ordered original batch When auto-split occurs Then the sub-batch contains only items completed prior to the cap and their results are immutable And the continuation batch contains only the remaining items in the original order And starting the continuation batch does not reprocess any items present in the sub-batch
Resume conditions for continuation batch
Given a continuation batch created due to cap When account limits reset or credits are topped up Then the continuation batch becomes eligible to start automatically if auto-start is enabled for the account And if auto-start is disabled, the batch remains queued and the approver is notified to start manually And upon start, preflight re-validates limits and proceeds only if sufficient capacity exists
Failure handling and audit log for split event
Given a split operation is initiated at cap When any step fails (e.g., creating the continuation batch, assigning identifiers, or sending notifications) Then the system retries the failed step up to 3 times with exponential backoff And if retries are exhausted, the original batch remains unmodified (no sub-batch closure), an error is surfaced to the approver, and support is alerted And an audit log records timestamp, cap value, counts, resulting IDs (if any), actor, and outcome
Mode exclusivity and cap behavior in Auto-Split
Given Overrun Shield is configured to Auto-Split mode When the cap is reached Then no grace overrun is applied and the system does not pause for approver confirmation And the split occurs immediately without exceeding the configured cap for billable units
Pause-and-Notify Approval Flow
"As an approver, I want to be notified and approve or decline overrun requests so that I control costs while avoiding delays."
Description

If pause mode is selected, halt the batch at the next safe boundary and notify designated approvers via email, in-app, and Slack/webhook with actionable options: approve temporary grace, purchase credits, change plan, reschedule, or cancel. Include auto-expiring requests with defaults on timeout, inline cost/ETA estimates, and a one-click resume path. Display a clear pause banner on the batch detail page and reflect state in the API.

Acceptance Criteria
Pause at Safe Boundary on Cap Hit
Given a workspace has Overrun Shield set to Pause mode and a batch with cap X is processing And some items are in-flight when the processed count reaches X When the cap is reached Then the system finishes only in-flight items and does not start new ones And the batch status changes to paused within 5 seconds of cap detection And no partial outputs are written for any item And the paused_at_count equals X + in_flight_completed And processing workers release the batch lease within 5 seconds
Actionable Multi-Channel Notifications to Approvers
Given the batch transitions to paused due to cap_reached When notifications are dispatched Then email, in-app, and Slack/webhook notifications are sent to designated approvers within 30 seconds And each notification includes batch_id, batch_name, pause_reason, remaining_items, expiry_at, options [approve_temporary_grace, purchase_credits, change_plan, reschedule, cancel], and per-option deep links And cost and ETA estimates are displayed per option And the webhook payload matches schema version v1 and is signed with HMAC-SHA256 with a timestamp header And failed deliveries are retried up to 5 times with exponential backoff to a maximum delay of 10 minutes
Auto-Expiring Requests with Default Action
Given an approval request with expiry_at T and default_action configured from [keep_paused, resume_with_grace, cancel, reschedule, auto_split] When current_time >= T and no approver action has been recorded Then the default_action executes within 60 seconds And the resulting batch state reflects the action taken And a timeout notification is sent to approvers summarizing the applied default And the audit log records default_action, actor=system, and correlation_id
One-Click Resume via Secure Deep Links
Given an approver receives a signed deep link for a specific action with parameters When the approver clicks the link within its validity window and the token is valid Then the action executes and the batch state updates accordingly within 10 seconds And the approver sees a confirmation screen with updated cost and ETA And if the token is invalid or expired, the approver is prompted to authenticate and re-confirm And duplicate link activations are idempotent, resulting in a single state transition
Cost and ETA Estimates Accuracy and Availability
Given remaining_items R, plan P, credit_balance C, and current queue load Q When options are presented in notifications and in-app UI Then each option displays an incremental cost estimate and ETA And the absolute percentage error of cost estimates versus actual charge is <= 10% for at least 95% of batches over a rolling 30-day window And the absolute percentage error of ETA versus actual completion time is <= 15% for at least 90% of batches over a rolling 30-day window And a "How calculated" affordance reveals the assumptions used
Pause Banner and API State Reflection
Given a paused batch detail is viewed in the app or queried via API When the page loads or the GET /batches/{id} endpoint is called Then the UI shows a prominent "Paused — Approval Required" banner with reason cap_reached and an expiry countdown And action buttons for [Approve Grace, Purchase Credits, Change Plan, Reschedule, Cancel] are visible and permission-gated And the API response returns status=paused, pause_reason=cap_reached, action_required=true, expiry_at, options[], and counts {processed, remaining, in_flight} And status enumerations include [queued, processing, paused, completed, cancelled, failed] per the published schema
Authorization, Routing, and Escalation for Approvals
Given designated approvers and fallback routing rules exist for the workspace When a batch pauses for approval Then only approvers can execute actions; non-approvers see read-only state and disabled action controls And if no approvers are configured or reachable, the request routes to the workspace owner and escalates to an admin after 30 minutes with a distinct escalation notification And unauthorized attempts to act return HTTP 403 and are recorded in the audit log with actor identity and IP And all approval actions are attributed, timestamped, and include option, parameters, and outcome
SLA-Aware Policy Decision Engine
"As a product ops lead, I want Overrun Shield to choose the best action based on SLA and plan rules so that we optimize delivery speed and cost automatically."
Description

Implement a rules engine that selects between grace, auto-split, or pause based on SLA targets, due-by timestamps, customer tier, batch priority, queue load, and remaining credits. Support workspace-level defaults and per-batch overrides, with simulation/testing mode to preview decisions before execution. Ensure deterministic, auditable decisions and guardrails (e.g., maximum grace per day/month).

Acceptance Criteria
Deterministic Policy Selection & Override Precedence
Given a workspace default policy and a per-batch override When the engine evaluates a batch Then the per-batch override takes precedence over the workspace default and the precedence is recorded in the decision trace. Given identical inputs (SLA target, due-by with timezone, customer tier, batch priority, queue load snapshot, remaining credits, and rule version) When the engine evaluates twice Then it returns the same selected action and reason codes deterministically. Given a due-by timestamp in any timezone When the engine evaluates Then it normalizes to UTC for all comparisons and logging. Given queue load or remaining credits change between evaluations When the engine re-evaluates Then the decision may change and the audit explains which inputs changed and how they affected the outcome. Given some optional inputs are missing When the engine evaluates Then documented defaults are applied and explicitly listed in the decision trace.
Grace Application with Daily/Monthly Guardrails
Given the selected action is Grace and daily/monthly grace quotas remain for the workspace When the batch reaches the cap Then the engine authorizes overrun up to the lesser of the per-decision limit and remaining quota, processes the overrun, and atomically decrements the quota counters. Given grace quotas are exhausted When the rules would otherwise select Grace Then Grace is disqualified, the next eligible action per policy (Auto-Split or Pause) is selected, and reason "guardrail_blocked" is logged. Given multiple concurrent batches request Grace When the engine processes them Then quota counters are updated atomically so total granted overrun does not exceed daily or monthly maxima and no double-counting occurs.
Auto-Split Execution and Linking
Given the selected action is Auto-Split When the batch reaches the cap Then items up to the cap are completed, a child batch is created for remaining items, and parent/child are linked with persistent IDs. Given the child batch is created When metadata is assigned Then it inherits SLA target, due-by, customer tier, and batch priority; due-by is recalculated if required by policy but never earlier than the original due-by. Given item processing is idempotent When Auto-Split occurs Then no item is processed twice, an idempotency key prevents duplication, and credits are only consumed for items actually processed. Given the split occurs When auditing Then both parent and child decisions are recorded with cross-references to maintain traceability.
Pause-and-Notify with SLA-Aware Escalation
Given the selected action is Pause When the batch reaches the cap Then processing pauses and a notification is sent to the configured approver within the configured notification SLA (e.g., <= 60s) including batch ID, items remaining, SLA impact estimate, and actionable options (approve Grace up to allowable max, request Auto-Split, keep paused). Given the approver responds via any supported channel When an action is selected Then the engine executes the selected action subject to guardrails and logs approver identity and decision details. Given no approver response by the escalation threshold or before SLA risk exceeds the configured limit When the threshold is reached Then the engine executes the configured fallback action and logs reason "no_response_escalation". Given notification delivery failures occur When retries are attempted Then the system performs the configured number of retry attempts per channel before escalation and logs all attempts and outcomes.
Simulation Mode Parity and No Side Effects
Given simulation mode is enabled When a decision is evaluated Then no side effects occur (no queue mutations, no quota/credit changes, no notifications) and the response includes predicted action, reason codes, and projected metrics (e.g., overrun amount, split size). Given identical inputs and rule version When comparing simulation and execution results Then the selected action and reason codes match exactly. Given a user supplies hypothetical overrides for inputs (e.g., queue load, remaining credits, priority) When simulation runs Then those overrides are used only for the simulation and are labeled as simulated in the decision trace.
Auditable, Reproducible Decisions
Given any decision evaluation completes When the audit record is written Then it contains UTC timestamp, inputs snapshot (SLA target, due-by, customer tier, priority, queue load metrics, remaining credits, overrides), evaluated rule path and order, policy/rule version hash, selected action, reason codes, outputs (grace amount, split details), side effects (notifications, batches), and actor metadata, and is retrievable via API and UI. Given two decisions have identical inputs and rule version When their audit records are compared Then their normalized content hashes match (excluding timestamps and unique IDs), demonstrating reproducibility. Given an attempt is made to alter an existing audit record When the system processes the request Then the original record remains immutable; corrections are appended as new linked records without overwriting the original.
Grace Usage Accounting & Billing
"As a finance admin, I want all grace usage itemized and capped so that overages are predictable and billable."
Description

Track all grace consumption (units, time, and cost) at workspace and batch levels. Deduct against future credits or bill at defined overage rates with itemized invoice lines and capped monthly limits. Expose usage via API and UI, including per-batch summaries and downloadable statements, and reconcile with event logs to ensure accuracy and dispute resolution.

Acceptance Criteria
Workspace-Level Grace Usage Aggregation & Cost Calculation
Given a workspace with defined overage rates per unit and per processing-minute and a starting credits balance, When multiple batches consume grace units and time within a billing period, Then the workspace usage summary displays total_units, total_minutes, gross_cost, credits_applied, and net_cost that equal the sum of all batch-level records and calculations using the configured rates, rounded to 2 decimals. Given credits >= gross_cost, When the period closes, Then credits_applied equals gross_cost, net_cost equals 0.00, and the remaining credits balance is decremented by gross_cost. Given credits < gross_cost, When the period closes, Then credits_applied equals the remaining credits balance, and the remainder is billed as overage at the configured rates. Given a billing period timezone setting, When summaries are generated, Then all timestamps and period boundaries respect that timezone.
Batch-Level Grace Capture During Overrun Shield Events
Given an in-flight batch triggers Overrun Shield and processes items beyond the configured cap, When Finish with Grace is selected, Then the batch summary records grace_units equal to the number of items processed beyond the cap, grace_minutes equal to incremental processing time, the applied rates, and computed grace_cost, and marks the batch with overrun_mode=finish. Given Overrun Shield auto-splits at cap, When the split occurs, Then the original batch shows no grace consumption and a new spillover batch is created with its own cap context; If grace is consumed in the spillover batch, it is attributed to that batch's summary. Given Overrun Shield pauses at cap, When an approver resumes with grace enabled, Then only the resumed segment's over-cap processing is counted as grace and appears in the batch summary with approver_id and approval_timestamp. Then per-batch summaries are immutable after invoice issuance and include references to event_log_ids for each grace item.
Itemized Invoice Lines for Overage and Credit Deduction
Given the end of a billing period or a manual invoice generation trigger, When an invoice is created, Then the invoice includes itemized lines that show for each batch: batch_id, period, grace_units, grace_minutes, unit_rate, minute_rate, line_amount, and a link to the batch summary. Then credits applied appear as separate negative lines referencing the same period, with amounts matching credits_applied, and the invoice total equals sum(all overage line_amounts) - sum(all credits lines). Given a monthly overage cap, When the invoice subtotal would exceed the cap, Then overage charges are limited to the cap and any excess grace appears as a $0.00 line item labeled "Unbilled due to cap" with quantities preserved. Then the invoice can be downloaded as PDF and CSV, and values are rounded to 2 decimals with a consistent currency code.
Monthly Overage Cap Enforcement & Threshold Notifications
Given a workspace monthly overage cap amount is configured, When cumulative overage for the current month reaches 80% of the cap, Then the system sends a notification to the workspace approver(s) and records the event in the audit log. When cumulative overage reaches 100% of the cap, Then additional grace consumption for the month is recorded as "unbilled" and does not increase the billed amount, and approvers are notified within 5 minutes. Then the usage API and UI reflect both billed and unbilled grace separately for the month, with totals matching the cap enforcement state.
Usage Exposure via API, UI, and Downloadable Statements
Given the usage API endpoint /v1/usage/grace with filters (workspace_id, date_range, batch_id, group_by), When called with valid credentials, Then it returns totals and line items for units, minutes, gross_cost, credits_applied, and net_cost, paginated, within 2 seconds for up to 10,000 records. Given the UI Usage page, When filtered by billing period and batch, Then it displays per-batch summaries with totals and offers a "Download Statement" action that exports CSV and JSON matching the API schema. Then all totals in the UI and exported files equal the API values for the same filters.
Reconciliation Against Event Logs for Accuracy & Disputes
Given an event log that records per-item processing with event_id, timestamp, batch_id, and overrun_flag, When a reconciliation job runs for a period, Then 100% of billed grace units and minutes map to event_log entries with matching batch_id and overrun_flag, or the job emits a discrepancy report listing unmatched items. Then reconciliation outputs a signed report with checksum and a unique reconciliation_id, and stores it for retrieval via API and UI download. Given a dispute is opened on a billed batch, When reconciliation is re-run, Then the system can produce an adjustment transaction (credit or debit) that appears on the next invoice with a reference to reconciliation_id and the disputed line.
Configuration UI & API Controls
"As a workspace admin, I want to configure Overrun Shield defaults and per-batch overrides so that the system aligns with our risk tolerance and workflows."
Description

Provide settings pages and REST endpoints to configure Overrun Shield defaults (mode priority, grace limits, notification channels, auto-split naming/export presets) and per-batch overrides at creation time. Enforce role-based permissions for who can change policies or approve overruns. Surface inline tooltips and validation to prevent conflicting configurations.

Acceptance Criteria
Settings UI: Configure Overrun Shield Defaults
Given a user opens Settings > Overrun Shield When the page loads Then the form displays editable fields for modePriority (ordered list), graceLimit (value and unit), notificationChannels, and autoSplit (namingTemplate, exportPreset) and a Save action And constraints (min/max, required fields) are loaded from the backend and applied client-side When the user updates valid values and clicks Save Then the client sends a single update request and receives 200 OK And a success toast is shown And a subsequent hard refresh shows the persisted values exactly as saved
REST API: Read/Update Overrun Shield Settings
Given a client with token that has settings.read When it calls GET /v1/settings/overrun-shield Then it receives 200 OK with the current settings and constraints (including allowedModes, min/max for graceLimit, required fields for selected modes) and an ETag header Given a client with token that has settings.write When it calls PUT /v1/settings/overrun-shield with a valid payload and If-Match of the last ETag Then it receives 200 OK with the updated resource and a new ETag And a follow-up GET returns the same values When PUT is called without settings.write Then 403 Forbidden is returned When the payload violates constraints Then 422 Unprocessable Entity is returned with machine-readable error codes When If-Match does not match the current ETag Then 412 Precondition Failed is returned
Per-Batch Overrides at Creation (UI & API)
Given a user with overrun.override permission creates a new batch in the UI or via POST /v1/batches with overrunShieldOverride specified When the override values satisfy server-provided constraints and guardrails Then the batch is created 201 Created and its details show the effective Overrun Shield configuration derived from override + defaults And processing uses the override values for this batch only When override is omitted Then defaults are applied When a user without overrun.override includes overrunShieldOverride in the request Then the UI blocks submission and the API returns 403 Forbidden When override violates constraints (e.g., out of range, missing required fields based on selected modes) Then the UI shows inline errors and the API returns 422 with field-specific codes
RBAC: Manage Policies and Approve Overruns
Given permissions are defined as settings.read, settings.write (alias policy.manage), overrun.override, and overrun.approve When a user lacks settings.read Then the Overrun Shield settings page and GET endpoint are inaccessible (UI hidden, API 403) When a user has settings.read but not settings.write Then fields render read-only and Save is disabled; API PUT returns 403 When a batch hits a cap and requires approval to proceed (pauseNotify in effect) Then only users with overrun.approve see the Approve Overrun action and can approve/deny; others do not see the action and API returns 403 on attempt
Notification Channels Configuration and Test
Given pauseNotify is in modePriority When the user attempts to Save with zero notification channels configured Then Save is blocked with a clear inline message When the user adds a valid email (RFC 5322) and/or HTTPS webhook URL Then field validation passes When the user clicks Send Test on a configured channel Then a test message is dispatched and the UI shows success/failure state based on a 2xx/4xx-5xx response And failing webhooks show the returned status code and reason
Auto-Split Naming and Export Presets Validation
Given autoSplit is present in modePriority When the user enters a namingTemplate Then it must include tokens {batch} and {part}; otherwise an inline error prevents Save and shows a live preview example once valid When the user selects an exportPreset from the provided list Then Save is allowed When an actual overrun auto-split occurs during processing Then resulting child batches/files are named according to the template and use the chosen export preset
Inline Tooltips and Conflict Prevention Validation
Given the settings form is displayed Then each Overrun Shield field has an inline tooltip explaining its purpose and examples, accessible via hover/focus and screen-reader labels When the user configures modePriority with duplicate or unknown modes Then Save is blocked and an inline error lists allowed unique values only: grace, autoSplit, pauseNotify When grace is in modePriority and graceLimit.value is 0 Then Save is blocked with guidance to either remove grace from priority or set a positive limit When pauseNotify is in modePriority and no notification channels are configured Then Save is blocked with an inline error and a link to add channels When autoSplit is in modePriority and namingTemplate or exportPreset is missing Then Save is blocked with specific inline errors When all conflicts are resolved Then the Save action becomes enabled
Observability & Audit Logging
"As a support engineer, I want traceable logs of Overrun Shield decisions so that I can resolve disputes and debug issues quickly."
Description

Emit structured events for cap detection, policy evaluation inputs/outputs, chosen action, notifications sent, approvals received, and final outcomes with correlation IDs per batch. Provide dashboards for overrun frequency, grace spend, and SLA impact, plus alerts on anomalous usage. Enable export of logs as CSV/JSON and retain records per compliance policy.

Acceptance Criteria
Cap Detection Event Emission & Correlation
Given a batch exceeds a configured cap during processing When the cap is detected Then a cap_detected event is emitted within 200 ms and persisted with fields: event_name, event_version, timestamp (ISO-8601 UTC), batch_id, correlation_id, tenant_id, cap_type, cap_value, observed_value, policy_id, request_id, environment, severity And the same correlation_id is reused for all subsequent overrun-related events for that batch until completion And the event is queryable via the Observability API and UI within 5 seconds of emission And the event payload validates against the published JSON schema
Policy Evaluation Inputs/Outputs Audit Event
Given a cap_detected event exists for a batch When Overrun Shield evaluates the applicable policy Then a policy_evaluated event is emitted capturing: policy_id, policy_version, inputs {cap_type, cap_value, observed_value, batch_stats, sla_target_ms, tenant_overrides}, outputs {decision in [finish_with_grace, auto_split, pause_and_notify], rationale_code, grace_units_requested, split_size, pause_reason}, evaluation_duration_ms And the policy_evaluated event shares the batch_id and correlation_id of the cap_detected event And the event is emitted before any action_selected event and within 300 ms of evaluation start And the event payload validates against the published JSON schema
Action Selection & Outcome Event Chain Integrity
Given a policy_evaluated event exists for a batch When an action is chosen and executed Then an action_selected event is emitted with fields: action_type, parameters, expected_sla_impact_ms, grace_units_consumed, split_batch_ids (if any) And when overrun handling completes, an overrun_outcome event is emitted with fields: outcome_status in [completed_with_grace, split_completed, paused_awaiting_approval, cancelled], total_processing_ms, final_sla_met (boolean) And all events in the chain [cap_detected -> policy_evaluated -> action_selected -> overrun_outcome] use the same correlation_id and are linked via previous_event_id And event timestamps are strictly increasing and each event is queryable within 5 seconds of emission And in failure cases, an error event with error_code, error_message, and retry_count is emitted and linked to the chain
Notification Sent & Approval Received Audit
Given action_selected = pause_and_notify for a batch When notifications are dispatched Then a notification_sent event is emitted per recipient with fields: channel in [email, webhook, in_app], recipient_identifier, template_id, delivery_status in [queued, sent, failed], attempt, provider_message_id And upon approver decision, an approval_received event is emitted with fields: approver_id, decision in [approved, rejected, expired], approval_latency_ms, approval_source And both events share the batch_id and correlation_id and are queryable within 5 seconds And notification failures emit notification_failed events including retry_schedule and last_error_code
Operational Dashboards: Overrun Frequency, Grace Spend, SLA Impact
Given production events are ingested When a user opens the Overrun Shield dashboard Then widgets are present with tenant and time-range filters: Overrun frequency by cap_type, Grace spend units over time, SLA impact (ms) distribution, Decision mix by action_type And dashboard aggregates for a selected 24h window match recomputed values from raw events within ±1% for counts and ±50 ms for SLA metrics And data freshness is < 2 minutes for streaming updates and < 10 minutes for scheduled recomputations And users can drill down from any widget to the filtered event list by correlation_id, batch_id, or tenant_id
Anomalous Usage Alerts
Given alert thresholds are configured (static or baseline-based) When overrun frequency or grace spend exceeds the configured threshold (e.g., > 3× 7-day moving average or above an absolute value) Then an alert is generated within 60 seconds and delivered to configured channels [email, Slack/webhook, PagerDuty] And alerts include: metric_name, current_value, threshold_value, lookback_window, top contributing tenants/policies, up to 5 sample correlation_ids And duplicate alerts for the same condition are suppressed for the configured dedup interval, and an alert_resolved notification is sent once the metric drops below threshold for one lookback window And all alert notifications are logged as alert_event and reference impacted batches when applicable
Log Export & Retention Compliance
Given an authorized user scopes an export by tenant, time range, and event types When an export is requested Then the system produces downloadable files in CSV and NDJSON within 5 minutes for up to 10,000,000 events, with pagination for larger sets And exported files conform to the public schema, include headers (CSV) or one JSON object per line (NDJSON), are UTF-8, and escape delimiters correctly And retention policies are configurable per tenant and event type with TTL in days (min 30, max 1825), and events older than TTL are purged daily with a purge_run event recorded (job_id, counts_deleted, time_range) And exports respect retention and legal hold flags; when legal_hold = true for a tenant, purges are skipped and the state is auditable And delete-by-batch (batch_id) requests remove matching events within 24 hours and emit a deletion_event linked to a deletion_request_id

Invoice Split

Create itemized, client-billable invoices from one shared credit pool. Split charges by workspace, bucket, or tag; add tax IDs, currencies, and memo fields; and auto-send on schedule—cutting hours of manual re-billing and reconciliation.

Requirements

Client Profiles & Billing Entities
"As a finance admin, I want to maintain client profiles with tax and currency details so that invoices are accurate, compliant, and routed to the right recipients."
Description

Manage client records with legal name, billing address, tax IDs (VAT/GST/EIN), default currency, payment terms, and invoice recipients. Support mapping of workspaces, buckets, and tags to a client for default cost attribution. Include custom memo fields and purchase order number capture. Validate required fields and prevent invoice issuance if mandatory tax or identity data is missing. Provide CRUD via UI and API, with uniqueness checks and soft deletion to preserve historical invoices. Store change history for auditability.

Acceptance Criteria
Create Client via UI with Required Fields and Validation
Given I am on the New Client form When I enter legal name, billing address (street, city, region/state, postal code, country), default currency (ISO 4217), payment terms (Net N), and at least one invoice recipient email Then the Save action is enabled And When I submit with all required fields valid Then the client record is created and appears in the client list and is retrievable by ID And Email addresses must be syntactically valid; invalid addresses block save with a field-level error And Currency must be one of the supported ISO 4217 codes; invalid codes block save And Payment terms must be a supported option or a numeric Net N where 0 ≤ N ≤ 365; out-of-range values block save And If a tax ID is entered, it must pass format validation for the selected country; invalid formats block save And If no tax ID is provided, the client is created with invoicing_blocked = true
API CRUD and Soft Deletion Preserves Historical Invoices
Given a well-formed POST /api/clients payload with required fields When submitted Then the API responds 201 Created with client_id and persisted fields When PATCH /api/clients/{id} updates mutable fields Then the API responds 200 OK and changes are persisted with an audit entry recorded When DELETE /api/clients/{id} is invoked Then the record is soft-deleted (deleted_at set), API responds 204 No Content, and the client is excluded from default lists and selectors And Historical invoices that reference the client remain readable and unchanged When GET /api/clients?include_deleted=true is called Then the soft-deleted client appears with deleted_at not null When PATCH /api/clients/{id} to restore (clear deleted_at) Then the client becomes selectable again under the same ID
Prevent Invoice Issuance When Mandatory Tax/Identity Data Missing
Given a client missing mandatory tax or identity data for its billing country When a user attempts to generate or schedule an invoice for that client via the UI Then the action is blocked with a descriptive validation error When attempting via API POST /api/invoices with that client_id Then the API responds 422 Unprocessable Entity with error code CLIENT_TAX_INFO_REQUIRED When the missing data is added and saved on the client Then both manual issuance and scheduled auto-send succeed without additional changes And The block applies uniformly to draft creation, finalization, and sending operations
Map Workspaces, Buckets, and Tags to Client for Default Cost Attribution
Given a client has default mappings configured for one or more workspaces, buckets, or tags When new usage records or charges are ingested with those identifiers Then the system attributes those costs to the client by default When generating an invoice for the client Then only mapped charges are included by default And Users can override inclusion/exclusion of specific charges at invoice time without altering saved mappings And Attempting to map the same workspace, bucket, or tag to multiple clients in the same tenant is rejected with a validation error And Mapping changes apply prospectively; previously issued invoices and historical attributions remain unchanged
Uniqueness Constraints and Duplicate Detection
Given an attempt to create or update a client with a client_code matching any existing (active or soft-deleted) client in the tenant Then the operation is rejected with 409 Conflict and a client_code_unique violation And A combination of tax_id and tax_jurisdiction must be unique within the tenant; duplicates are rejected with 409 Conflict and a tax_id_unique violation And In the UI, when legal name and billing address closely match an existing client, a duplicate warning is displayed with a link to the existing record; saving still enforces the uniqueness rules above And The API returns a machine-readable errors[] array specifying field, code, and message for each uniqueness violation
Change History and Audit Trail
Given any create, update, mapping change, or soft delete on a client When the operation completes Then an audit event is recorded with actor, timestamp (UTC), action, and field-level before/after values And Audit events are immutable and viewable via UI and GET /api/clients/{id}/audit in reverse chronological order And Changes to sensitive fields (e.g., tax IDs) are stored securely but masked in audit views based on role permissions And Audit export to CSV includes event_id, actor_id, action, fields_changed, old_value(masked when applicable), new_value, and timestamp
Custom Memo Fields and Purchase Order Capture on Invoices
Given a client has custom memo field definitions (key, type: text|number|date, required flag) and PO number capture enabled When an invoice is created for that client via UI or API Then values are required for all memo fields marked required and validated against their types And If a required memo field or PO number is missing or invalid (e.g., fails ^[A-Za-z0-9-_\/]{3,30}$) Then invoice creation is blocked with inline errors in UI or 422 Unprocessable Entity in API with field-specific error codes When the invoice is successfully created Then memo fields and PO number persist on the invoice record and appear on generated PDFs and CSV exports And Changes to memo field definitions are versioned and captured in the audit log; new definitions apply only to future invoices
Shared Credit Pool Attribution Rules
"As an operations manager, I want to define how usage is attributed to clients from one credit pool so that charges are split correctly without manual spreadsheets."
Description

Configurable rules engine that allocates usage charges from a shared credit pool to billable clients based on workspace, bucket, tag, or explicit client override. Support rule priority, conditions, and fallbacks when no match exists. Allow fixed percentage splits, per-item attribution, and caps. Provide retroactive re-rating for unbilled usage within a configurable window and lock charges after invoice issuance. Expose attribution previews before finalizing, and persist an audit trail of rule evaluations per line item.

Acceptance Criteria
Priority-Based Rule Matching with Fallback
Given a usage line item with workspace=W1, bucket=B1, tags=[T1,T2], and an explicit client override=CX And rules are configured with priorities where a lower number indicates higher priority and a default fallback exists When the rules engine evaluates the item Then the explicit client override is applied and the item is attributed 100% to client CX And if no explicit override is present, the highest-priority matching rule among workspace, bucket, and tag is applied And if no rules match, the default fallback rule is applied And the chosen applied_rule_id and applied_rule_priority are recorded in the item’s audit trail
Fixed-Percentage Splits and Deterministic Rounding
Given a $10.00 charge and a rule that splits 60% to client A and 40% to client B When attribution is calculated Then client A receives $6.00 and client B receives $4.00 and allocations sum to $10.00 with a tolerance of $0.00 Given a $0.03 charge and a rule that splits 50% to client A and 50% to client B When attribution is calculated with 2-decimal currency rounding Then allocations are deterministic and sum to $0.03, with the remainder cent assigned to the first split in rule order And the remainder allocation decision is recorded in the audit trail for the item
Per-Item Evaluation Within Batch
Given a batch containing 3 usage items where one matches a tag rule for client A and two match a bucket rule for client B When the engine processes the batch Then each item is evaluated independently and attributed according to the rule it matches And no item inherits attribution from another item in the batch And the batch-level totals equal the sum of all per-item allocations
Client Caps with Overflow Handling
Given rule R1 assigns 100% to client A with a monthly cap of $100 and client A has $95 already attributed this month And a fallback rule Rf assigns 100% to client B When a $10.00 item is processed Then $5.00 is attributed to client A (up to the remaining cap) and $5.00 overflow is attributed by the next eligible rule (client B via Rf) And the fields cap_applied=true, cap_remaining=0.00, overflow_attributed_to=Rf are recorded in the audit
Retroactive Re-Rating Within Window
Given window_days=30 and unbilled usage items exist from the past 25 days And a new higher-priority rule R2 is created that changes attribution outcomes When re-rating is requested Then only unbilled items within 30 days are re-evaluated under current rules And items older than 30 days or already invoiced are excluded And a preview shows per-client deltas and net effect by currency before apply And on apply, allocations and audits are updated atomically with a single re-rating batch id
Lock After Invoice Issuance
Given usage items included on invoice INV-123 are marked locked=true with lock_reason="Invoiced" and lock_timestamp When rules change or re-rating is requested for the same period Then locked items are not modified and are excluded from re-rating calculations And the preview marks them as locked and immutable And any attempt to override the lock is rejected with HTTP 409 and an audit entry is appended with action="lock_override_rejected"
Attribution Preview and Audit Trail
Given a draft attribution preview is requested for period P When the engine evaluates rules Then the response includes for each line item: item_id, original_amount, currency, attributed_amounts by client (client_id and amount), applied_rule_id, match_dimension (override|workspace|bucket|tag|fallback), rule_priority, split_percentages, cap_applied (true|false), remainder_recipient, evaluation_timestamp And a persistent audit record is written per item with the same metadata and re_rating_batch_id (if applicable) And audit records are immutable and retrievable by item_id, client_id, period, and rule_id filters
Usage Tagging & Enforcement
"As a studio lead, I want to enforce usage tags at the time of upload so that downstream invoices reflect the right client and project without rework."
Description

Enable tagging of uploads and processing jobs with client, project, and cost-center metadata via UI, CSV import, and API. Support required/optional tags, default inheritance from workspace or template, and validation rules that can block processing or route to an “unassigned” bucket when metadata is missing. Ensure tags propagate to invoice line items and exports unchanged. Provide bulk retagging tools and history to correct misattributions with traceability.

Acceptance Criteria
UI Tagging with Required Fields and Blocking
Given a workspace where tags client and project are required and cost-center is optional And a user uploads images via the UI without the project tag When the user attempts to start processing Then the system blocks processing and displays an inline error listing the missing required tags And the Start button remains disabled until all required tags are provided And no credits are consumed and no jobs enter the queue And an audit log entry is recorded with user, timestamp, and reason "missing_required_tags" And upon adding the missing tags, the user can start processing and the job is accepted
CSV Import Tag Validation and Unassigned Routing
Given a CSV import of 100 rows where 5 rows are missing the client tag And the workspace rule is set to route jobs with missing required tags to the Unassigned bucket When the CSV is imported Then 95 rows are accepted with their provided tags and 5 rows are routed to the Unassigned bucket And a post-import summary report shows counts by bucket and fields missing per row And routed rows are marked as Unassigned in job listings and are still fully processed And invoice previews show 5 line items under the Unassigned grouping with accurate costs
API Job Submission with Default Inheritance and Overrides
Given workspace defaults client=Acme and cost-center=CC-12 And a processing template sets project=Summer24 When an API job request includes project=Fall25 and omits client and cost-center Then the job inherits client=Acme and cost-center=CC-12 from the workspace And project=Fall25 overrides the template value And the effective tag set is persisted and returned unchanged via the job metadata endpoint And values preserve exact casing and Unicode characters And the API responds 201 with the effective tags in the response body
Tag Propagation to Invoice Line Items and Exports
Given completed jobs tagged client=Acme, project=Fall25, cost-center=CC-12 And an invoice run is generated grouped by client and project When the invoice is exported to CSV and JSON Then each line item includes the exact tag key/value pairs unchanged from the jobs And grouping, counts, and totals match underlying job usage and credits consumed And tag values up to 128 characters are not truncated And UTF-8 characters are preserved byte-for-byte in both export formats And the exported digest/hash matches the server-side checksum for integrity
Bulk Retagging with Audit History and Reconciliation
Given an admin filters 50 jobs dated 2025-09-01..2025-09-15 with client=Acem (typo) When the admin applies a bulk retag to client=Acme with reason "typo correction" Then all 50 jobs update atomically to client=Acme or none do if an error occurs And an immutable audit record is created per job capturing old value, new value, user, timestamp, and reason And subsequent invoice previews and exports reflect the corrected tag values without altering pricing or credits And the admin can perform a one-click rollback to the prior tag values using the audit record And the audit trail is queryable and exportable for the affected jobs
Tag Schema Configuration and Validation Rules
Given a workspace tag schema where - client is required with allowed values [Acme, BetaMart] - project is optional string with max length 64 - cost-center is required matching regex ^CC-[0-9]{2,4}$ When a user sets client=Gamma via UI or API Then validation fails with a clear error listing invalid values and constraints And processing is blocked until the schema is satisfied And API responses return HTTP 400 with a machine-readable error code and field details And schema changes apply only to new jobs; existing jobs remain unaffected and are not retroactively invalidated
Performance and Concurrency for Tag Validation at Scale
Given 10,000 API job submissions with valid tags arrive within 5 minutes When the system validates and enqueues these jobs Then P95 tag validation latency is <= 20 ms per job and overall throughput is >= 2,000 jobs/min And no deadlocks or lost updates occur under concurrent tag writes And validation error rate alert triggers at > 1% over a 5-minute window and emits metrics to monitoring And the system maintains data consistency for tags across job, invoice, and export records
Tax & Multi-Currency Support
"As a bookkeeper, I want invoices to apply correct taxes and currencies so that accounting and compliance are straightforward across regions."
Description

Generate invoices with per-client currency and localized formatting, using daily FX rates from a trusted provider and storing rate source and timestamp. Support VAT/GST registration capture and validation where available, zero-rating rules for reverse charge, and configurable tax rates by jurisdiction. Apply rounding rules per currency and display net, tax, and gross totals. Allow tax-exempt flags and multi-jurisdiction addresses, and include tax ID fields on PDFs and exports.

Acceptance Criteria
Per-Client Currency and Localized Formatting on Invoices
Given a client profile with currency EUR and locale de-DE When an invoice is generated for that client Then all monetary amounts display in EUR with de-DE number formatting (comma decimal, period thousand) And the currency code "EUR" is displayed in the totals summary and line items And the invoice date and numeric formats follow de-DE locale rules And no other currencies appear on the PDF or CSV export
FX Rate Sourcing, Timestamping, and Application
Given the base ledger currency is USD and the client invoice currency is EUR And a daily FX rate from the configured FX provider is available for the invoice issuance timestamp When the invoice is generated Then the conversion uses the rate snapshot effective at the invoice issuance timestamp And the FX provider name, rate value, and UTC timestamp are stored with the invoice and shown in invoice metadata And the same rate is used consistently across line items, taxes, and totals And if the FX provider is unavailable, the last successful rate within the past 24 hours is used and flagged as "cached"; if none exists, invoice generation fails with a clear error message
VAT/GST ID Capture, Normalization, and Validation
Given a client enters a VAT ID "DE123456789" with country DE (Germany) When the client profile is saved Then the VAT ID is validated via the configured service (e.g., VIES) and normalized (uppercase, strip spaces) And the validation status (valid/invalid/unavailable) and timestamp are stored with the client And if invalid, EU zero-rating cannot be applied and the UI shows an actionable error message And if the validation service is unavailable, the ID is stored as "unverified" and a background retry is scheduled And GST-style tax IDs (e.g., AU ABN) are validated via their regional service with the same status storage
Reverse Charge Zero-Rating for Eligible Cross-Border B2B
Given the seller is VAT-registered in FR and the buyer has a validated VAT ID in DE And the sale is configured as a B2B digital service eligible for reverse charge When the invoice is generated Then VAT rate applied is 0% with reason "Reverse charge" And both the seller and buyer VAT IDs are shown on the PDF And the PDF includes a reverse charge note per EU guidance And if the buyer VAT ID is invalid or missing, the appropriate VAT rate for the buyer's jurisdiction is applied instead
Jurisdiction Selection with Multi-Jurisdiction Addresses
Given a client has a billing address in Ontario, CA and a shipping address in New York, US And the tax engine is configured to prioritize shipping address for tax determination When the invoice is generated Then the jurisdiction is determined as New York, US And the configured New York tax rate(s) are applied And the invoice tax summary labels the jurisdiction used And if an item is tagged to a different jurisdiction bucket, that item's tax follows its bucket jurisdiction and the tax breakdown shows per-jurisdiction subtotals
Currency-Specific Rounding and Totals Integrity
Given the client currency is JPY with zero decimal places and rounding mode "half up" When line items, taxes, and totals are calculated Then amounts are rounded per currency rules at the correct stages (line, tax, total) And the sum of rounded line items plus rounded taxes equals the displayed gross total And no fractional subunits appear in PDF/CSV And for currencies with two decimals (e.g., USD), amounts show exactly two decimal places
Tax-Exempt Clients and Document Outputs
Given a client is marked tax-exempt with exemption ID "TX-EXEMPT-001" When an invoice is generated Then no tax is applied regardless of jurisdiction And the PDF and CSV include the exemption note and ID And seller and buyer tax ID fields are included on PDF and exports when present And totals clearly show net equals gross with tax = 0.00
Itemized Invoice Generation & Templates
"As an agency owner, I want branded, itemized invoices I can review and approve so that clients clearly see what they’re paying for."
Description

Create draft invoices that itemize usage by client with grouping options (by workspace, bucket, tag, or day) and show quantity, unit price, discounts, and memos. Provide customizable templates for branding, fields, and numbering sequences. Support edit, approve, and finalize states with immutable PDFs and CSV line-item exports upon finalization. Include per-line attachments or references to job batches and a calculated summary section with subtotals and totals.

Acceptance Criteria
Draft Invoice Creation with Grouping Options
Given a billing admin selects a client and a date range with usage present And chooses a grouping option of workspace, bucket, tag, or day When they click Create Draft Then a draft invoice is created scoped to the client and date range And line items are grouped by the selected option with correct group labels And each group's quantity and amount are aggregated from underlying usage And groups with zero quantity are excluded And the invoice state is Draft
Line-Item Fields, Editing, and Calculations
Given a draft invoice exists When the user views or edits a line item Then the line shows quantity, unit price, discount, and memo fields And editing quantity, unit price, or discount immediately recalculates line total = quantity × unit price − discount, rounded to 2 decimals And invoice subtotal and total equal the sum of line totals And validation prevents saving a line item with missing quantity or unit price
Template Customization for Branding and Numbering
Given a user creates or edits an invoice template When they set logo, brand color, field visibility toggles, footer text, and numbering sequence format (prefix, counter start, padding) Then the template saves successfully and shows a live preview And numbering sequences are unique per client and per template And upon finalization of an invoice using the template, the next sequential invoice number is assigned and is immutable And the selected template can be applied when creating a draft invoice
Workflow States and Immutable Exports
Given an invoice is in Draft When the user approves it Then the status changes to Approved and line items and pricing become read-only When the user finalizes it Then the status changes to Finalized And a PDF and a CSV line-item export are generated, stored, and downloadable from the invoice And after finalization no fields (including lines, totals, numbering, or template) can be edited
Per-line Attachments and Job Batch References
Given a draft invoice with line items mapped to job batches When the user attaches a file or links a job batch to a line item Then the attachment metadata and batch reference are saved to the line item And on finalization, the PDF displays batch IDs and the CSV includes columns for batch_id and attachment_file_name (if present) And attachments remain accessible from the finalized invoice detail view And only job batches belonging to the client can be linked
Summary Section with Subtotals and Totals
Given an invoice with grouped line items When the user opens the summary section Then subtotals per group and the grand total are displayed And values equal the sum of the visible line totals And values update in real time while in Draft and are fixed once Finalized
Scheduled Invoicing & Auto-Delivery
"As a finance admin, I want invoices to be generated and emailed on a schedule so that I don’t spend hours on manual rebilling every period."
Description

Allow recurring schedules (weekly, biweekly, monthly, custom date ranges) with cut-off times and time zones per client. Automatically compile eligible charges, generate invoices, and send via email to configured recipients with optional CC/BCC and portal links. Support retries on bounced emails, delivery logs, and failure notifications. Provide calendar previews and the ability to pause, skip, or rerun a cycle without double-billing.

Acceptance Criteria
Configure Schedule with Frequency, Cutoff, and Time Zone
Given an admin configures a client schedule with frequency (weekly, biweekly, monthly, or custom date range), a cutoff time, and a time zone When the schedule is saved Then the next run timestamp is calculated in the specified time zone And the billing window start and end are derived from the frequency and time zone And charges with timestamps strictly before the cutoff are included; charges at or after the cutoff are deferred to the next cycle And daylight-saving transitions do not shift the intended local run time
Auto-Compile Eligible Charges and Generate Invoices
Given charges exist that match the schedule's filters and fall within the computed billing window And some charges have already been invoiced in prior cycles When the schedule runs Then only non-invoiced, in-window charges are included And a single invoice is generated per client per schedule run unless configured otherwise And the invoice line items and grand total equal the sum of included charges And the invoice period shown matches the billing window
Email Delivery with To/CC/BCC and Portal Link
Given recipients are configured for To, CC, and optional BCC, and portal link delivery is enabled When an invoice is generated by the schedule Then an email is sent from the configured sender to the To recipients with CC/BCC included And the email includes the invoice PDF attachment and a portal link that resolves to the invoice view without requiring login And delivery status is recorded as Sent when the provider accepts the message
Retry on Delivery Failure and Notify on Final Failure
Given the email provider returns a bounce or a transient failure for a scheduled invoice email When the first send attempt fails Then the system retries automatically up to 3 times with exponential backoff And each attempt is logged with timestamp and provider response And after the final failed attempt, a failure notification is sent to the schedule owners and appears in the activity feed And the invoice delivery status is marked Failed
Delivery Logs and Audit Trail
Given one or more scheduled invoice emails have been attempted When a user opens the delivery log for a schedule or invoice Then they see per-attempt entries including timestamp, recipients, status (Queued, Sent, Deferred, Bounced, Failed), provider message ID, and error reason if any And logs can be filtered by date range, status, and recipient And logs are retained for at least 180 days
Calendar Preview with Upcoming Runs and Controls
Given a schedule exists When a user opens the calendar preview Then the next 6 scheduled runs are displayed in the client's time zone with their expected run timestamps and billing windows And past runs for the last 90 days are visible with their delivery status And the user can pause the schedule or skip the next occurrence from the calendar, and the change updates the schedule state immediately
Pause, Skip, and Rerun Without Double-Billing
Given a schedule is paused When its next nominal run time occurs Then no invoice is generated or emailed Given a user chooses Skip next occurrence When the skipped run time passes Then no invoice is generated for that occurrence and the subsequent run advances as expected Given a user triggers Rerun for a specific past cycle When the rerun executes Then the system regenerates invoices using the original cycle's billing window and only charges that were unbilled at that time And any charge already billed in that cycle or later is not billed again And rerun deliveries are logged distinctly from the original attempt
Accounting Exports & Webhooks
"As a controller, I want structured exports and webhooks for invoices so that our accounting stack stays in sync without manual data entry."
Description

Provide real-time webhooks for invoice.created, invoice.finalized, invoice.sent, and invoice.paid events, including line-item payloads and attribution metadata. Offer CSV/JSON exports compatible with common accounting systems and a QuickBooks/Xero field mapping preset for easy import. Include secure API endpoints to fetch invoices, line items, and attachments. Track payment status updates via manual input or API to aid reconciliation, and push status changes to integrated systems.

Acceptance Criteria
Real-time Webhook Delivery for Invoice Lifecycle Events
Given a verified webhook endpoint is registered for a workspace When an invoice is created, finalized, sent, or paid in that workspace Then a POST is delivered to the endpoint within 5 seconds of the event time And the payload matches schema v1 and includes: event_type, event_id, created_at, invoice{id, status, totals, currency, tax_ids, customer, memo}, line_items[{id, description, unit_price, quantity, subtotal, tax, total, attribution{workspace_id, bucket_id, tag_ids, user_id}}] And headers include X-PixelLift-Event-Id, X-PixelLift-Signature, and Idempotency-Key And a 2xx response marks the delivery successful; non-2xx triggers exponential retries for up to 24 hours (max 15 attempts) And failed deliveries are logged with timestamp, response code, and error body and are visible in the webhook delivery log And in a test burst of 100 events, P95 delivery latency is <= 3 seconds and success rate >= 99.5% (excluding intentionally failing endpoints)
Webhook Signature Verification and Idempotency
Given a webhook signing secret is configured for the workspace When PixelLift sends a webhook Then the X-PixelLift-Signature header contains t=<unix timestamp> and v1=<HMAC-SHA256 signature> And the signature validates using HMAC-SHA256 over the documented base string with the signing secret And the timestamp is within ±60 seconds of PixelLift server time at send And all retries for the same event reuse the same event_id and Idempotency-Key And no two distinct events share the same event_id or Idempotency-Key
Accounting Exports with QuickBooks/Xero Mapping Presets
Given a user selects Export type CSV or JSON and chooses the QuickBooks or Xero preset When exporting invoices for a specified date range and workspace with Include line items enabled Then the exported file includes all required fields per preset (e.g., Customer, InvoiceNo, InvoiceDate, DueDate, Currency, ExchangeRate, TaxID, Memo, LineItem, Description, Qty, Rate, Amount, TaxCode, TaxAmount, Workspace, Bucket, Tags) And CSV is UTF-8 encoded, comma-delimited, with RFC 4180 quoting; JSON is UTF-8 and schema-documented And multi-currency invoices include Currency and ExchangeRate; tax fields populate per item and totals And importing the generated file into QuickBooks Online and Xero test companies completes without manual mapping or validation errors for a sample of 50 invoices containing taxes and multi-currency And export completes within 60 seconds for 5,000 invoices and streams results to avoid timeouts
Authenticated API Endpoints for Invoices, Line Items, and Attachments
Given an API token scoped to workspace A with invoices:read permission When GET /v1/invoices?status=sent&from=YYYY-MM-DD&to=YYYY-MM-DD&page[size]=100 is called Then response is 200 with up to 100 invoices from workspace A only and includes pagination cursors (next, prev) And GET /v1/invoices/{id}/line_items returns the invoice’s line items and 404 for non-existent or cross-workspace IDs And GET /v1/invoices/{id}/attachments returns time-limited signed URLs (expiry 15 minutes) with correct content types And missing/invalid tokens return 401; insufficient scope or cross-workspace access returns 403; invalid parameters return 422 And endpoints enforce rate limiting of 60 requests/min/token and return 429 with Retry-After when exceeded And ETag/If-None-Match is supported on invoice resources and returns 304 when unchanged
Payment Status Updates via UI and API with Audit Trail
Given an invoice in sent status with amount_due > 0 When a user marks it paid in the UI with a payment_reference and paid_amount equal to amount_due Then invoice status becomes paid, amount_paid equals total, balance_due becomes 0, and an audit log entry is created with actor, timestamp, source, and before/after values And when a partial payment is applied (paid_amount < amount_due) the status becomes partial, amounts reconcile correctly, and remaining balance is updated And PATCH /v1/invoices/{id} with {payment_status, paid_amount, payment_reference} applies the same rules and returns 200 with the updated resource And invalid transitions (e.g., paid -> draft) or inconsistent amounts are rejected with 422 and error details And on status changes to paid, configured webhook endpoints receive an invoice.paid event within 5 seconds
Invoice Status Push to Integrated Systems with Retry
Given an external integration target is configured for a workspace with a valid outbound credential When an invoice transitions to finalized, sent, or paid Then PixelLift pushes the mapped invoice payload to the integration within 10 seconds of the change And non-2xx responses are retried with exponential backoff for up to 24 hours; 4xx (except 429) are not retried And each push includes a de-duplication key equal to the webhook event_id; duplicates are not reprocessed by the target when the key is reused And integration outbox logs display last attempt, next retry, attempt count, and final disposition (delivered/failed)
Attribution Metadata Completeness on Line Items
Given invoices and line items originate from a shared credit pool with Invoice Split by workspace, bucket, or tag When line items are delivered via webhooks, API, or exports Then each line item includes attribution fields: workspace_id, workspace_name, bucket_id (nullable), bucket_name (nullable), tag_ids[], tag_names[], split_method, split_percent, split_amount And attribution values are consistent across API, webhooks, and exports for the same invoice And missing values are represented as null or empty arrays rather than omitted, preserving schema stability And for each invoice, the sum of line_item totals equals the invoice total within 0.01 of the invoice currency

Forecast Alerts

Get predictive alerts on days-to-empty and projected overages based on recent usage velocity. Receive smart recommendations for top-up sizes, see a heatmap of high-burn teams/marketplaces, and trigger pre-approvals—so credits never run out mid-launch.

Requirements

Usage Velocity Engine
"As a billing admin, I want accurate, up-to-date usage velocity metrics so that forecasts and alerts reflect real consumption trends and I can plan top-ups confidently."
Description

Service to calculate rolling credit consumption rates per workspace, team, and marketplace from event-level usage data. Aggregates and smooths (e.g., EWMA) across multiple windows (7/14/30 days), normalizes by day-of-week to reduce volatility, and flags anomalies using z-score thresholds. Exposes a versioned internal API returning velocity metrics and confidence bands, updates near real-time (≤5 minutes lag), and backfills on data reprocessing. Integrates with the existing billing/usage pipeline, is idempotent and horizontally scalable, and persists metrics for querying by forecasting, dashboards, and alert modules. Includes monitoring, retries, and PII-safe logging.

Acceptance Criteria
Compute Rolling Velocities by Scope and Window
Given event-level usage records are available for a workspace containing multiple teams and marketplaces When the engine processes the latest data Then it computes consumption velocities for 7, 14, and 30-day windows at each scope (workspace, team, marketplace) And applies EWMA smoothing to each window And returns both normalized-by-day-of-week and non-normalized velocities And each metric includes: scope identifiers, window, calculation_time, data_start, data_end, and sample_size
Near Real-Time Updates Within 5 Minutes
Given new usage events are ingested at time T When a client queries velocity for the affected scope Then the metrics reflect those events within 5 minutes (p95) of T And the response includes observed_lag_seconds and last_updated timestamps And if metrics are stale beyond 5 minutes, the response marks stale=true
Versioned API and Persistent Store for Downstream Access
Given an authorized GET request to /internal/velocity/v1 with valid scope and window parameters When the request is processed Then the API responds 200 with JSON including velocity, confidence_low, confidence_high, window, scope identifiers, last_updated And the API supports explicit version selection (e.g., path or header) and rejects unsupported versions with a 404/invalid_version error And invalid parameters return 400 with a machine-readable error code And the same metrics are persisted and queryable by forecasting, dashboards, and alert modules via the internal data store with a stable, versioned schema
Anomaly Flagging via Z-Score
Given velocity is computed for a scope and window When the absolute z-score of the latest normalized velocity exceeds the configured threshold (default 3.0) Then the engine flags anomaly=true, includes z_score and direction (high|low), and persists an anomaly record with timestamp and scope And the threshold is overrideable via configuration per scope/window And anomalies are retrievable via the internal API and data store
Idempotent Backfill on Reprocessing
Given historical usage events are reprocessed for a previously computed time range When the engine performs a backfill Then duplicate events are not double-counted and metrics equal a clean recomputation over the canonical event set And metrics versions increment, with prior versions retrievable by specifying version in the API And cache invalidation and replacement occur atomically so no query returns mixed-version data
Horizontal Scalability Under Load
Given ingestion throughput increases to 10x baseline for 30 minutes When the engine autoscaling is triggered Then p95 processing lag remains ≤ 5 minutes and error rate ≤ 0.1% And no data loss occurs; transient failures are retried with exponential backoff and dead-letter capture upon max attempts And additional workers can be added without configuration changes and without impacting correctness
PII-Safe Logging and Operational Monitoring
Given normal operation and transient/permanent failures When logs and metrics are emitted Then logs contain no PII (e.g., no emails, names, raw image/file metadata) and only hashed/opaque identifiers And retries use exponential backoff with a configurable max_attempts (default 3), with outcomes observable in monitoring dashboards And alerts are generated for SLA breaches, elevated error rates, and dead-letter events, with messages free of PII
Days-to-Empty Forecasting
"As an ops manager, I want days-to-empty predictions so that I can proactively schedule purchases and avoid credits running out during launches."
Description

Model to predict time remaining until current credit balance reaches zero per scope (account, team, marketplace) using velocity inputs, seasonality adjustments, and configurable assumptions (e.g., growth factor). Computes estimated depletion timestamps, days remaining, and three confidence bands. Supports fallback logic for sparse data, handles balance changes (top-ups) mid-horizon, and exposes threshold configurations (e.g., 7/3/1 days) to trigger alerts. Provides an API returning structured forecast objects with metadata and explanations. Recomputes hourly and on balance-change events.

Acceptance Criteria
Per-Scope Forecast Outputs
Given credit balances and usage histories exist for at least one account, team, and marketplace scope When the forecast job runs Then for each scope the response includes scope.type, scope.id, estimated_depletion_at (ISO 8601 UTC), and days_remaining (float with 2 decimals) And days_remaining >= 0 and equals (estimated_depletion_at - now) in days within ±0.05 days tolerance And scope identifiers in the response match the requested scopes
Velocity, Seasonality, and Configurable Assumptions
Given usage data spanning at least the configured history window and a configuration with growth_factor=g and seasonality.enabled=true When the forecast runs Then metadata.assumptions includes growth_factor=g, seasonality.enabled=true, and window settings used And the response includes effective_velocity, baseline_velocity, and seasonality_weight_applied per scope And increasing growth_factor while holding data constant strictly decreases days_remaining; decreasing it strictly increases days_remaining
Confidence Bands Generation
Given sufficient historical variability for a scope When the forecast completes Then bands include pessimistic, likely, and optimistic each with fields days_remaining and estimated_depletion_at And pessimistic.days_remaining <= likely.days_remaining <= optimistic.days_remaining for the same scope And metadata.bands.percentiles are present (e.g., p15/p50/p85 or configured values) And if historical variability is zero, all three bands return identical values
Sparse Data Fallback and Flags
Given a scope with fewer than the configured min_history_days or min_usage_events When the forecast runs Then metadata.fallback.used=true and metadata.fallback.method is one of the configured methods (e.g., global_default, peer_average, last_nonzero_day) And metadata.fallback.reason and metadata.data_window_used are populated And the forecast returns valid bands and depletion fields without error And (optimistic.days_remaining - pessimistic.days_remaining) >= configured min_fallback_band_width
Recomputation Cadence and Balance-Change Triggers
Given no balance-change events for a scope When scheduled recomputation runs Then metadata.last_recomputed_at updates at least every 60 minutes and no more than 65 minutes apart for active scopes Given a top-up or manual balance adjustment event for a scope When the event is processed Then a new forecast for that scope is available via API within 5 minutes, reflects the new balance, and does not double-count prior consumption And metadata.balance_change_events includes the triggering event_id and applied_at timestamp
Threshold Configuration and Alert Triggering
Given alert thresholds configured at 7, 3, and 1 days on the likely band (overridable per scope) When a scope’s likely.days_remaining crosses from above to at or below a configured threshold Then exactly one alert event is emitted per threshold per scope with payload including scope_id, threshold_days, previous_days_remaining, current_days_remaining, and forecast_id And subsequent upward crossings do not emit duplicates until a new downward crossing occurs (hysteresis) And the forecast metadata includes the effective thresholds used for that scope
API Response Contract and Explanations
Given a GET /forecasts?scope=... request with valid authentication When the server responds Then status=200 and Content-Type=application/json And each item matches the contract: forecast_id, scope{type,id}, balance{current,unit}, effective_velocity, estimated_depletion_at, days_remaining, bands{pessimistic,likely,optimistic}, assumptions{growth_factor,seasonality,window}, thresholds, metadata{model_version,last_recomputed_at,data_window,sparse_data_fallback_used,balance_change_events[],explanations[]} And explanations[] contains at least velocity_source, seasonality_adjustments, and recent_balance_changes entries per scope And invalid or unsupported scope parameters return 400 with a structured error object and no partial 200 responses
Overages Projection & Smart Top-ups
"As a finance approver, I want data-backed top-up recommendations so that I can approve the right amount with minimal waste and risk of overage."
Description

Computation of projected overage risk and generation of recommended top-up sizes that minimize waste while covering forecasted demand over configurable horizons (e.g., next 14/30 days). Considers plan rules, volume discounts, minimum increments, and budget caps. Produces multiple recommendation tiers (conservative, balanced, aggressive) with estimated runway, cost, and risk. Provides rationale and enables one-click creation of a pre-approval request. Integrates with billing APIs to validate pricing and with the approvals workflow to streamline execution.

Acceptance Criteria
14-Day Overage Projection Calculation
- Given tenant timezone TZ, current credit balance C at t0, and selected horizon = 14 days - When the projection is requested - Then the system outputs: daysToEmpty (timestamp in TZ, hour precision), projectedOverageRiskPercent (integer 0–100), and projectedShortfallCredits (>= 0) - And projectedShortfallCredits = 0 implies projectedOverageRiskPercent = 0 - And the calculation completes within 5 seconds for accounts with <= 1M usage events in the last 30 days
30-Day Tiered Top-up Recommendations Under Constraints
- Given selected horizon = 30 days, plan minIncrement M, budgetCap B, and the account’s volume discount schedule - When recommendations are generated - Then exactly three tiers are returned: conservative, balanced, aggressive - And each tier includes: topUpCredits (multiple of M), estimatedRunwayDays (integer), estimatedNetCost (currency), residualRiskPercent (0–100) - And no tier proposes a top-up that exceeds B - And unit pricing and discounts applied match the discount schedule within 0.01 currency units - And if no tier can be produced within constraints, the system displays "No viable recommendation within constraints"
Billing API Price Validation and Resilience
- Given valid billing API credentials for the tenant - When generating recommendations - Then the system retrieves and validates unit price and applicable discount tiers from the billing API - And pricing responses are cached for up to 15 minutes per account-plan key - And if the billing API fails or times out, recommendations are shown with "pricing unavailable" and the pre-approval action is disabled - And the system retries up to 2 times with exponential backoff and logs the failure with a correlation ID
One-Click Pre-Approval Request Creation from Recommendation
- Given a displayed recommendation tier - When the user clicks "Request pre-approval" - Then a pre-approval request is created with fields: tierName, topUpCredits, estimatedNetCost, horizon, rationaleSummary, requesterId, and recommendationSnapshotId - And the request status is set to "Pending Approval" and is visible in the approvals queue within 5 seconds - And an idempotency key prevents duplicate requests for the same tier by the same user within 10 minutes - And the UI displays a confirmation with the request ID and a link to the approvals item
Configurable Forecast Horizon Selection
- Given available horizons of 14 and 30 days - When the user switches the horizon - Then projections, risks, and recommendations recalculate using the selected horizon - And all displayed metrics (days-to-empty, shortfall, tier runways, costs, risks) update consistently within 3 seconds - And the selected horizon persists per user session and defaults to 14 days if unset
Rationale Transparency and Runway Estimates Display
- Given any displayed recommendation tier - When the user expands "Why this recommendation?" - Then the system shows inputs and assumptions: selected horizon, current balance, velocity method name/window, applied discount tier, min increment M, budget cap B, and rounding rules - And the rationale shows formulas for runway and cost with actual numbers substituted - And all user-visible numeric values in the rationale match the tier’s displayed metrics within rounding rules
Zero or Low Usage Edge Case Handling
- Given forecasted cumulative demand over the selected horizon <= current balance - When projections are computed - Then projectedOverageRiskPercent = 0 and projectedShortfallCredits = 0 - And no top-up tiers are recommended and the UI shows "No top-up needed for the selected horizon" - And pre-approval actions are disabled in this state
Multi-Channel Alerts & Preferences
"As a team lead, I want to receive alerts in my preferred channel with the right frequency so that I can act promptly without alert fatigue."
Description

Delivery of predictive alerts via in-app notifications, email, Slack, and webhooks when thresholds for days-to-empty or projected overages are met. Supports per-scope subscriptions, role-based recipients, and granular controls for thresholds, digest vs. real-time delivery, snooze, and mute. Implements deduplication, cooldown windows, and severity levels to reduce noise. Includes templated messages with key metrics and quick actions (view forecast, create pre-approval). Tracks delivery status and failures with retries and allows admin configuration of org-wide defaults. Ensures GDPR-compliant opt-outs and is localization-ready.

Acceptance Criteria
Threshold-Based Multi-Channel Alert Dispatch
Given an org with scopes (Org X, Team A, Marketplace M) and per-scope subscriptions configured for in-app, email, Slack, and webhook, and thresholds set to daysToEmpty <= 7 or projectedOverage >= 15% When the forecast engine computes daysToEmpty = 5 and projectedOverage = 0% for Team A Then one alert is dispatched within 60 seconds to all subscribed channels for Team A, addressed only to eligible role-based recipients for that scope, and includes scope name, severity=Warning (per defaults), daysToEmpty=5, projectedOverage=0%, and action links (View Forecast, Create Pre-Approval) Given projectedOverage = 22% for Marketplace M When thresholds match projectedOverage >= 15% Then an alert with severity=Critical is dispatched to subscribed channels for Marketplace M within 60 seconds and includes the overage percentage, forecasted date of overage, and action links
Digest vs Real-Time Delivery Controls
Given a user has Real-Time disabled and Daily Digest enabled for Org X with digest time 09:00 local When 5 alerts are generated between 00:00 and 09:00 Then no real-time alerts are sent to that user and a single digest is delivered at 09:00 containing 5 entries, highestSeverity=Critical, and per-scope summaries Given Real-Time enabled for Team A and Digest disabled When a threshold triggers Then the alert is delivered within 60 seconds and not included in any digest Given both Real-Time and Digest are enabled When a threshold triggers Then the alert is delivered in real-time and also summarized in the next scheduled digest if still relevant
Snooze, Mute, and GDPR Opt-Out Compliance
Given a user snoozes Team A alerts for 4 hours at 10:00 When thresholds trigger for Team A between 10:00 and 14:00 Then the user receives no alerts for Team A during that window, and snooze auto-expires at 14:00 Given a user mutes Marketplace M alerts When any thresholds trigger for Marketplace M Then no alerts are delivered to that user for Marketplace M until unmuted Given a user opts out of email alerts under GDPR When any alert would be sent via email Then no email is sent, processing/logging reflects opt-out, and other opted-in channels continue; re-enable requires explicit consent capture with timestamp
Alert Deduplication, Cooldown, and Severity
Given an identical alert (same scope, threshold band, severity) would be emitted multiple times When a deduplication window of 15 minutes is active Then only the first alert is delivered and subsequent duplicates within 15 minutes are suppressed per channel Given repeated triggers for the same scope and severity When a cooldown of 60 minutes is configured Then no more than one alert per scope/severity/channel is delivered within the cooldown Given thresholds escalate from Warning to Critical When severity increases Then the Critical alert is delivered immediately, replacing any active cooldown for lower severity
Slack, Email, Webhook Delivery and Retries with Status Tracking
Given Slack channel subscription exists When an alert is sent Then a Slack message is posted via app/bot with title, severity color, metrics (daysToEmpty, projectedOverage), and buttons (View Forecast, Create Pre-Approval), and delivery status records SENT within 10 seconds Given email subscription exists When an alert is sent Then an email is sent from notifications@pixellift.com with localized subject including severity and scope, and delivery status is updated to SENT or FAILED based on provider response within 60 seconds Given a webhook endpoint responds 5xx or times out When an alert is POSTed with JSON payload {eventType:"forecast.alert", scopeId, severity, metrics, dedupKey, timestamp, actionUrls} Then retries occur with exponential backoff (1m, 2m, 4m, 8m, 16m) up to 5 attempts; final status marked FAIL with error reason if all attempts fail Given any channel returns success (2xx for webhook, accepted for email, ok for Slack) When delivery is confirmed Then delivery status is stored per recipient and channel and visible in admin logs with timestamps
Localization and Template Content with Quick Actions
Given org locale = de-DE and user locale = fr-FR When an alert is delivered Then message content is rendered in user locale (fr-FR), with ICU-formatted dates and numbers, and fallback to org locale if user locale unsupported, then to en-US Given a template with placeholders {scopeName}, {daysToEmpty}, {projectedOverage}, {severity}, {forecastDate} When an alert is generated Then all placeholders are populated with correct values and links for View Forecast and Create Pre-Approval are present and functional across channels Given a missing translation key exists When rendering an alert Then English content is used and a missing-translation warning is logged without blocking delivery
Admin Org-Wide Defaults and Role-Based Recipients
Given an admin sets org-wide defaults for thresholds (daysToEmpty=7, overage=15%), severity bands, cooldowns, and digest time 09:00 When new teams/marketplaces are created Then those defaults apply automatically to the new scopes Given role mappings: Org Admin receives all Critical, Finance receives overage alerts, Team Owner receives their team alerts When alerts are dispatched Then recipients are resolved according to role mappings and scope, and no users outside those roles receive alerts unless explicitly subscribed Given a user-level override conflicts with org defaults When determining final preferences Then precedence is: user override > scope override > org default, and the resolved configuration is displayed in the UI
Burn Heatmap & Drilldowns
"As a program manager, I want a heatmap of high-burn areas so that I can quickly identify hotspots and investigate drivers."
Description

Interactive dashboard visualizing consumption intensity across teams, marketplaces, and time ranges. Displays a color-coded heatmap of burn rates with alert overlays, supports filtering (date range, product, channel), and enables drill-down to entity-level trends and recent usage events. Integrates with velocity and forecast APIs to ensure consistency of metrics. Provides export (CSV/PNG) and shareable links while respecting role-based access control.

Acceptance Criteria
Heatmap Visualization with Predictive Alert Overlays
Given a selected date range and accessible entities, When the heatmap loads, Then each entity tile displays a burn-rate bucket mapped to a 5-step color scale (0–20, 20–40, 40–60, 60–80, 80–100 percentiles) with a visible legend. Given forecast data flags days-to-empty ≤ 7 days or projected overage ≥ 10%, When the tile renders, Then an alert overlay badge is shown (amber for days-to-empty, red for overage) and a tooltip displays days-to-empty, projected overage %, and recommended top-up credits. Given an entity has no usage data for the selected range, When the tile renders, Then the tile shows "—" with a tooltip "No data for selected range". Given color scale definitions, When specific sample inputs are provided (e.g., 15th, 35th, 55th, 75th, 95th percentiles), Then the mapped hex codes match the design spec. Given a backend error occurs (HTTP 5xx), When the heatmap fails to load, Then an error state is displayed with a retry action and no stale data is shown.
Multi-Filter Controls (Date Range, Product, Channel, Team)
Given preset ranges (Last 7/14/30/90 days) and a custom range picker, When a user applies a range, Then all tiles, legend, overlays, and summary counts update within 1s and the selected range is reflected in the URL. Given multi-select Product and Channel filters and Team selector, When filters are combined, Then results reflect the intersection of selected values and a filter chip bar shows each active filter with counts. Given filters are changed rapidly (≤ 3 changes in 2s), When requests are issued, Then previous in-flight requests are canceled and only the latest result is rendered. Given the user clicks Clear All, When filters reset, Then defaults apply (Last 30 days; All products/channels/teams) and the URL/state reflect defaults. Given organization timezone is UTC±X, When a custom range is set, Then API queries use that timezone and tooltips show timestamps in the same timezone.
Drill-Down to Entity Trends and Recent Usage Events
Given a user clicks a heatmap tile, When navigation occurs, Then a drill-down view opens within 300ms showing a time-series of daily burn and a 30-day velocity overlay for the same filters. Given the drill-down view, When events are listed, Then the table shows the 100 most recent usage events with columns: timestamp (ISO 8601), action, quantity, user/team, marketplace/channel, and source, with server-side pagination beyond 100. Given a deep-linkable drill-down, When the page is refreshed or the link is opened in a new tab, Then the same entity and filters are restored from the URL. Given the user navigates back, When returning to the heatmap, Then prior scroll position and filters are preserved. Given an entity lacks event history, When the drill-down loads, Then the chart shows an empty state and the table indicates "No recent events".
API Consistency for Velocity and Forecast Metrics
Given a selected entity and date range, When the velocity API is queried, Then the dashboard burn totals equal the API sum within ≤0.5% tolerance for the same parameters. Given forecast API responses for days-to-empty and projected overage, When alert overlays render, Then the values shown in tooltips match the forecast API exactly for the same timestamped snapshot. Given shared query parameters (entity_id, products, channels, date_range, timezone), When both APIs are called, Then requests include identical parameter values and are logged for traceability. Given either API is unavailable (timeout > 5s or HTTP ≥ 500), When the page renders, Then a non-blocking banner indicates degraded metrics and overlays dependent on the failed API are suppressed with a "Data temporarily unavailable" tooltip. Given nightly data reconciliation, When sampled entities are validated, Then discrepancies >0.5% are flagged and reported to monitoring.
Export to CSV and PNG with Filters
Given the heatmap is visible with active filters, When the user exports CSV, Then the file includes one row per visible entity with columns: entity_id, entity_type, team, marketplace, date_range, burn_total, burn_rate_bucket, alert_type, days_to_empty, projected_overage_pct, applied_filters, generated_at (ISO 8601). Given the heatmap is visible, When the user exports PNG, Then the image matches the on-screen heatmap (tiles, legend, overlays) and includes the generated_at timestamp and filter summary in the footer. Given an export exceeds 10,000 rows, When CSV export is requested, Then an asynchronous export job is created, the user is notified in-app, and a link to a pre-signed URL (valid 24h) is delivered upon completion. Given RBAC restrictions, When exporting, Then only entities the user is authorized to view are included and totals reflect the filtered subset. Given an export is initiated, When it completes or fails, Then an audit log entry records user, filters, row count (if success), and error (if fail).
Shareable Links with RBAC Enforcement
Given filters are applied, When the user generates a shareable link, Then the URL encodes all active filters and viewport context and can be opened to reproduce the same state. Given organization-internal access, When a non-tokenized link is shared, Then only authenticated users with at least Viewer role on the relevant scope can access; others receive 403 without leakage of entity metadata. Given tokenized sharing is enabled, When a tokenized link is created, Then it carries a randomly generated token, default expiry of 7 days (configurable), single-tenant scope, and can be revoked; upon expiry/revocation the link returns 410. Given a shareable link is accessed, When the view loads, Then an access audit record is created with user (or token), timestamp, IP, and scope. Given cross-tenant isolation, When a link from Tenant A is opened by a user from Tenant B, Then the system denies access and no heatmap data is rendered.
Performance and Accessibility SLAs
Given a dataset of 2,000 entities and 90-day range, When the heatmap first loads on a standard desktop (4-core CPU, 8GB RAM, 10Mbps), Then p95 time-to-interactive ≤ 2.5s and p95 tile hover-to-tooltip latency ≤ 100ms. Given filter changes (any single filter), When the heatmap re-renders, Then p95 update latency ≤ 500ms and no main-thread long task > 200ms. Given keyboard-only navigation, When focus is on the heatmap grid, Then arrow keys move focus tile-to-tile, Enter opens drill-down, and Esc closes it, with visible focus outlines. Given WCAG 2.1 AA requirements, When tiles and overlays render, Then color contrast ratios are ≥ 4.5:1, and alerts are indicated by both color and an icon for non-color reliance. Given screen reader usage, When tiles receive focus, Then ARIA labels announce entity name, burn bucket, and any active alerts; the legend is announced as a single grouped control.
Pre-Approval Workflow
"As a budget owner, I want a lightweight approval flow tied to forecasts so that credit purchases are controlled yet fast enough to prevent downtime."
Description

Workflow that enables users to trigger, review, and approve forecast-driven top-up requests with configurable approver chains, budget limits, and expiry dates. Pre-populates requests with forecast details, recommended amounts, and justification. Supports comments, attachments, audit trails, and notifications at each step. Integrates with billing to lock pricing and with identity/roles for permissions. Exposes API endpoints for integration with external procurement systems.

Acceptance Criteria
Create Pre-Populated Top-Up Request from Forecast Alert
Given a user with Requester role views a Forecast Alert that predicts days-to-empty <= the configured threshold or projected overage > 0% When the user clicks "Create Pre-Approval" from the alert Then a draft pre-approval request is created and saved with status "Draft" And the request is pre-populated with team/marketplace, days_to_empty, usage_velocity, projected_overage_percent, recommended_top_up_credits, estimated_runout_date, and auto-generated justification text And the estimated price is calculated from the current pricing tier and displayed alongside credits And required fields (team/marketplace, amount in credits, expiry date, justification) must be present or validation errors are shown and the request cannot be submitted
Multi-Level Approver Chain with Threshold Routing
Given an organization approval policy with role-based approvers and amount thresholds is configured When a requester submits a pre-approval for X credits Then the system constructs the approver chain according to policy (e.g., Team Manager if X <= team_limit; add Finance Approver if X > team_limit; add Org Approver if X > org_limit) And approvals are collected in the defined sequence (or in parallel where configured) And the requester cannot approve their own request; if the requester appears in the chain they are skipped and the next approver is required And if no eligible approver is found for any step, submission is blocked with a clear error identifying the missing role And if an approver does not act within the SLA window, the request escalates to the configured delegate and a reminder is sent
Budget Limit and Expiry Enforcement
Given a team/monthly budget limit policy is configured When a pre-approval request amount exceeds the remaining budget and the policy "Allow overage with extra approval" is disabled Then the system blocks submission and displays a budget exceeded error When the same policy is enabled Then the system adds the Budget Owner approval step and allows submission And each pre-approval requires an expiry date >= current date + 1 day And when the pre-approval reaches its expiry without being converted to a purchase, the request status changes to "Expired", locked pricing (if any) is released, and requester and approvers are notified And expired or rejected pre-approvals cannot be used to authorize purchases
Comments, Attachments, and Immutable Audit Trail
Given a pre-approval request exists When the requester or any approver posts a comment Then the comment is appended to a threaded discussion and notifications are sent to participants When a user uploads attachments Then up to 5 files with types pdf|png|jpg and size <= 20 MB per file are accepted, virus scanned, and stored with the request And all actions (create, submit, approve, reject, comment, attachment add/remove, policy evaluation, escalation, expiry, billing lock/release) are recorded in an immutable audit log entry including timestamp (UTC ISO 8601), actor, action, and changed fields old->new And the audit log is read-only in the UI and exportable via API
Step Notifications and Reminders
Given notifications are enabled for the workspace When a pre-approval is submitted Then the first approver(s) receive in-app and email notifications with a deep link and request summary When an approval or rejection occurs Then the requester and next approver(s) are notified And outstanding approvers receive automated reminders every 24 hours until they act, the request escalates, or the request expires And notifications are de-duplicated if multiple events occur within 1 minute for the same recipient And webhook events are emitted for create, submit, approve, reject, escalate, and expire with signed HMAC headers and retried with exponential backoff on non-2xx responses
Billing Price Lock on Approval
Given billing integration is enabled When the final required approval is recorded Then the system requests a price lock from billing for the approved credits, capturing lock_id, unit_price, taxes, and lock_expiration_at (e.g., 14 days from approval) And subsequent purchases that reference this pre-approval use the locked pricing if lock_expiration_at is in the future, otherwise the purchase is blocked or re-quoted per policy And if the pre-approval is rejected or expires, any active price lock is released and the UI/API reflect the release And price lock details are visible on the pre-approval record and included in API responses
External Procurement API and Webhooks
Given a partner system holds valid OAuth2 client credentials with appropriate scopes When it calls POST /pre-approvals with a unique Idempotency-Key header and valid payload Then the API returns 201 with the created record, echoes the Idempotency-Key, and subsequent retries with the same key return 201/200 with the same resource When it calls GET /pre-approvals/{id} Then the API returns 200 with full request details including status, approver chain, audit summary, and price lock metadata When it calls POST /pre-approvals/{id}/approve or /reject with proper scope and role constraints Then the API enforces RBAC and separation-of-duties, returns 200 on success, and 401/403/409 on unauthorized/forbidden/conflict cases And API rate limits are enforced at 100 requests/min per client (429 on exceed) And outbound webhooks deliver status changes with HMAC-SHA256 signatures, 2-minute timeout, and 3 retries with exponential backoff

Role Blueprints

Spin up least‑privilege access in minutes with prebuilt, customizable roles (Contributor, Reviewer, Approver, Publisher, Finance, Admin). Granular toggles control who can view/annotate images, edit presets, run batches, push exports, manage credits, and invite users—per workspace or client. Map groups to roles once and keep audits clean while eliminating permission sprawl.

Requirements

Role Blueprint Catalog
"As a workspace admin, I want to assign prebuilt roles to users so that I can onboard teams quickly with least‑privilege defaults."
Description

Provide a prebuilt library of least‑privilege roles (Contributor, Reviewer, Approver, Publisher, Finance, Admin) aligned to PixelLift actions. Admins can assign a blueprint in seconds and optionally customize before saving, enabling fast, consistent onboarding across workspaces and clients. The catalog ships with safe defaults that map to core capabilities (view/annotate images, edit presets, run batches, push exports, manage credits, invite users). Blueprints integrate with authorization checks across the image library, batch processor, export pipelines, billing, and user management to reduce permission sprawl and errors. Expected outcome: dramatic reduction in time-to-assign, fewer misconfigurations, and standardized access patterns across accounts.

Acceptance Criteria
Assign Prebuilt Role to New Workspace User
- Given an Admin in Workspace A viewing the Role Blueprint Catalog, when the catalog screen is opened, then the list of prebuilt roles (Contributor, Reviewer, Approver, Publisher, Finance, Admin) loads with names and brief permission summaries in <= 2 seconds p95. - Given the Admin selects the Contributor blueprint and assigns it to user U in Workspace A, when the Admin confirms, then U’s effective permissions in Workspace A reflect the Contributor set within 5 seconds and a success confirmation is displayed. - Given U holds the Contributor role only in Workspace A, when U switches to Workspace B, then no new permissions are granted in Workspace B. - Given the assignment completes, then an audit record is created capturing actor, subject, role, workspace, action (assign), and timestamp.
Customize Blueprint Before Saving
- Given an Admin selects the Reviewer blueprint, when they toggle permissions and save as "Reviewer - Client A", then a new custom blueprint with that name is created, labeled as Custom, and appears in the catalog. - Given a custom blueprint is created, when it is assigned to a user, then the user receives exactly the permissions toggled on and none that are toggled off. - Given the Admin enters a name that already exists, when saving, then the system blocks save and prompts for a unique name without creating a duplicate. - Given permission dependencies (e.g., Push Exports requires Run Batches), when saving a custom blueprint, then the system validates dependencies and blocks invalid combinations with a clear error.
Granular Permission Toggles Per Workspace
- Given an Admin is editing permissions for Workspace A, when they modify a blueprint or assignment, then the changes apply only to Workspace A and do not affect other workspaces or clients. - Given "Run Batches" is disabled for user U in Workspace A, when U attempts to start a batch in Workspace A, then the API returns 403, the UI control is disabled or shows an inline error, and an authorization failure is logged. - Given "View/Annotate Images" is enabled for U, when U opens the image library in Workspace A, then U can view and annotate images but cannot access actions that are not permitted.
IdP Group-to-Role Mapping
- Given an Admin maps IdP group "Designers" to the Contributor blueprint for Workspace A, when a new user who belongs to "Designers" signs in, then the user is automatically assigned Contributor in Workspace A before they reach the dashboard. - Given a user is removed from "Designers", when the IdP update is received, then the role is revoked in Workspace A within 5 minutes or upon next login, whichever occurs first. - Given multiple group mappings match a user for the same workspace, when determining effective role, then the system applies the most restrictive mapped role by default to minimize permission sprawl, with precedence configurable by Admins. - Given a group-to-role mapping is changed, when the change is saved, then an audit record is created and propagated without duplicating past audit entries.
Authorization Enforcement Across Modules
- Given a role lacks "Edit Presets", when the user tries to edit a preset in the Presets module, then the action is blocked (HTTP 403), the UI explains that Edit Presets is required, and an audit event is recorded. - Given a role lacks "Run Batches", when the user tries to start a new batch in the Batch Processor, then the action is blocked (HTTP 403), the Start button is disabled or results in an inline error, and an audit event is recorded. - Given a role lacks "Push Exports", when the user attempts to push an export in Export Pipelines, then the action is blocked (HTTP 403) and logged. - Given a role lacks "Manage Credits" and "Invite Users", when the user navigates to Billing or User Management, then billing amounts are hidden and Invite controls are not visible.
Safe Default Permission Sets
- Given the catalog loads, then defaults are as follows and are editable only by Admins: - Contributor: View/Annotate Images only. - Reviewer: View/Annotate Images; cannot Edit Presets, Run Batches, Push Exports, Manage Credits, Invite Users. - Approver: View/Annotate Images; Push Exports; cannot Edit Presets, Run Batches, Manage Credits, Invite Users. - Publisher: View/Annotate Images; Run Batches; Push Exports; cannot Edit Presets, Manage Credits, Invite Users. - Finance: Manage Credits; View Billing; cannot View/Annotate Images, Edit Presets, Run Batches, Push Exports, Invite Users by default. - Admin: All permissions. - Given a fresh tenant, when an Admin assigns any prebuilt role without customization, then the effective permissions match the default set exactly.
Performance, Consistency, and Time-to-Assign
- Given the Role Blueprint Catalog is requested, then p95 load time <= 2 seconds with up to 50 blueprints (including customs). - Given an Admin assigns a prebuilt role to a single user, then end-to-end propagation to authorization checks occurs in <= 5 seconds p95. - Given an Admin assigns roles to 20 users in the same workspace during a single session, then all assignments complete within <= 30 seconds and are visible in the audit log. - Given normal operation, then authorization decisions are consistent across API and UI (no mismatches) in 100% of tested actions.
Permission Toggle Matrix
"As an account owner, I want granular permission toggles within roles so that I can precisely restrict actions without creating one-off custom roles."
Description

Implement a granular permission matrix that exposes fine-grained toggles per capability (e.g., view vs. annotate images, create vs. edit presets, start vs. schedule batches, export by destination, manage credits with limits, invite users, manage groups). Each toggle supports scope-aware enforcement and can be adjusted within a role before assignment. The matrix binds to backend authorization middleware so every API call is evaluated against the effective toggle set and scope. Benefits include precise control without custom one-off roles, reduced privilege creep, and easier audits. Expected outcome: consistent least‑privilege enforcement across UI and API with minimal overhead for admins.

Acceptance Criteria
Adjust Role Toggles Before Assignment
Given I am an Admin editing a role in the Permission Toggle Matrix, When I enable or disable a capability toggle and adjust its scope, Then the role preview updates in real time to show the exact effective permissions and scopes. Given I save the role, When I reopen the role configuration, Then all toggles and scopes persist exactly as saved. Given I assign this role to a user, When the user signs in, Then the user's effective permissions equal the role's toggles and scopes with no per-user overrides. Given the user navigates the UI, When gated controls render, Then controls are enabled or disabled strictly according to the effective toggles and scopes.
Scope-Aware Enforcement Across Workspaces
Given a user has view_images enabled only for Workspace A, When the user requests GET /workspaces/B/images, Then the API returns 403 PERMISSION_SCOPE_MISMATCH and the UI does not display images for Workspace B. Given the same user switches to Workspace A, When the user requests GET /workspaces/A/images, Then the API returns 200 and the UI lists images. Given annotate_images is disabled for Workspace A, When the user posts POST /workspaces/A/annotations, Then the API returns 403 PERMISSION_DENIED and the annotate action is disabled in the UI.
Backend Authorization Middleware Binding
Given any protected endpoint is invoked, When the request enters the backend, Then authorization middleware evaluates the effective toggle and scope before the handler executes. Given the required toggle is missing or out of scope, When the endpoint is called, Then the middleware short-circuits the handler and returns 403 with a machine-readable code and request_id. Given the required toggle is present and in scope, When the endpoint is called, Then the request succeeds (2xx) and an auth_decision=allow metric is emitted. Given an endpoint is not mapped to any matrix capability, When it is called, Then it is denied by default (no implicit allow).
Export Destination Toggles Enforcement
Given a user has export:amazon enabled and export:shopify disabled, When the user calls POST /exports?dest=amazon, Then the API returns 201 and the export proceeds; When the user calls POST /exports?dest=shopify, Then the API returns 403 PERMISSION_DENIED and the Shopify option is disabled in the UI. Given the role's export scope excludes Client X, When the user attempts any export of Client X assets even to Amazon, Then the API returns 403 PERMISSION_SCOPE_MISMATCH and no export job is created.
Start vs Schedule Batch Permission Separation
Given a role grants batch.start but not batch.schedule, When the user calls POST /batches/start, Then the API returns 202 and a batch job is created; When the user calls POST /batches/schedule, Then the API returns 403 PERMISSION_DENIED and the Schedule control is disabled in the UI. Given a role grants batch.schedule but not batch.start, When the user calls POST /batches/start, Then the API returns 403 PERMISSION_DENIED; When the user calls POST /batches/schedule with a future time in an allowed scope, Then the API returns 201 and a scheduled job is created.
Credits Management With Limits and Enforcement
Given a role includes manage_credits with limit_edit enabled and a monthly_credit_limit of 10000, When the user updates the limit to a value <= 10000 via PATCH /credits/limits, Then the API returns 200 and an audit record captures before/after values. Given remaining monthly credits are 500, When the user starts an action requiring 600 credits, Then the API returns 403 PERMISSION_LIMIT_EXCEEDED, no credits are deducted, and the UI shows an over-limit message. Given concurrent requests attempt to consume remaining credits, When both requests are processed, Then credits are deducted atomically with no negative balances and at most one request succeeds if total exceeds the remaining amount.
Audit Trail of Permission Changes and Access Decisions
Given any role permission change is saved, When the change is committed, Then an audit event records actor, timestamp, role id, changed toggles and scopes (before/after), and optional reason. Given an authorization denial occurs, When a 403 is returned by middleware, Then an audit event records user id, action, scope, decision=deny, error code, and request_id. Given an auditor requests GET /audits?type=role_permissions&range=90d, When the export is generated, Then a CSV is delivered within 30 seconds containing complete, chronologically ordered records for the range.
Workspace/Client Scoped Assignments
"As an agency admin, I want to assign roles per client workspace so that collaborators only access the assets and credits for their client."
Description

Enable assigning roles per workspace or client, allowing the same user to hold different roles in different contexts. Provide UI for selecting scope during assignment and clear context switching to avoid cross-client data exposure. Authorization checks must include both role and scope identifiers to guard image libraries, presets, batch runs, exports, credits, and invitations. Include inheritance and override rules (e.g., global Admin can be restricted at a client scope). Expected outcome: secure multi-tenant collaboration for agencies and multi-brand sellers without duplicating accounts.

Acceptance Criteria
Scope-Specific Role Assignment via UI
Given I am a Workspace Admin in Workspace W with Clients C1 and C2 When I assign user M the Reviewer role scoped to Client C1 using the assignment UI Then the UI requires explicit selection of scope (Workspace or specific Client) before save And the saved assignment is Reviewer@Client:C1 with scopeType=client and scopeId=C1 And M has no role in Client C2 or at Workspace level unless explicitly assigned And GET /roleships?userId=M returns an item with role=Reviewer, scopeType=client, scopeId=C1 And an audit record is created with actorId=myId, subjectId=M, role=Reviewer, scopeId=C1, action=assign, outcome=created
Context Switching and Scope Indicator
Given I hold roles in multiple scopes (e.g., Contributor@Client:C1 and Approver@Client:C2) When I switch context to Client C2 using the scope switcher Then all images, presets, batch runs, exports, credits, and invitations are filtered to C2 only And a persistent scope indicator displays Client C2 on every page and API response header X-PL-Scope=C2 And navigating directly to a C1 resource while in C2 returns 403 with code=insufficient_scope and offers a "Switch to C1" action And my selected scope persists across sessions for 30 days unless changed
Cross-Client Data Isolation by Role+Scope
Given user R is Reviewer@Client:C1 and has no role in Client C2 When R attempts in Client C1: view images (allow), annotate images (allow), edit presets (deny), run batches (deny), push exports (deny), manage credits (deny), invite users (deny) Then allowed actions return 200 and denied actions return 403 with code=insufficient_scope And when R requests any resource under Client C2, the response is 403 with code=insufficient_scope And each denial is logged with actorId=R, role=Reviewer, scopeId=C1, resourceScopeId=C2 or action, outcome=denied
Scope Overrides Precedence over Global Admin
Given user G has Global Admin and an explicit client-level override for Client C disabling "push exports" and "invite users" When G operates in Client C Then all Admin capabilities remain except push exports and invite users, which return 403 with code=overridden_permission And when G operates in Client D (no override), full Admin capabilities are available And permission evaluation order is: explicit client override > explicit workspace role > global role; first applicable rule applies
Scoped Batch Run and Export Restrictions
Given user U is Contributor@Client:C and selects a batch with 120 images from C and 30 from Client D When U starts the batch and selects export destinations Then only the 120 images from Client C are processed; 30 are excluded with reason=out_of_scope And only export destinations belonging to Client C are selectable; others are hidden or disabled And the batch summary shows processed=120, excluded=30 with breakdown by reason
Scoped Group-to-Role Mapping Synchronization
Given IdP group "Brand-A Editors" is mapped to Contributor@Client:A and sync is enabled When a user joins the IdP group Then within 60 seconds the user receives Contributor@Client:A And when the user leaves the group, the assignment is removed within 60 seconds unless the user also has a manual assignment in the same scope And audit logs attribute the change with source=IdP_sync, groupId, mappingId, and actorId=system And manual per-user role assignments in Client A remain unchanged
Audit Trail Includes Role and Scope on Access Decisions
Given any role assignment, revocation, or access decision occurs When the system records the audit entry Then the entry includes actorId, subjectId (if applicable), role, scopeType, scopeId, resourceType, resourceId, action, decision (allow|deny), reason (e.g., insufficient_scope, overridden_permission), timestamp, and requestId And audit entries are immutable and retrievable via API with filters for scopeId and date range
Workspace vs Client Scope Authorization Consistency
Given user P is Publisher@Workspace:W with a client-level override in Client C2 that disables "push exports" When P pushes exports from Client C1 (within Workspace W) Then the action succeeds with 200 and an export job is created under Client C1 And when P attempts to push exports from Client C2, the action returns 403 with code=overridden_permission And GET /authorizations?userId=P&action=push_export returns allow for scopeId=C1 and deny for scopeId=C2
IdP Group-to-Role Mapping
"As an IT admin, I want to map IdP groups to PixelLift roles so that access is automatically granted and revoked based on our directory."
Description

Support mapping identity provider (Okta, Azure AD, Google Workspace) groups to Role Blueprints for automatic provisioning and deprovisioning. On SSO, users inherit roles based on current group membership; SCIM sync updates assignments when HR changes occur. Provide a mapping UI, conflict resolution rules, and sync logs. Integrate with invites to prevent manual drift and ensure least‑privilege remains aligned with corporate directories. Expected outcome: hands-off access lifecycle, faster onboarding/offboarding, and cleaner audits.

Acceptance Criteria
SSO assigns Role Blueprints from IdP groups at login
Given an active mapping exists between IdP group "PL-Approvers" and the Approver role for workspace "Acme Store" And the user is a current member of "PL-Approvers" in the configured IdP (Okta/Azure AD/Google Workspace) When the user signs in to PixelLift via SSO Then the user is granted the Approver role for workspace "Acme Store" before the session is established (within 3 seconds of assertion receipt) And any previously assigned manual role for that workspace is replaced by the mapped role And the user’s effective permissions exactly match the Approver Role Blueprint definition (no extra toggles enabled) And an audit log entry records the mapping ID, source (SSO), evaluated groups, and resulting role
SCIM-driven updates adjust roles automatically
Given SCIM provisioning is enabled and connected for the tenant When a SCIM group membership add event is received for a user that matches a configured mapping Then the corresponding role is assigned in PixelLift within 5 minutes of event receipt and logged When a SCIM group membership remove event is received that previously granted a role Then the corresponding role is removed within 5 minutes and the change is logged When a SCIM user deprovision (delete/disable) event is received Then all workspace roles are revoked within 60 seconds, user cannot access PixelLift, and the action is logged And SCIM throttling/backoff is respected without losing events (missed events are retried until success)
Mapping UI supports per-IdP, per-workspace role mappings
Given an Admin opens the Role Mappings UI When the Admin creates a mapping by selecting IdP (Okta/Azure AD/Google), IdP group, target workspace/client, and a Role Blueprint Then the mapping can be saved only if the combination is unique and valid And the UI validates the IdP group (live lookup or deferred verification flag) and displays its canonical identifier And the Admin can preview effective roles for a test user email before saving And the Admin can search, sort, and edit mappings; all edits are versioned and logged And mappings support scoping to multiple workspaces by adding explicit rows (no implicit wildcarding)
Deterministic conflict resolution and precedence
Given a user matches multiple mappings that apply to the same workspace When the system evaluates mappings at SSO or SCIM sync Then the effective role is determined deterministically using the precedence order configured in the UI (default: Admin > Publisher > Approver > Contributor > Reviewer > Finance) And the precedence is editable by Admins and takes effect within 1 minute of save And the evaluation log records all candidate mappings, the applied precedence, and the resulting effective role And if two mappings yield identical precedence, the one with the most restrictive permissions is chosen And the resulting permissions never exceed those defined by the selected Role Blueprint
Invite flow prevents manual drift with IdP-managed users
Given a tenant has an IdP configured with group-to-role mappings When an Admin invites a user whose email domain is covered by the configured IdP Then the invite UI indicates the user will be managed by IdP and disables manual role selection When the invited user completes first SSO Then their role assignments are derived solely from current IdP group membership, replacing any pre-existing manual roles And subsequent attempts to edit roles for IdP-managed users are blocked with a clear message and are logged
Sync logs and audit trails for mappings and assignments
Given an Admin views the Access Sync Logs page When SSO or SCIM-driven role changes occur Then each event is logged with timestamp (UTC), actor (system), source (SSO/SCIM), user, IdP group(s), mapping ID(s), workspace, action (grant/revoke), result, and error details if any And logs are filterable by user, group, workspace, source, action, and status, and are exportable to CSV And logs are retained for at least 365 days and are accessible only to Admins And a warning banner and webhook/email alert are generated for repeated sync errors (3+ failures within 10 minutes)
Safe defaults for unmapped users and failure modes
Given a user signs in via SSO and none of their IdP groups match any configured mapping Then the user receives no workspace roles and is shown a "No access—contact your admin" message; API returns HTTP 403 for protected operations And an audit log records the unmapped login with evaluated groups When mapping evaluation fails due to transient system error Then no new roles are granted, last-known roles are not escalated, and an error is logged with a retry scheduled within 1 minute
Role Audit Trail & Change History
"As a compliance officer, I want detailed audit logs of role and permission changes so that I can demonstrate governance during audits."
Description

Create immutable, searchable logs for role creation, edits, assignments, scope changes, and deletions, capturing actor, timestamp, previous vs. new values, and affected users/workspaces. Provide filters, exportable reports (CSV/JSON), and retention controls to meet compliance needs. Surface inline audit context in the UI (e.g., “who added export permission to Reviewer”). Integrate with existing event logging and webhooks to notify on sensitive changes. Expected outcome: demonstrable governance with fast root-cause analysis and audit readiness.

Acceptance Criteria
Immutable Role Change Logging
Given any role lifecycle event (create, edit, assignment, scope change, delete) When the event is recorded Then an append-only audit entry is created with a unique immutable ID and write-once storage And the entry cannot be updated or deleted via any API or UI Given an existing audit entry When any user attempts to alter or delete it Then the system rejects the request with 403 and records a tamper-attempt audit event Given a sequence of audit entries When integrity verification is requested via the audit integrity API Then the API returns OK if all hashes verify or the first failing entry ID if not Given multiple updates to the same role attribute When each update occurs Then a new audit entry is appended and earlier entries remain unchanged
Captured Audit Fields for Role Events
Given any role-related event When the audit entry is stored Then it includes: event_type, occurred_at (UTC ISO 8601), actor_id, actor_email, actor_ip, request_id, correlation_id, role_id, role_name, scope_type (org/workspace/client), scope_id, affected_user_ids, affected_workspace_ids, previous_values (diff), new_values (diff), outcome (success/failure), platform (UI/API), and version Given an assignment to a group When the audit entry is stored Then it includes subject_type=group, subject_group_id, subject_group_name Given an event initiated by API token/service account When the audit entry is stored Then actor_type=service and token_id/service_name are captured Given any audit entry is retrieved via API When fields contain sensitive values Then redaction rules are applied to mask secrets while preserving diffs
Audit Log Filtering and Search
Given a large audit history (≥100k entries) When filtering by date range, event_type, actor, role_id/role_name, scope_id, affected_user_id, attribute_changed, and outcome Then only matching entries are returned And results are ordered by occurred_at desc with stable cursor-based pagination Given a text search for actor_email, role_name, event_id, or request_id When the query is executed Then matching records are returned case-insensitively Given typical load and indexed data When a query is executed Then the first page (≤200 items) responds within 2 seconds at the 95th percentile Given a paginated query When the next page is requested using the cursor Then results continue without duplication or omission and total_count is returned
Inline Audit Context in Role Permissions UI
Given a user with View Audit permission opens a role's permission matrix When viewing any permission toggle Then the UI displays last_changed_by, occurred_at, and a "View change" link Given the user clicks the "View change" link When the modal opens Then it shows previous vs new values, actor details, and a deep link to the full audit entry Given a user lacks View Audit permission When viewing the role Then inline audit context is hidden and no audit data is exposed Given a permission like "Export images" was added to Reviewer When the role page loads Then the permission row shows "Added by <actor> on <UTC timestamp>" with the audit link
Exportable Audit Reports (CSV/JSON)
Given filters are applied to the audit log When the user exports as CSV or JSON Then the export includes only filtered entries with a defined schema header (schema_version, generated_at UTC, filter_summary) And timestamps are UTC ISO 8601 Given a large export (>50k rows) When the export is requested Then it runs as an async job with status endpoints, progress reporting, and completion notification And a single file contains up to 1,000,000 rows with a continuation token for more Given an export file is generated When the user downloads it Then a SHA-256 checksum is provided and verified And CSV follows RFC 4180; JSON is NDJSON Given role-based access control When a user without Export Audit permission attempts export Then the request is denied with 403 and the attempt is audited
Retention Policies and Legal Hold
Given an organization admin opens audit retention settings When configuring retention Then they can set a period between 90 days and 7 years (default 365 days) And changes require explicit confirmation and are themselves audited Given entries exceed the configured retention and are not on hold When the daily purge job runs Then expired entries are irreversibly purged And a purge summary audit event records count, time window, and actor=system Given a legal hold is required When an admin applies a hold with a reason Then entries matching the hold scope are exempt from purge until the hold is removed And hold application/removal is audited Given entries are approaching purge When an admin views the audit UI Then a banner indicates upcoming deletions and offers export of the expiring set
Sensitive Change Webhook Notifications
Given a sensitive event occurs (permission escalation, assignment to Admin role, retention change, role deletion) When the event is recorded Then a webhook is enqueued to subscribed endpoints with event_id, event_type, occurred_at, minimal payload, and links to the audit entry Given webhook delivery When the payload is sent Then it includes HMAC-SHA256 signature and idempotency key headers And failures are retried with exponential backoff up to 10 times over 24 hours Given webhook outcomes When deliveries succeed or fail Then response code, duration, and attempt count are captured in the audit log And failures trigger an email alert to admins Given an admin tests a webhook endpoint When using the Test Delivery action Then a signed sample payload is sent and the UI displays delivery result and verification guidance
Role Versioning & Rollback
"As a security-conscious admin, I want to version and rollback role configurations so that I can quickly fix misconfigurations without downtime."
Description

Allow admins to clone and customize blueprints, track every version of a role’s permission matrix, and compare diffs before publishing. Provide safe rollback to prior versions and staged rollouts (draft, publish, schedule). Show impact previews listing users/workspaces affected by a change. Integrate with the audit trail to capture rationale and approvals. Expected outcome: safer iterations on access policies with quick recovery from misconfigurations.

Acceptance Criteria
Drafting Role Version from Blueprint Clone
Given an Admin with Manage Roles permission When they clone a "Contributor" blueprint, modify permission toggles, and click "Save Draft" Then a new role version is created with status "Draft", a unique version identifier, author, and timestamp And the active published version remains unchanged for all users and workspaces And the draft appears in the role’s version history and can be reopened and edited
Permission Diff Preview Before Publish
Given a draft version exists for a role When the Admin opens "Compare with Published" Then the UI shows a permissions diff highlighting additions, removals, and unchanged items with total counts for each And if no differences exist, the Publish action is disabled with a message "No changes to publish" And the Admin can export the diff as CSV or JSON
Scheduled Publish with Timezone and Conflict Handling
Given a draft version exists and the Admin selects "Schedule Publish" When the Admin sets a valid future date/time and timezone and confirms Then a scheduled job is created, visible with countdown and exact execution timestamp And the Admin can cancel or reschedule prior to execution And at the scheduled time the draft becomes the Published version atomically within 60 seconds And if another publish is already scheduled for the same role, the system blocks creation with an error "Conflicting schedule exists"
Rollback to Prior Version with Immediate Enforcement
Given a role has at least one prior published version When the Admin selects a previous version and clicks "Rollback Now" Then the system records a new Published version that mirrors the selected prior version and marks the previous active version as superseded And effective permissions are enforced across UI and API within 60 seconds And any in-memory permission caches are invalidated And an audit entry is created for the rollback
Impact Preview Lists Affected Users and Workspaces
Given a draft exists When the Admin opens Impact Preview Then the system lists all users and workspaces affected by the prospective change with counts of permission gains and losses And the list supports filtering by workspace/client and export to CSV And publishing or rollback requires the Admin to acknowledge "I have reviewed the impact" before proceeding
Audit Trail Captures Rationale and Approvals
Given an Admin initiates publish, schedule, or rollback When prompted, the Admin must enter a rationale of at least 10 characters and optionally select approvers if approvals are configured Then the action cannot proceed without a valid rationale And the audit trail stores actor, timestamp, action type, role id, from-version, to-version, rationale, and a machine-readable diff And approval decisions (approve/deny, approver, timestamp) are recorded and immutable
Concurrent Edit Locking and Version Conflict Resolution
Given two Admins open the same draft version When Admin A saves changes and Admin B attempts to save without refreshing Then Admin B is prevented from overwriting and is shown a version conflict message with options to reload or save as a new draft copy And if two publish actions are attempted concurrently for the same role, only the first succeeds and the second receives a clear error, both events logged
Permission Simulator & Access Preview
"As a project lead, I want to preview a user’s effective permissions so that I can verify access before inviting them."
Description

Offer a simulator where admins can select a user (or hypothetical role+scope) and preview effective permissions, accessible assets, and blocked actions. Provide “why/why not” explanations referencing specific toggles and scopes, and allow testing of common workflows (e.g., run batch, push export). Integrate directly with the authorization engine to ensure parity with production checks. Expected outcome: fewer support tickets and faster troubleshooting, preventing over-permissioning to unblock users.

Acceptance Criteria
Preview Effective Permissions for Existing User
Given an admin selects an existing user and a workspace scope When the admin opens the Permission Simulator Then the simulator displays Allow/Deny for view images, annotate images, edit presets, run batches, push exports, manage credits, and invite users And the displayed outcomes reflect all current role assignments, group mappings, and per-scope overrides in effect at the time of simulation And the results load within 2 seconds for users with up to 10 group memberships
Hypothetical Role and Scope Simulation
Given an admin selects a Role Blueprint (e.g., Contributor, Reviewer, Approver, Publisher, Finance, Admin) and customizes toggles and scope without assigning it to any user When the admin runs the simulation Then the simulator shows the effective Allow/Deny outcomes exactly as if a user had that role and scope in production And no changes are persisted to real user permissions or groups And the simulator clearly labels the session as Hypothetical
Workflow Test: Run Batch
Given a user or hypothetical role is selected in the simulator with a specific workspace or client scope When the admin runs the Run Batch workflow test Then the simulator returns Pass if can_run_batches is Allowed and all prerequisite permissions (view images and edit presets if required by batch definition) are satisfied And the simulator returns Fail if any required permission is Denied, listing the missing permissions And the outcome is generated in under 1 second after clicking Test
Workflow Test: Push Export
Given a user or hypothetical role is selected in the simulator with destinations configured When the admin runs the Push Export workflow test Then the simulator returns Pass only if can_push_exports is Allowed and the user has access to the selected assets within scope And the simulator returns Fail if export is blocked, enumerating which permission or scope constraint caused the block And the simulator displays the count of assets eligible vs ineligible for export
Why/Why Not Explanations Traceability
Given the simulator shows an Allow or Deny for an action When the admin expands Why/Why not Then the simulator lists the evaluation chain including role blueprint name, specific toggle (e.g., can_run_batches), scope (workspace/client), and any higher-precedence deny/allow rules And each item includes a link or reference to the exact settings screen where it can be changed And explanations include timestamp and policy version used for the decision
Asset Visibility Preview by Scope
Given a user or hypothetical role and a scope are selected When the admin opens the asset visibility preview Then the simulator displays the total count of assets accessible vs blocked for that selection And a random sample of at least 50 accessible asset IDs can be listed or exported for verification And the accessible count matches an authorization spot-check of 100 sampled assets with 0 mismatches
Authorization Engine Parity Verification
Given a test suite of at least 200 combinations of users/roles/scopes/actions and assets When the simulator is executed against the suite Then the simulator’s Allow/Deny results match the production authorization engine for 100% of cases And the simulator records the policy version and environment endpoint used, matching production configuration And any policy update is reflected in simulator results within 60 seconds of deployment

Preset Lockbox

Protect brand and compliance by locking critical presets behind versioned approvals. Users submit change requests with auto‑generated diffs and sample previews; reviewers approve, comment, or roll back in one click. Only ‘signed’ preset versions can run in exports, ensuring consistent outputs across teams and zero drift between clients or channels.

Requirements

Versioned Preset Repository
"As a brand manager, I want presets to be versioned with full history so that I can track changes and reliably reference the exact configuration used for any export."
Description

Implement an immutable, Git-like versioning system for image-edit presets where each change creates a new version with metadata (author, timestamp, commit message), machine-readable changelog, and semantic version tags. Support diff-able JSON for preset parameters, attach representative sample previews to each version, and enable side-by-side comparison between any two versions. Provide browse, search, and filter across presets by client, channel, tag, and status, ensuring a single source of truth within PixelLift.

Acceptance Criteria
Immutable version creation with metadata and SemVer validation
Given an existing preset and a current head version, When a user submits a new version with preset JSON, authorId, commitMessage, semverTag, and baseVersionId equal to the current head, Then the system persists a new immutable version with a unique versionId, parentVersionId = baseVersionId, authorId, ISO-8601 UTC timestamp, commitMessage, semverTag, and a contentHash. Given a persisted version, When any attempt is made to modify its JSON, metadata, or semverTag, Then the system rejects the operation and no data changes persist. Given a new version submission, When semverTag is not a valid Semantic Version (MAJOR.MINOR.PATCH with optional -pre and +build) or already exists for this preset, Then the system rejects the request with a validation error. Given a new version submission missing authorId or commitMessage, Then the system rejects the request as invalid input.
Machine-readable changelog and JSON diff generation
Given a successful version creation, Then the system generates and stores a machine-readable changelog representing the difference from the parent JSON using RFC 6902 JSON Patch operations (add, remove, replace) ordered by path. Given a version with a stored changelog, When a client requests GET /presets/{presetId}/versions/{versionId}/changelog, Then the response is application/json and applying the patch to the parent JSON reproduces the new version JSON exactly. Given two versions of the same preset, When a diff is requested between them, Then the API returns a deterministic, path-sorted diff and the UI groups and highlights changes by parameter path.
Version sample previews attachment and retrieval
Given a new version is created, When sample previews (minimum 1) are generated or attached for that version, Then the version stores preview metadata (previewId, format, width, height, byteSize, createdAt) and the files are retrievable via secure URLs/endpoints. Given a version creation request, When no preview is provided and auto-generation fails, Then the version creation fails and no partial version is persisted. Given a version with previews, When the version detail view loads, Then all previews render successfully and are accessible within acceptable performance bounds (e.g., p90 < 2s for up to 3 previews).
Side-by-side comparison between two versions
Given two versions of the same preset, When a user opens the compare view, Then the UI displays sample previews side-by-side and a synchronized JSON parameter diff highlighting added/removed/changed fields and the total change count. Given two versions from different presets, When a compare request is made, Then the system rejects the comparison with a clear error indicating presets must match. Given versions with different numbers of previews, When displayed side-by-side, Then the UI aligns previews by index and labels missing previews clearly.
Browse, search, and filter presets repository
Given presets tagged with client, channel, tags, and status, When filters are applied in combination, Then only matching presets are returned and the total count reflects the filtered set. Given a search query, When searching by preset name or commitMessage, Then results include any preset where either field contains the query (case-insensitive, substring). Given a large dataset (>= 1000 presets), When requesting the listing with pagination, Then the API responds within 500 ms p90 and includes pagination metadata (page, pageSize, totalItems, totalPages). Given active filters, When the user clears all filters, Then results revert to the default sort by updatedAt descending.
Latest version resolution and semantic sort order
Given multiple versions including pre-release and build metadata (e.g., 1.9.10, 1.10.0, 2.0.0-beta.1, 1.10.0+build.5), When requesting latestStable, Then the system returns the highest non-prerelease SemVer (1.10.0). Given the same set, When requesting latestAny, Then the system returns the highest SemVer by precedence including pre-releases (2.0.0-beta.1). Given a list of versions, When sorted by SemVer, Then numeric components are compared correctly (1.10.0 > 1.9.10) and build metadata does not affect order.
Concurrency control with parent linkage and conflict handling
Given version creation requires a baseVersionId, When baseVersionId does not match the current head, Then the system rejects the request with a conflict and returns the current head id. Given two concurrent create requests with the same baseVersionId, When processed, Then at most one succeeds; the other receives a conflict without creating a duplicate. Given a successfully created version, Then parentVersionId equals the provided baseVersionId, forming a linear, gapless history for the preset.
Change Request with Auto-Diff & Preview
"As an editor, I want to raise a preset change request with auto-generated diffs and previews so that reviewers can quickly understand the impact and approve or request changes with confidence."
Description

Enable users to submit change requests (CRs) against existing presets that automatically generate human-readable diffs of parameter changes and visual previews across a configurable sample set (by client/channel/product category). Support CR templates, required fields (rationale, impact scope), and attachment of annotated comments. Provide preflight validation (e.g., missing cutout mask, out-of-range parameters) and compute predicted output deltas (file size, aspect ratio, background color) to reduce review risk.

Acceptance Criteria
CR Submission via Template with Required Fields
Given an existing preset and at least one CR template is defined When the user initiates a Change Request and selects template "T" Then the form auto-populates fields per template "T" And submission is blocked until required fields (rationale, impact scope) are completed And inline validation messages identify each missing or invalid required field And upon successful submit, a CR with a unique ID and timestamp is created and linked to the preset
Human-Readable Parameter Diff Generation
Given a baseline preset version V and a proposed change set C When the user submits or saves the CR draft Then a human-readable diff is generated listing parameter name, old value, new value, units, and change type (added/updated/removed) And nested parameters are represented using dot notation and sorted consistently And unchanged fields are excluded; out-of-range values are highlighted And the diff is downloadable as PDF and JSON
Configurable Sample Set Previews
Given a sample set selection filtered by client, channel, and/or product category with N ≥ 3 assets When the CR is saved as draft or submitted Then before/after previews are rendered for ≥ 95% of assets within 120 seconds And each preview displays asset ID, baseline preset version, proposed version, and render time And preview failures surface an error code and allow retry And the chosen sample set persists with the CR
Preflight Validation at Submit
Given preflight rules for missing cutout mask, out-of-range parameters, and incompatible aspect ratio are enabled When the user clicks Submit on the CR Then validations execute and return a pass/fail summary within 5 seconds And blocking errors prevent submission; warnings require explicit acknowledgment to proceed And each issue displays rule ID, severity, affected parameter/asset, and suggested fix And the validation outcome is stored in the CR audit log
Predicted Output Deltas Computation
Given baseline output metrics for the selected sample set are available When previews are generated for the CR Then predicted deltas are computed per asset and aggregate for file size (KB), aspect ratio, background color, and color profile And any delta exceeding configured thresholds is flagged with a warning icon and tooltip And aggregate summary includes mean, median, and max delta values And deltas are included in the CR detail view and exportable as CSV
Annotated Comments and Attachments on Previews
Given a CR draft with generated previews When the user adds a comment with an annotation on a preview and attaches a file (PNG/JPG/PDF) up to 10 MB Then the annotation pins to specific coordinates on the preview and remains accurate across zoom levels And threaded replies with @mentions and timestamps are supported And attachments are virus-scanned and stored with access controls; failed scans block upload with an error message And all comments and edits are versioned in the CR activity log
Approval Workflow & Roles
"As a compliance reviewer, I want a structured approval workflow with role rules so that only properly reviewed presets can be promoted to signed versions."
Description

Introduce a configurable approval workflow with role-based rules: minimum number of approvers, required approver roles (e.g., Compliance, Brand), and optional code owner mappings by preset scope. Provide a unified review queue with in-app and email notifications, inline commenting, and actions to Approve, Request Changes, or Reject. Support SLA reminders, escalation, and audit-friendly capture of decisions and reviewer notes. Allow one-click “Approve & Sign” to advance a CR to a signed version.

Acceptance Criteria
Enforce Minimum Approvers and Required Roles
Given a workflow requires min_approvers=2 and required_roles=[Compliance, Brand] When a change request (CR) is submitted and approvals are collected Then the system blocks “Approve & Sign” until at least 2 unique approvers have approved And at least one approver with the Compliance role and one with the Brand role have approved And the CR author’s approval does not count toward the minimum And a user cannot submit more than one approval for the same CR And the UI displays which role requirements remain unmet
Code Owner Auto‑Assignment by Preset Scope
Given a CR is created for a preset whose scope matches a code owner rule When the CR is created Then mapped code owners are auto-assigned as required reviewers with their roles indicated And they appear in the reviewers list as Pending And in‑app and email notifications are sent to assigned reviewers within 60 seconds And an audit entry records the auto-assignment and notifications with timestamps
Unified Review Queue with Filters and SLAs
Given I have review permissions When I open the Review Queue Then I see CRs assigned to me or to my roles with status, age, and SLA due time And I can filter by role, status (Pending, Changes Requested, Rejected, Signed), SLA (Due Soon, Breached), and scope And the queue updates within 5 seconds when a CR assignment to me changes And result counts match the applied filters
Inline Commenting on Diffs and Previews
Given a CR displays auto-generated diffs and sample previews When I add a comment anchored to a specific change or preview Then the comment is threaded, timestamped, and attributed to the author And I can @mention users who receive in‑app and email notifications within 60 seconds And comments are immutable with edits allowed for 5 minutes; all edits retain visible history And all comments and resolutions are captured in the audit log
Review Actions and State Transitions
Given a pending CR assigned to my role When I choose Approve Then my approval is recorded with my role and timestamp and the CR remains Pending until all requirements are met When I choose Request Changes and provide a note Then the CR moves to Changes Requested and the author is notified; signing is blocked until a new revision is submitted When I choose Reject and provide a reason Then the CR moves to Rejected, is locked from signing, and reviewers are notified
SLA Reminders and Escalation
Given a workflow SLA of 48 hours is configured When a CR remains pending without all required approvals Then reminders are sent to outstanding reviewers at T−24h and T−1h And upon SLA breach, an escalation email is sent to the escalation group and an in‑app banner appears on the CR And all reminders and escalations are logged with recipients and timestamps
One‑Click Approve & Sign to Create Signed Version
Given I am the final required approver on a CR When I click Approve & Sign Then my approval is recorded and a new signed preset version is created with an incremented version number And the signed version stores signature metadata (signer, roles, timestamp, CR ID, workflow policy hash) And the CR status updates to Signed and is locked from further edits And in‑app and email notifications are sent to the CR author and watchers
Signed-Version Enforcement in Exports
"As an operations lead, I want exports to run only against signed preset versions so that outputs remain consistent and compliant across teams and channels."
Description

Enforce that only approved, cryptographically signed preset versions can be used by export jobs across UI, API, and CLI. Require explicit pinning of a signed version per client and channel to prevent drift, and block execution if an unsigned or superseded version is referenced (with override policies configurable by admins). Embed a checksum/signature in job manifests and verify at run time to detect tampering. Provide clear error messaging and remediation guidance to maintain consistent outputs.

Acceptance Criteria
UI Export Submission Blocks Unsigned or Superseded Preset
Given a user is creating an export in the Web UI with clientId C and channelId H And the selected presetVersionId V is unsigned or marked superseded When the user submits the export job Then the submission is rejected and no job is enqueued And the UI displays an error banner with code in [PRESET_NOT_SIGNED, PRESET_SUPERSEDED] And the message includes: presetId, presetVersionId, currentApprovedVersionId (if any), and links/actions to Request Approval and Update Pin And an audit event "ExportBlocked" is recorded with actorId, clientId, channelId, presetId, presetVersionId, reasonCode, timestamp
API Export Request Validates Signed-Version Pinning
Given a POST /v1/exports request contains a job manifest with clientId C, channelId H, presetId P, presetVersionId V And either presetVersionId is missing OR V is not signed OR V is marked superseded When the API validates the request Then it responds 400 with application/json body containing error.code in [PIN_REQUIRED, PRESET_NOT_SIGNED, PRESET_SUPERSEDED], error.details.presetId, error.details.presetVersionId, error.details.clientId, error.details.channelId, and remediation.url And no job record is created or queued And an audit event "ExportRejected" is recorded with the same identifiers
CLI Export Enforces Signed-Version and Structured Errors
Given a user runs `pixelift export` with a manifest or flags specifying clientId C, channelId H, presetId P, and presetVersionId V that is unsigned or marked superseded When the command executes Then the process exits with non-zero status 2 And stderr outputs a single-line JSON object with fields: code, message, presetId, presetVersionId, clientId, channelId, remediation And no output files are written and no remote job is started
Runtime Manifest Signature Verification and Tamper Detection
Given a queued export job includes a manifest with embedded signature S over presetVersionId V and parameters And V is signed and not superseded When the worker starts the job Then it verifies S against the Lockbox trust root using the approved algorithm and records verification_result=pass And processing begins And if verification fails (signature missing, invalid, or mismatched), Then the job is immediately cancelled before any external writes, marked failed with code PRESET_SIGNATURE_INVALID, and an audit event "SignatureVerificationFailed" is recorded
Per-Client and Per-Channel Signed-Version Pinning Required
Given the system maintains a pin table mapping (clientId, channelId, presetId) -> signed presetVersionId When an export is submitted without an existing pin for (C,H,P) Then the submission is rejected with error code PIN_REQUIRED (UI: blocked, API: HTTP 400) And when a pin exists but references a version marked unsigned or superseded, the submission is rejected with code PIN_INVALID And when a valid pin exists, the job uses that pinned presetVersionId exactly, overriding any different version supplied by the requester
Admin Override Policy for Superseded/Signed-Version Exceptions
Given an admin has configured a policy allowSupersededRun with TTL days and scope over clientId/channelId And the requester has Admin privileges and provides override=true with a non-empty overrideReason When submitting an export that references a superseded presetVersionId within the TTL window Then the job is accepted and enqueued with policyOverride=true and overrideReason captured And an audit event "PolicyOverrideUsed" is recorded with actorId, clientId, channelId, presetId, presetVersionId, and TTL remaining And if the requester lacks Admin scope or the TTL has expired, the submission is rejected with status 403 and code POLICY_OVERRIDE_DENIED
Audit Logging of Signature Verification and Policy Decisions
Given any export submission, validation outcome, or runtime verification result When the event occurs Then an immutable audit record is written containing: eventType, requestId, actorId (or client credentials id), clientId, channelId, presetId, presetVersionId, signatureDigest, verificationResult, policyOverride flag and reason (if any), errorCode (if failed), and timestamp And audit records are retrievable via GET /v1/audit?requestId={id} within 5 seconds of the event And audit records are read-only and include a hash chain or sequence number to detect tampering
Rollback & Freeze Controls
"As a production manager, I want fast rollback and freeze capabilities so that I can quickly restore a known-good preset during incidents or lock configurations during critical launches."
Description

Provide one-click rollback to a previously signed version with automatic propagation to linked clients/channels while respecting local overrides. Support “freeze” states that prevent further CRs on critical presets during campaigns or audits. Optionally auto-rollback on failure signals (e.g., QC threshold breaches, spike in rejection rate) with notifications and postmortem links. Ensure safe rollbacks via dependency checks and preview confirmation.

Acceptance Criteria
One-Click Rollback to Signed Preset Version
Given a preset has at least one previously signed version and the user has Preset:Rollback permission And a target signed version is visible in version history When the user clicks Rollback on the selected signed version and confirms Then that version becomes the active signed version within 10 seconds And new exports triggered after activation use the rolled-back version And rollback controls are disabled for unsigned/draft versions And a success message shows the new active version ID and timestamp
Propagation to Linked Clients/Channels with Local Overrides
Given preset P is linked to clients C1..Cn and channels CH1..CHm, with some links having local overrides When P is rolled back to signed version Vx Then Vx becomes the active base version for all links with inherit base version = on within 60 seconds And local override parameters on each link remain unchanged And links with inherit base version = off are not updated And unrelated clients/channels are unaffected And an activity entry per link records before/after base versions
Freeze State Blocks Change Requests and Approvals
Given the user has Preset:Freeze permission on a preset When the user applies Freeze with reason and optional expires_at Then the preset displays a Frozen badge with reason and expiry (if set) And creation of new change requests is blocked with an explanatory error And approval/merge of existing open change requests is blocked while frozen And only users with Preset:Unfreeze can unfreeze prior to expiry And at expires_at the preset auto-unfreezes and logs the event
Auto-Rollback on Quality Signal Breach with Notifications
Given auto-rollback is enabled for preset P with thresholds (QC_score < T for N consecutive samples OR RejectionRate > R% over 1h) And a previous signed good version Vgood exists When monitoring detects a breach meeting configured thresholds Then the system rolls back P to Vgood within 5 minutes of detection And notifications are sent to configured channels with breach metrics and links to diffs, previews, and postmortem template And an audit log entry records trigger details and outcome And if no prior signed version exists, no rollback occurs and a No Safe Version alert is sent
Dependency Checks Gate Rollback Execution
Given preset P depends on resources with version constraints (e.g., LUTs, brush packs) When a user initiates rollback to version Vx Then the system evaluates dependency compatibility for Vx before execution And if any incompatibilities are detected, the rollback is blocked and a list of failing dependencies with remediation hints is shown And if all dependencies are compatible, rollback proceeds
Preview and Diff Confirmation Before Rollback
Given at least 3 sample input images are configured for the preset When a user initiates rollback to version Vx Then the system generates side-by-side previews and a parameter diff between current and Vx And rollback executes only after explicit user confirmation And if preview generation fails, rollback is not executed and the user is offered retry options
Audit Trail and Delivery of Event Notifications
Given any rollback, auto-rollback, freeze, or unfreeze action occurs When the action completes Then an immutable audit record is created with actor/system, timestamp, action type, reason, before/after version IDs, affected links, and outcome And the record is queryable via API and visible in the preset timeline And notifications are delivered to email/Slack/webhook within 60 seconds containing a link to the audit record
Audit Trail & Compliance Reporting
"As a compliance officer, I want a complete audit trail and reports so that I can demonstrate control over image outputs and meet audit requirements."
Description

Record an end-to-end audit trail of all preset activities, including CRs, diffs, comments, approvals, signatures, rollbacks, and export jobs with the exact signed version used. Capture actor identity, timestamps, IP/device, and change rationale. Provide exportable reports (CSV/JSON), scheduled compliance summaries, and dashboards to track approval SLAs, drift incidents, and usage by client/channel. Support retention policies and data residency settings.

Acceptance Criteria
CR Audit Entry with Complete Metadata
Given a permitted user submits a preset change request with diffs and sample previews via UI or API When the request is saved Then an immutable audit record is written with fields: tenant_id, preset_id, cr_id, prior_version_id, proposed_version_hash, diff_summary, sample_preview_ids, rationale, actor_user_id, actor_role, org/team, timestamp_utc (ISO 8601), actor_ip, device_fingerprint, request_origin And the audit record is append-only with a tamper-evident hash that links to the previous audit event for that preset And the audit record is retrievable via UI and API within 5 seconds of submission And if any mandatory field is missing, the save is rejected with a validation error and no audit record is created
Approval, Signature, Comment, and Rollback Traceability
Given a pending change request When a reviewer approves with required re-authentication within 5 minutes and applies an e-signature Then the audit trail records an approval event with reviewer_user_id, role, timestamp_utc, actor_ip, device_fingerprint, signature_method, signature_hash, approved_version_id, approved_version_hash, and a link to the originating cr_id When a reviewer comments or requests changes Then the audit trail records the comment text, mentions, attachment_ids, timestamp_utc, and actor metadata When a rollback to a prior signed version is executed Then the audit trail records a rollback event with actor metadata, timestamp_utc, rationale, from_version_id/hash, to_version_id/hash, and linkages to prior approval And the UI/API renders the causal chain CR -> approval -> signed_version -> rollback in chronological order
Export Jobs Reference Exact Signed Preset Version and Block Drift
Given an export job is triggered (manual or scheduled) for a specific client and channel When the job starts Then the audit trail records job_id, trigger_source (user/schedule/api), client_id, channel_id, environment, start_timestamp_utc, and the exact signed preset version_id/hash used And the job record includes asset_counts (attempted/succeeded/failed), end_timestamp_utc, duration_ms, worker_id, job_config_checksum, and per-batch version references if multiple presets are used When an export attempts to use an unsigned or superseded preset version Then the job is blocked, zero assets are processed, and a drift_incident is recorded with severity, actor identity, timestamp_utc, attempted_version_hash, and required_policy_ref And the drift incident is visible on dashboards and APIs within 60 seconds
On-Demand Audit Report Export (CSV/JSON) with Schema and Performance Guarantees
Given a reporting user selects a date range and filters (tenant, client, channel, preset, action_type, actor) and chooses CSV or JSON When the export is requested Then the system returns a downloadable file with a stable schema including: event_id, tenant_id, client_id, channel_id, preset_id, version_id/hash, event_type, actor_user_id, actor_role, ip, device_id, timestamp_utc, rationale, related_ids (cr_id, job_id), outcome, checksum And for up to 100,000 events, the export completes within 60 seconds and includes a SHA-256 checksum and record_count And pagination tokens are returned for larger result sets and produce non-overlapping, complete coverage And access is RBAC-enforced and the export action itself is audit-logged And the row count in the file equals the count shown in the on-screen preview
Scheduled Compliance Summaries Configuration and Delivery
Given an admin configures a scheduled compliance summary (cadence, timezone, recipients, filters, KPIs) and tests delivery When the schedule is saved Then a test summary can be sent on demand and delivery is audit-logged with message_id and timestamp_utc When a scheduled run occurs Then recipients receive the summary within 10 minutes containing KPIs: approval SLA compliance %, average approval time, overdue approvals, drift incident count and MTTR, rollback count, exports by client/channel, and top actors by changes And a CSV attachment or secure link provides the underlying events for the period And delivery failures are retried up to 3 times with exponential backoff and, on final failure, a webhook/alert is emitted and logged
Compliance Dashboard KPIs, Filtering, and Drill-Down
Given a compliance user opens the dashboards When they apply filters (date range, tenant/client/channel, preset, actor) or change SLA target thresholds Then KPI tiles update within 2 seconds and reflect data no older than 5 minutes And tiles include: Approval SLA %, Avg approval lead time, Open approvals, Drift incidents (count by severity), Rollbacks, Exports by client/channel, Drift MTTR And each tile supports drill-down to a paginated list of underlying audit events, and selecting an event opens its full audit entry And RBAC ensures users only see authorized tenants/clients/channels; unauthorized filters are disabled and attempts are logged
Retention Policy and Data Residency Enforcement
Given a tenant admin sets data retention policies per event class (audit_events, previews, reports) with durations and selects permissible storage region(s) When policies are saved Then policy version, author, timestamp_utc are recorded and enforcement limits storage to selected region(s) When data reaches retention thresholds Then records are deleted or pseudonymized within 24 hours; purge jobs are audit-logged with counts, ids, and errors And legal holds can be applied to scoped entities (preset, client, channel) to suspend deletion until removed, with justification captured and logged And all exports and report deliveries honor data residency by serving from the configured region; cross-region transfers are blocked and logged
API & Webhooks for Preset Governance
"As a platform engineer, I want APIs and webhooks for the preset governance flow so that I can integrate approvals and enforcement into our existing tooling and automations."
Description

Expose secure APIs to programmatically manage preset lifecycle: create CRs, fetch diffs and previews, approve/sign, rollback/freeze, and query signed version mappings per client/channel. Provide event webhooks (CR created, approved, signed, rollback, export blocked) with retry/backoff, idempotency keys, and OAuth2 scopes. Include rate limiting, pagination, and SDK snippets to integrate PixelLift governance into CI/CD and external DAM/PIM systems.

Acceptance Criteria
Create Change Request via API (OAuth2, Idempotency)
Given a valid OAuth2 access token with scope presets:write When the client POSTs /v1/presets/{preset_id}/change-requests with a valid payload and Idempotency-Key header Then the API responds 201 Created with body {cr_id, preset_id, base_version_id, status:"pending_review", submitted_by, created_at} and a Location header And an audit log entry is recorded for the CR with actor, timestamp, and IP When the exact same request (same Idempotency-Key) is replayed within 24h Then the API responds 200 OK with the same cr_id and header Idempotent-Replayed:true When the payload is invalid or references a non-existent preset Then the API responds 422 Unprocessable Entity with machine-readable error codes and fields When the token is missing or lacks presets:write Then the API responds 401/403 accordingly and no CR is created
Fetch Diffs and Previews for Change Request
Given an existing change request and a valid access token with scope presets:read When the client GETs /v1/change-requests/{cr_id}/diff Then the API responds 200 OK with an array of changes including path, before, after, and summary counts When the client GETs /v1/change-requests/{cr_id}/previews Then the API responds 200 OK with signed URLs that expire in 15 minutes, format, dimensions, channel, and generated_at And at least one preview exists per target channel declared in the change request When previews are still rendering Then the API responds 202 Accepted with progress.percent and Retry-After <= 30 seconds When previews exceed 25 items Then results are paginated via cursor with page[size] up to 100 and Link headers for next
Approve/Sign and Rollback/Freeze Governance Actions
Given a reviewer with scope presets:approve When the reviewer POSTs /v1/change-requests/{cr_id}/approve with an Idempotency-Key Then the change request status becomes approved and a signed preset version is created with immutable version_id and sha256 And only signed version_ids are eligible for export; attempts to export non-signed versions are rejected And webhooks cr.approved and preset.signed are emitted When the reviewer POSTs /v1/presets/{preset_id}/rollback with target version_id Then the signed pointer moves to the target version, a new audit entry is recorded, and webhook preset.rolled_back is emitted When the account freezes a preset via POST /v1/presets/{preset_id}/freeze {frozen:true} Then attempts to create new change requests for that preset return 423 Locked with code PRESET_FROZEN When the reviewer lacks scope or the change request is not in a reviewable state Then the API responds 403/409 accordingly and no signature state changes
Query Signed Version Mappings per Client/Channel
Given a valid access token with scope presets:read When the client GETs /v1/mappings?client_id={client_id}&channel={channel} Then the API responds 200 OK with items {client_id, channel, preset_id, version_id, updated_at, signed_by} And the response supports ETag/If-None-Match and returns 304 Not Modified when unchanged And results can be filtered by preset_id and are sorted by client_id,channel And mapping changes propagate and are visible to reads within 60 seconds
Webhook Delivery Reliability and Idempotency
Given a registered webhook endpoint and events cr.created, cr.approved, preset.signed, preset.rolled_back, export.blocked enabled When any such event occurs Then PixelLift POSTs application/json with fields {id, type, occurred_at, tenant_id, resource} and headers X-Event-Id and X-Idempotency-Key And deliveries are signed with HMAC-SHA256 in X-Signature using the tenant's secret And retries follow exponential backoff starting at 30 seconds, doubling up to 24 hours, with a maximum of 9 attempts, and stop on any 2xx And duplicate deliveries (same X-Event-Id) are idempotent and must be deduplicated by receivers And events for the same resource are delivered in order
Export Blocked Event for Unsigned Preset in Export Pipeline
Given a user attempts to create an export referencing an unsigned preset or an approved-but-unsigned change request When the client POSTs /v1/exports with a preset_id or version_id that is not signed Then the API responds 422 Unprocessable Entity with code PRESET_UNSIGNED and details including preset_id/version_id And no export job is created And an export.blocked webhook is emitted referencing the attempted resource and reason PRESET_UNSIGNED
Rate Limiting and Pagination Across Governance Endpoints
Given any governance API request Then responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers When a client exceeds the limit within the window Then subsequent requests receive 429 Too Many Requests with Retry-After indicating seconds to wait When listing resources (change requests, previews, mappings) Then the API supports cursor-based pagination with page[after] and page[size] (default 25, max 100) and returns RFC 5988 Link headers for next/prev And pagination is stable and deterministic when new items are created concurrently by sorting by created_at, id

Approval Matrix

Route work through the right sign‑off path automatically. Build rules like “Amazon main images require 2‑step review” or “>500 SKUs needs Senior Approver + Finance.” SLA timers, reminders, and escalation keep launches on track, while export gates prevent go‑lives until required steps are completed or properly overridden with justification.

Requirements

Visual Rule Builder for Multi-Step Approvals
"As an operations manager, I want to compose approval rules visually so that the right steps and approvers are applied consistently without manual routing."
Description

Provide a no-code builder to define conditional, multi-step approval paths based on attributes such as marketplace (e.g., Amazon, eBay), image type (main, gallery), SKU count thresholds, category, price band, tags, account, and quality flags. Support sequential and parallel steps, minimum approvals per step, role- or group-based assignees, rule precedence/priority, versioning, draft/test modes, validation of conflicting rules, and a simulator to preview which path a batch would take. Ensure multi-tenant isolation, templateable rule sets, and safe rollout with staged activation.

Acceptance Criteria
Build Conditional Rule Using Attributes
Given I am in the Visual Rule Builder in Draft mode within my tenant When I define a rule with conditions on marketplace=Amazon, image type=Main, category in ["Shoes"], SKU count > 500, price band=Premium, tags contains "Launch", account="SellerA", and quality flags include "NeedsRetouch" Then the rule saves successfully and displays a readable summary of all conditions Given I specify numeric thresholds When I enter operators (>, >=, <, <=, between) and lists (in, not in) Then the builder validates value types and operators and prevents saving invalid combinations Given I define a condition that is syntactically correct but references an unknown attribute or enum value When I attempt to save Then the builder blocks save with a clear validation error highlighting the offending field
Configure Sequential and Parallel Steps with Min Approvals
Given an existing rule When I add Step 1 "Catalog Review" (sequential) and Step 2 with parallel branches "Senior Approver" and "Finance" and set minimum approvals per step/branch Then the builder persists the topology and shows step order and parallelism Given a configured minimum approvals count When I enter a value greater than the number of unique eligible assignees in that step/branch Then the builder blocks save and explains the inconsistency Given a parallel step with two branches each requiring min approvals=1 When the rule executes Then both branches must reach the required min approvals before the workflow proceeds
Assign Roles/Groups and Enforce Min Approvals
Given a step configured with assignees by role and/or group When I select roles/groups Then only roles/groups from my tenant are listable and selectable Given a step with min approvals=2 and three eligible users in the selected group When a batch triggers the rule Then the step is visible to the group and the system requires two distinct approvals to satisfy the step Given a step has no resolvable eligible assignees at publish time When I attempt to publish the rule Then publish is blocked with a validation error indicating missing assignees
Set Rule Priority and Block Conflicts on Publish
Given two rules whose conditions can both match the same batch When I set different priority values Then the rule with the higher priority (numerically lower value) is selected during evaluation Given two active (or to-be-activated) rules share equal priority and have overlapping conditions When I attempt to publish the second rule Then the system detects the overlap and blocks publish until I change priority or narrow conditions Given I change rule priorities When I publish Then the new precedence is applied to subsequent evaluations without altering in-flight batches
Versioning with Draft, Test Mode, and Scheduled Activation
Given an active rule version v1 When I edit it Then a new Draft version v2 is created while v1 remains active Given v2 in Test mode When enabled for shadow evaluation Then it logs matched batches and predicted paths without affecting routing Given v2 is scheduled for activation at a future timestamp T When T is reached Then v2 becomes Active and v1 is archived, and in-flight batches continue under v1
Simulate Approval Path for a Sample Batch
Given a saved rule set When I open the simulator and input marketplace=Amazon, image type=Main, SKU count=600, category=Shoes, price band=Premium, tags=["Launch"], account="SellerA", quality flags=["NeedsRetouch"] Then the simulator displays the matched rule name and priority, the full approval path (steps, branches), required min approvals, and assignee roles/groups Given I toggle an attribute so that no rules match When I run the simulator Then it indicates "No matching rule" and no path is generated Given a rule includes export gate or override options When I simulate Then the simulator shows the export gate outcome and whether override is permitted
Enforce Multi-Tenant Isolation and Tenant-Scoped Templates
Given a user in Tenant A When they create rules or templates Then those entities are visible and executable only within Tenant A Given a template created in Tenant A When a user uses it to create a new rule set Then an independent Draft copy is created scoped to Tenant A Given a user from Tenant B When they search, preview, simulate, or execute Then rules, templates, users, groups, and logs from Tenant A are not discoverable or accessible
Workflow Binding to Batches, SKUs, and Variations
"As a release coordinator, I want approvals to bind automatically to batches and variations so that the correct review path runs whenever listings change."
Description

Automatically attach and evaluate approval rules when new batches, SKU groups, or A/B image variations are created or updated in PixelLift. Persist workflow state on the job object, re-trigger steps when edits materially change assets, support partial approvals for large batches, and ensure idempotent, asynchronous processing. Expose current approval state in UI and API, handle backfill for existing jobs, and guarantee no deadlocks or orphaned steps when items are added/removed mid-flow.

Acceptance Criteria
Auto-Binding on New Batch/SKU/Variation Creation
Given a new job (batch, SKU group, or variation set) is created with marketplace, image_role, and sku_count metadata When the job is saved Then the system evaluates Approval Matrix rules and attaches the correct workflow_template_id and step instances to the job within 3 seconds, persisting job.workflow_state {status: "pending", version, selected_rule_id} And only one workflow is attached per job (no duplicate step instances) even if the save is repeated or duplicate events are received And if multiple rules match, the highest-priority rule is selected and selected_rule_id is stored in the audit log And on transient evaluation errors the system retries up to 5 times with exponential backoff; on final failure job.workflow_state.status = "error", error_code set, and a notification is emitted
Expose Workflow State via UI and API
Given a job with an attached workflow When a user opens the Job Details screen Then the UI shows per-item step status counts (Pending/Approved/Rejected/Skipped/Overridden), current step name, SLA remaining time, and escalation flags, updating within 1 second of a status change And step-level audit entries (actor, action, timestamp, justification) are visible Given an API client requests GET /v1/jobs/{job_id}/workflow When the job exists Then the response is 200 with JSON containing {workflow_state: {status, version, selected_rule_id, steps: [{step_id, name, level, required, sla_seconds, status, started_at, completed_at, escalated_at}], items: [{item_id, step_statuses[]}]} } And ETag is returned and reflects changes to workflow_state
Material Change Re-Triggering and Rule Re-Evaluation
Given a job with approved or pending steps When a material change occurs (asset file hash changes OR marketplace changes OR image_role changes OR SKU membership of SKU group changes) Then the system re-evaluates rules and updates the workflow within 3 seconds And if the selected rule changes, the old workflow is superseded and new required steps are created; only unaffected previously-approved steps explicitly marked as reusable remain; all others reset to Pending And affected items/steps that reset generate audit entries and notifications Given a non-material change (e.g., title or tag not referenced by rules) When saved Then no steps re-trigger and prior approvals remain intact
Partial Approvals and Export Gates
Given a batch with 1,000+ items under a multi-step workflow When approvers action a subset of items Then those items transition independently through steps without blocking others, and per-item statuses are persisted Given a user attempts to export images When some items have unmet required steps Then the export is blocked with HTTP 409 (API) or an in-UI gate showing blocking steps and counts And the user may export only the Approved subset successfully Given an authorized user invokes an override When they provide a justification of at least 15 characters and a reason code Then the gate is bypassed for the selected items, status becomes Overridden, and the audit log records actor, reason code, and justification
Idempotent Asynchronous Processing and Concurrency Safety
Given duplicate create/update events for the same job are enqueued concurrently When the worker processes them Then exactly one set of step instances is created and the others are no-ops, enforced via an idempotency key (job_id + workflow_version + step_type + item_id) And no deadlocks occur; concurrent workers complete without timeouts, and job.workflow_state remains consistent And 95th percentile end-to-end workflow binding time is ≤ 5 seconds for a 10,000-item batch And retryable failures are retried with backoff; non-retryable failures surface as job.workflow_state.status = "error" with error_code and are alertable
Backfill of Existing Jobs
Given the feature flag "approval_workflow_binding" is enabled for an account with existing jobs lacking workflows When a backfill is initiated in dry-run mode Then a report is produced listing candidate jobs, matched rules, and projected step counts without mutating data When the backfill is executed Then workflows are attached only to jobs without an existing workflow, honoring rule priorities, at a controlled rate (≤ 100 jobs/minute) And previously completed/approved content is not invalidated; only jobs missing workflows are updated And progress metrics (processed, succeeded, failed) are emitted and visible via API/UI, and failures include error_code and remediation hints
No Orphaned Steps on Items Added/Removed Mid-Flow
Given a workflow is in progress on a job When new items are added to the job Then required step instances for those items are created to match the current workflow, and SLA timers start at creation When items are removed from the job Then any pending steps for those items are canceled with status = "canceled", SLA timers stop, and counts update within 1 second And no orphaned steps reference non-existent items (referential integrity check passes), and remaining items continue unaffected
Role and Permission Directory for Approvers
"As an admin, I want to manage approver roles and permissions so that only authorized people can approve, override, or escalate tasks."
Description

Define approver roles (Reviewer, Senior Approver, Finance) and groups with tenant scoping, marketplace-specific capabilities, and granular permissions for approve/reject/return/override. Integrate with SSO/SCIM for user provisioning, support out-of-office substitutions, coverage windows, and fallback groups when no eligible approver is available. Enforce permissions across UI, API, notifications, and override gates.

Acceptance Criteria
Role Creation with Tenant and Marketplace Scoping
Given I am a Tenant Admin in Tenant A When I create approver roles named Reviewer, Senior Approver, and Finance with explicit action permissions (approve, reject, return, override) and select marketplace scope (e.g., Amazon only for Reviewer, Amazon + eBay for Finance) Then the roles are saved and visible only within Tenant A and their action permissions are recorded per marketplace And when I assign the Reviewer (Amazon-only) role to user U1 Then U1 can take actions only on Amazon-scoped approval tasks and cannot act on eBay tasks (UI action controls hidden/disabled; API returns 403 PERMISSION_DENIED) And when the same role names exist in Tenant B Then U1 cannot view or reference Tenant B roles or approval tasks (no cross-tenant leakage) And when I create group G1 with Finance role scoped to Amazon + eBay and add users U2 and U3 Then U2 and U3 inherit all Finance permissions for those marketplaces and can action accordingly
Permission Enforcement Across UI, API, Notifications
Given user U1 has approve and return but not reject or override permissions for Amazon When U1 opens an Amazon approval task in the UI Then Approve and Return controls are enabled, Reject and Override controls are not rendered (or disabled with tooltip reason) And when U1 attempts the missing actions via API Then the API responds 403 PERMISSION_DENIED with action-specific error codes (e.g., ACTION_REJECT_NOT_ALLOWED, ACTION_OVERRIDE_NOT_ALLOWED) And notifications sent to U1 for this task include only links for allowed actions; disallowed action links are omitted And when U1 views an eBay task without any eBay permissions Then no actionable controls are shown and API action attempts return 403 with marketplace_scope_violation And all denied attempts are captured in the audit log with user, action, marketplace, timestamp, and reason
SSO/SCIM User and Group Provisioning
Given SCIM is configured for Tenant A with group-to-role mappings (e.g., scim:finance -> Finance role; scim:reviewer -> Reviewer role) and tenantId scoping When an IdP sends a SCIM Create for user U4 with groups [scim:finance] and tenantId=TenantA Then U4 is created within 5 minutes, assigned Finance role per mapping, and can act per marketplace scopes defined on the mapped role(s) And when the IdP updates U4 groups to [scim:reviewer] Then U4 loses Finance permissions and gains Reviewer permissions within 10 minutes (tokens reflect updated claims on next use; new sessions honor changes immediately) And when the IdP deactivates U4 Then U4 is deprovisioned within 5 minutes: login disabled, active sessions revoked, API tokens invalidated, and U4 cannot receive actionable notifications And SCIM Create/Update are idempotent; repeated messages do not duplicate users or roles And SCIM requests with mismatched tenantId or missing required attributes respond 422 with error details and no changes applied And all SCIM-driven changes are written to the audit log with source=SCIM and correlation ids
Out-of-Office Substitution and Coverage Windows
Given Approver A defines an out-of-office period from 2025-10-01 00:00 to 2025-10-07 23:59 with delegate D and coverage window 09:00–17:00 evaluated in the tenant's default timezone When an approval task targeting A is created during this OOO period at 10:15 local time Then the task is routed to delegate D within 1 minute and D receives the actionable notification; A receives FYI only (no actionable links) And any action taken by D is recorded as performed on behalf of A in audit entries and task history And when a task targeting A is created at 18:05 during OOO Then it is queued and delivered to D at the next coverage window start (09:00) And when the OOO period ends Then new tasks route back to A; in-flight tasks assigned to D remain with D unless manually reassigned by an admin And if delegate D is also OOO with no further delegate configured Then routing falls back to the configured fallback group for the step
Fallback Group Routing When No Eligible Approver
Given an approval step requires Senior Approver + Finance for jobs with >500 SKUs and has a fallback group FG1 configured When a qualifying job enters the step and no eligible Senior Approver is available (none assigned, deprovisioned, or all OOO without delegates) Then the task is assigned to FG1 within 1 minute and FG1 members receive actionable notifications And if the step's configured SLA elapses without action Then an escalation notification is sent to the step owner and Tenant Admins, and the task is marked as Breached in reporting And the export gate remains blocked until the step is approved by an eligible user in FG1 or an authorized override is executed with justification And all fallback assignments and escalations are recorded in audit history with timestamps and resolution outcome
Override Gate with Required Justification and Audit
Given user U5 has the Override permission for Amazon and user U6 does not When U6 attempts to override an Amazon export gate via UI or API Then the UI disables the override control with a reason tooltip and the API responds 403 ACTION_OVERRIDE_NOT_ALLOWED And when U5 initiates an override on an Amazon job pending approval Then the system requires a justification text (minimum 20 characters) and a reason code selection before proceeding And upon successful override, the export gate opens immediately, approvers are notified of the override event, and the task is marked Overridden with the recorded justification And the audit log contains immutable entries with user, marketplace, step, justification, reason code, timestamp, and before/after state And override is blocked for marketplaces not included in U5's scope (API/UI enforcement consistent)
SLA Timers and Reminder Scheduler
"As a workflow owner, I want to set SLAs and automated reminders so that reviews happen on time and launches stay on schedule."
Description

Enable per-step SLA definitions with business calendars, time zones, and pause/resume when items are returned for changes. Drive automated reminders via email, in-app, and Slack/MS Teams with configurable cadence, digests, and snooze. Surface countdowns and SLA status in the UI, emit webhooks for reminders and breaches, and log all timer events for reporting.

Acceptance Criteria
Per-step SLA using business calendars and time zones
Given a step SLA of 8 business hours with a business calendar (Mon–Fri 09:00–18:00) in time zone "US/Pacific", When an item enters the step at 16:00 PT on Friday, Then the due time is 15:00 PT on Monday. Given the business calendar includes a holiday on Monday, When the same item enters at 16:00 PT on Friday, Then the due time is 15:00 PT on Tuesday. Given a DST transition occurs within the SLA window, When an item’s due time is calculated, Then the due time reflects the correct local wall-clock time and total business-hours duration is preserved. Given the step’s calendar or time zone is changed after the item enters and before breach, When the configuration is saved, Then the due time is recalculated according to the new settings and a "due_time_recalculated" event is logged.
Pause and resume SLA when returned for changes
Given an item is in an active approval step with an SLA timer running, When the item is returned for changes, Then the SLA timer pauses and remaining_seconds is frozen. Given the paused item is updated and re-submitted to the same step, When it re-enters the step, Then the SLA timer resumes with the previously remaining_seconds. Given multiple pause/resume cycles occur, When the item finally completes the step, Then total_elapsed excludes all paused durations. Given an item is paused, When the current time passes the prior due time, Then no SLA breach occurs while paused. Given the assignee, business calendar, or time zone changes while paused, When the timer resumes, Then the due time is recalculated using the new configuration and a pause/resume event pair is logged.
Multi-channel reminders and escalation policy
Given email, in-app, Slack, and MS Teams are enabled for reminders, When remaining time crosses configured thresholds (e.g., T-24h, T-2h) or the SLA becomes overdue, Then reminders are sent on all enabled channels to current assignees. Given a reminder is sent, When recipients view it, Then the message contains item identifier, step name, due time in the recipient’s time zone, remaining time, and a deep link to the item. Given quiet hours are enabled in the business calendar, When a reminder would be sent outside business hours, Then delivery is deferred to the next business window unless "urgent_after_hours" is enabled. Given an escalation rule of 4 hours after breach to the "Senior Approver" group, When no acknowledgment is recorded within 4 hours after breach, Then an escalation notification is sent to the configured group and the escalation event is logged. Given channel-level retry policy (max 3 attempts, exponential backoff), When a reminder delivery fails, Then retries are attempted per policy and failures are logged with final status.
Digest scheduling and configurable cadence
Given a user has digest cadence set to Daily at 09:00 in their time zone, When multiple reminders are pending for that user, Then they receive a single consolidated digest at 09:00 listing all items with due times and priorities. Given digest mode is enabled for a channel, When items qualify for the digest window, Then individual reminders for those items are suppressed in that channel except for breach or escalation events if configured to bypass digests. Given a user updates their digest cadence or time zone, When the change is saved, Then the next digest is rescheduled accordingly within 5 minutes and reflected in the scheduler. Given recipients are in different time zones, When digests are generated, Then each recipient receives their digest at their local configured time.
Snooze controls for recipients
Given a recipient receives a reminder, When they snooze it for a duration within the allowed range (15 minutes to 48 hours), Then no further reminders or escalations for that item are sent to that recipient during the snooze window unless the step status changes to Completed. Given a reminder is snoozed, When the snooze expires or is canceled by the user, Then the next scheduled reminder is re-queued according to cadence without duplicating prior sends. Given multiple recipients exist, When one recipient snoozes, Then only that recipient’s notifications are suppressed; other recipients continue to receive reminders normally. Given "override_on_breach" is disabled, When the item breaches during a snooze, Then breach notifications to the snoozing recipient are suppressed until the snooze ends.
UI countdowns and SLA status indicators
Given an item is in an approval step, When viewed in list and detail views, Then a countdown shows remaining time in days/hours/minutes with statuses: On Track, At Risk (<= threshold), Paused, and Breached, using configured color codes. Given the countdown is displayed, When time passes, Then the countdown updates at least every 30 seconds without requiring a page reload. Given a user hovers or focuses on the countdown, When the tooltip appears, Then it shows the absolute due timestamp in the user’s time zone and the step’s native time zone, plus percentage elapsed. Given accessibility requirements, When using a screen reader or high-contrast mode, Then status labels are announced and contrast ratios meet WCAG AA. Given sorting by due time is applied in list view, When the dataset changes due to recalculation or pause/resume, Then the ordering updates to reflect the latest due times.
Webhooks and timer event logging for reporting
Given webhooks are enabled, When timer events occur (timer_started, timer_paused, timer_resumed, reminder_scheduled, reminder_sent, reminder_failed, snoozed, snooze_canceled, sla_breached, escalated, due_time_recalculated), Then a webhook is emitted for each with payload fields: event_id, event_type, item_id, step_id, tenant_id, occurred_at (UTC), due_time (UTC), remaining_seconds, recipients, channel (if applicable), and HMAC-SHA256 signature. Given webhook delivery, When the receiver returns non-2xx, Then retries occur with exponential backoff up to 5 attempts and a final failure is logged; duplicate deliveries can be deduplicated via event_id. Given timer-related actions occur, When events are logged, Then an immutable audit trail records each event with actor (system or user), timestamp (UTC), old/new values, and reason, and supports export to CSV for a specified date range and filters (tenant_id, item_id, step_id, event_type). Given a tenant disables webhooks, When timer events occur, Then no webhooks are sent while audit logging continues.
Escalation and Delegation Engine
"As a team lead, I want overdue or unowned tasks to escalate or delegate automatically so that bottlenecks are resolved without micromanagement."
Description

Define escalation paths triggered by SLA breaches, unassigned tasks, or workload thresholds. Support step-up to Senior Approver, skip-level escalation, time-bound delegation, out-of-office routing, and load-balanced assignment (round-robin or capacity-based). Prevent oscillation with cool-downs, record escalation reasons, and notify original owners and new assignees.

Acceptance Criteria
SLA Breach Step-Up to Senior Approver
Given a pending approval assigned to a Level 1 approver with a configured SLA And a Senior Approver is defined for the rule When the SLA timer expires without a decision Then the task is automatically reassigned to the Senior Approver And the escalation reason "SLA Breach" is recorded with timestamp, rule ID, previous owner, new owner, and task ID And the configured cool-down period is applied to this task to prevent repeated automated escalations And notifications are sent to the original owner and the new assignee
Skip-Level Escalation on Secondary SLA Breach
Given a task previously escalated to a Senior Approver with a configured secondary SLA When the secondary SLA expires without a decision Then the task is escalated to the skip-level approver as defined in the Approval Matrix And the system avoids assigning to the same person as the current owner; if identical, it selects the next eligible escalation target And the escalation reason "Secondary SLA Breach" is recorded And the configured cool-down period is applied to this task And both the previous and new owners are notified
Auto-Routing Unassigned Tasks After Grace Period
Given a task is created without an assignee And a grace period is configured When the grace period elapses and the task remains unassigned Then the system assigns the task to the default queue or approver per routing rules And if the primary target is out-of-office or at capacity, the system assigns to the next eligible target And the escalation reason "Unassigned Timeout" is recorded And notifications are sent to the team channel and the new owner
Load-Balanced Assignment (Round-Robin and Capacity-Based)
Rule 1: In round-robin mode with N equally available assignees, assignments rotate deterministically; over 100 tasks the distribution per assignee is within ±1 of 100/N. Rule 2: In capacity-based mode with weights [w1..wn] and all assignees available, over 200 tasks the distribution per assignee is within ±5% of wi/sum(w). Rule 3: Out-of-office or suspended assignees are skipped without breaking rotation fairness; when they return, rotation resumes including them. Rule 4: Capacity hard-limits are not exceeded; tasks that would exceed a limit are assigned to the next eligible assignee. Rule 5: Each assignment decision logs mode, inputs, selected assignee, and reason to the audit trail.
Time-Bound Delegation With Auto-Reversion
Given an owner configures a delegation to Delegate D from Start to End with a required justification When tasks are created or pending within [Start, End) Then they are assigned to Delegate D And when End is reached, new tasks route to the original owner And any tasks not yet accepted by Delegate D are automatically reassigned back to the original owner And the delegation window, justification, and changes are recorded; both parties are notified on start and end
Out-of-Office Routing to Backup
Given a user has an active out-of-office window with a designated backup B When a task would be assigned to the OOO user within that window Then the task is assigned to backup B And if B is also OOO or at capacity, the task is routed to the team queue or next eligible backup per rules And tasks already assigned to the user before OOO that are not yet accepted are reassigned to B; tasks in progress remain with the current owner And the routing reason "OOO" is recorded and notifications are sent to the OOO user and backup
Audit Trail and Notifications for Escalations and Delegations
Given any automated escalation, delegation, or re-routing event occurs When the event is executed Then an immutable audit entry is created capturing event type, reason code, triggering rule ID, previous owner, new owner, timestamp, SLA target and elapsed time, task ID, and actor (system/user) And notifications are sent to the previous owner, new owner, and watchers including task link, reason, next SLA deadline, and override instructions And duplicate notifications for the same task-event are suppressed within a 5-minute window And all audit records are exportable via API and visible in the task timeline
Export Gate Enforcement with Override Justification
"As a compliance officer, I want exports to be blocked until required approvals are complete or properly overridden so that we meet marketplace and internal policy requirements."
Description

Block export actions (UI and API) until mandatory approval steps are satisfied for the applicable rule path. Provide configurable override policies (who, when, and two-person rule), require structured reason codes and freeform justification with optional evidence attachments, and write gate decisions into the export manifest. Display clear block reasons, allow controlled bypass with audit, and ensure marketplace-specific gating differences are honored.

Acceptance Criteria
UI Export Block for Pending Approvals
Given a user selects assets for export targeting at least one marketplace and at least one selected asset has unmet mandatory approval steps for its applicable rule path When the user clicks Export Then the export is not initiated and no files or manifests are generated And the UI displays a blocking message listing each blocked asset with marketplace, rule name, and pending step names And the primary Export action remains disabled until all blockers are resolved or overridden And an audit event "export_blocked" is recorded with user ID, timestamp, asset IDs, marketplaces, and rule IDs
API Export Block and Error Contract
Given a valid API client requests an export for assets with any unmet mandatory approval steps for their applicable rule paths When the API call is made Then the service responds 409 Conflict with error_code "EXPORT_GATE_BLOCKED" And the response payload includes a list of blockers [{asset_id, marketplace, rule_id, pending_steps[]}] And no export job is created and no manifest is generated And a request_id is returned and the event is logged for audit
Policy-Controlled Override with Reason Codes and Evidence
Given an export is blocked and the signed-in user satisfies the configured override policy (role/team scope and allowed time window) When the user initiates an override for specific blocked items Then the system requires selection of a reason code from an admin-managed list and a freeform justification of at least 20 characters And allows up to 5 evidence attachments (PNG/JPG/PDF) totaling <= 25 MB And validates all inputs client- and server-side with clear error messages on failure And on submit creates an immutable audit record containing user ID(s), reason code, justification, attachment hashes, policy version, and affected items And immediately unblocks export only for the overridden items while non-overridden blockers remain blocked And if the policy forbids override in the current context, the action is rejected with a clear explanation
Two-Person Override Enforcement
Given the override policy for the applicable rule path enforces a two-person rule When Approver A submits an override Then the system records the override as Pending Counter-Sign and prevents Approver A from completing the second approval And only a distinct user who meets the policy may counter-sign And if Approver B counter-signs within the allowed window the export is unblocked; otherwise it remains blocked And all actions (request, counter-sign, reject, timeout) are auditable with user IDs and timestamps
Marketplace-Specific Gate Evaluation in Mixed Exports
Given an export batch targets multiple marketplaces with differing approval rule paths and statuses When gate evaluation runs Then gating is applied per marketplace and per asset according to that marketplace's rule path And the UI shows a summary of cleared vs blocked per marketplace And the user can proceed with Export Cleared Only (exports only cleared marketplace-asset combinations) or must resolve/override remaining blockers to Export All And API calls support mode=cleared_only to mirror this behavior And all excluded marketplace-asset combinations are reported back with precise block reasons
Gate Decision Metadata in Export Manifest
Given an export is executed When the manifest is generated Then for each asset-marketplace combination the manifest includes gate_status (cleared|overridden|blocked_excluded), rule_id, rule_version, override_used (boolean), override_reason_code (nullable), override_approvers [user_ids], override_timestamp (nullable) And manifest integrity is verified via checksum and stored with the export job And the manifest is retrievable via UI and API and immutable post-generation
Audit Trail and Compliance Reporting
"As a compliance analyst, I want a complete audit trail and SLA reports so that we can prove control adherence and improve the process over time."
Description

Capture an immutable event log for every approval, rejection, return, reminder, escalation, SLA breach, and override, including actor, timestamp, rule version, and justification artifacts. Provide search and filters by marketplace, batch, SKU, approver, and time window; export logs to CSV/JSON and stream via webhook. Offer dashboards for SLA compliance, bottleneck analysis, and override rates, with retention policies and tamper-evident hashing for compliance.

Acceptance Criteria
Immutable Event Capture Across Workflow Events
Given the Approval Matrix is enabled for a workspace When any of the following events occur: approval, rejection, return-for-changes, reminder-sent, escalation-triggered, SLA-breach, override-issued Then a log entry is appended within 2 seconds p95 containing: event_type, workspace_id, marketplace, batch_id, sku_id (optional), rule_id, rule_version, actor_id, actor_role, timestamp (UTC ISO8601), previous_status, new_status, justification_text (optional), justification_artifact_ids (optional), correlation_id And the log store is append-only; update/delete operations are disallowed and any attempt is recorded as a new audit_correction_attempt event And each entry is assigned a globally unique event_id and is readable via API/UI within 5 seconds p95 of occurrence
Search and Filter by Marketplace, Batch, SKU, Approver, Time Window
Given at least 10,000 log events exist across multiple marketplaces and batches When a user applies filters for marketplace, batch_id, sku_id, approver (actor_id), and a time window (start/end) Then the results contain only events matching all filters (logical AND) with inclusive time bounds And the query returns ≤ 2 seconds p95 for up to 50,000 matching records And results are sortable by timestamp (default desc) and paginated (cursor-based) with stable ordering
Export Logs to CSV and JSON
Given a filtered result set of up to 1,000,000 events When the user requests export as CSV Then the system produces RFC 4180–compliant CSV with header row, proper escaping, UTC ISO8601 timestamps, and all visible fields And large exports are chunked into files of 100,000 rows max each with a manifest and SHA-256 checksums; download links expire in 24 hours When the user requests export as JSON Then the system produces NDJSON with one event per line using the same schema; ordering by timestamp asc; chunked similarly
Real-time Webhook Streaming of Events
Given a webhook destination with URL and shared secret is configured and enabled When new audit events are appended Then a POST request is sent within 5 seconds p95 containing the event payload, an idempotency_key, and an X-PixelLift-Signature (HMAC-SHA256 of body + timestamp) And delivery uses at-least-once semantics with exponential backoff retries for up to 12 hours; failed deliveries are placed in a dead-letter queue and alerting is triggered And per-batch ordering is preserved; consumers can resume using last_delivered_event_id
Compliance Dashboards: SLA, Bottlenecks, Overrides
Given 90 days of audit events exist When a user opens the Compliance dashboard Then the dashboard loads within 4 seconds p95 and displays: SLA compliance rate (% on-time), average and p90 cycle time by step/marketplace, top bottleneck steps by average wait, and override rate by rule/approver level And filters for time range, marketplace, approver, and rule apply consistently to all widgets And drilling into any widget opens a pre-filtered log list whose counts match the widget within ±0.5%
Retention Policies and Legal Holds
Given an organization-level retention policy is configured per marketplace in days When an event ages beyond its policy and no legal hold applies Then it is purged by a daily job and a retention_purge summary event is appended with counts and time range And creating/updating/deleting retention policies or legal holds records an admin_audit event with actor, timestamp, and justification And legal holds can be applied by marketplace, batch_id, sku_id, or time window and prevent purge until removed
Tamper-Evident Hashing and Verification
Given tamper-evident hashing is enabled When events are appended to the log Then each event stores hash = SHA-256(event_canonical_form) and previous_hash referencing the prior event in the workspace chain; a daily anchor hash is recorded for external verification And a verification API can validate a requested time range (up to 1,000,000 events) in ≤ 30 seconds and returns Pass/Fail with first offending event_id when failing And cryptographic key rotation is supported with key_id metadata; previously written events remain verifiable after rotation

Audit Ledger

Get a tamper‑evident timeline of who changed what, when, and why—roles granted, presets edited, batches run, exports pushed, and overrides approved. See before/after diffs, attached justifications, and policy context at the time of change. Filter by client, workspace, user, or SKU and export CSV/PDF for client reports or compliance reviews.

Requirements

Tamper‑Evident Append‑Only Ledger
"As a compliance officer, I want a tamper-evident log of all actions so that I can prove integrity during audits."
Description

Provide an append-only event store with cryptographic hash chaining where each record references the prior record’s hash, making alterations detectable. Enforce immutability via database constraints and application safeguards, and periodically seal segments to write-once object storage for additional integrity. Include per-tenant chain roots, strictly monotonic sequence numbers, NTP-synchronized timestamps, and KMS-backed digital signatures. Expose a verification routine to validate chain integrity over selected ranges. Integrate through a unified ingestion API supporting idempotency keys, durable queuing, and back-pressure handling. Partition and index by client, workspace, user, SKU, batch, event type, and timestamp, with a separate read model optimized for query performance.

Acceptance Criteria
Hash Chaining Tamper Detection
Given an existing tenant ledger with at least one event When a new event is appended Then the new event.prev_hash equals the hash of the immediately prior event's canonical payload And the new event.hash equals the hash of the new event's canonical payload using the configured algorithm And any append where prev_hash does not equal the current chain head is rejected with HTTP 409 and no record is written
Append-Only Immutability and WORM Sealing
Given the ledger storage When an UPDATE or DELETE is attempted on any persisted event Then the database rejects the mutation and no change is committed And the application returns HTTP 405 and logs the attempt with actor, reason, and timestamp Given a ledger segment reaches the configured size or time threshold When the sealing job runs at the configured cadence Then the segment is sealed with a manifest containing start/end sequence numbers, segment hash root, and signature And the sealed artifacts are written to write-once object storage with the configured immutable retention And any attempt to overwrite a sealed object is denied by the object store
Monotonic Sequencing and NTP-Synchronized Timestamps
Given concurrent ingestion of events for a tenant When events are persisted Then their sequence numbers are strictly increasing by 1 with no gaps or duplicates per tenant chain And each event.timestamp is assigned by the service using an NTP-synchronized clock And the measured clock offset at write time is <= 500 ms as reported by the NTP client metrics
KMS-Backed Digital Signatures per Event
Given a configured KMS signing key When an event is persisted Then a digital signature over the event's canonical payload is created using the KMS key and stored with key version and algorithm And verification using the corresponding public key succeeds for 100% of a 10,000-event sample And after KMS key rotation, newly persisted events are signed with the new key version and both old and new events verify successfully
Per-Tenant Chain Roots, Partitioning, and Indexed Read Model
Given multiple tenants with separate chain roots When querying the read model scoped to a tenant with filters on workspace, user, SKU, batch, event type, and timestamp Then no records from other tenants are returned And p95 latency for range queries over the last 7 days is <= 800 ms for datasets up to 5 million events per tenant And newly accepted events appear in the read model within 5 seconds (p95)
Integrity Verification API Over Selected Ranges
Given a tenant and a sequence range [seq_start, seq_end] When the verification routine is invoked via API for that range with includeSignatures=true Then the response includes verified=true, head_hash, tail_hash, event_count, and signature_verification=true when the range is intact And if any event or hash link in the range is altered or missing, verified=false is returned with the first failing sequence number and reason And ranges that span sealed and unsealed segments verify without manual intervention And verification supports pagination or streaming for ranges > 100k events and maintains throughput >= 10k events/second
Ingestion API Idempotency, Durability, and Back-Pressure
Given an idempotency key K and identical event payloads When the ingestion API receives repeated requests with K within the idempotency window Then exactly one event is persisted and subsequent calls return the original response with HTTP 200 and no duplicate records Given a process crash or service restart after acknowledging a client write When the system recovers Then the event is present in durable storage and the read model within the normal propagation window Given sustained burst traffic that exceeds downstream capacity When back-pressure is applied Then the API responds with HTTP 429 and a Retry-After header while preserving per-partition ordering And accepted events are durably queued and eventually persisted with no loss
Comprehensive Event Instrumentation
"As an account admin, I want every critical action recorded with full context so that I can reconstruct activity when issues arise."
Description

Standardize and emit events for all critical actions: role/permission grants and revocations, preset lifecycle changes, batch job submissions and state transitions, export generation and delivery, and override requests with approvals/denials. Define a versioned event schema capturing actor identity, target entity, action, timestamps, environment, reason, and correlation IDs (request, batch, SKU). Record references to pre/post states and errors, plus latency and outcome metrics. Provide SDKs/middleware for web app, APIs, workers, and integrations to ensure consistent coverage. Enforce that critical operations persist their audit event synchronously or enqueue to durable storage before completion.

Acceptance Criteria
Role Permission Changes Audited
Given an admin grants the Editor role to a user in environment=prod When the request is processed Then exactly one audit event with schema_version=current is recorded with fields: actor.id, actor.role, target.type=user, target.id, action=role.granted, environment=prod, timestamps.emitted_at in ISO8601 UTC, correlation.request_id present, outcome=success, latency_ms >= 0 And the event includes pre_state.roles (before) and post_state.roles (after) referencing immutable snapshots And the event passes schema validation and is readable via the audit sink within 2 seconds of action completion Given an admin revokes a role from a user When the request is processed Then the event action=role.revoked is recorded with identical field coverage and validations
Preset Lifecycle Changes Audited
Given a preset is created via the web app When the operation completes Then an audit event is recorded with action=preset.created including actor.id, target.type=preset, target.id, timestamps.emitted_at (ISO8601 UTC), environment, reason (optional), correlation.request_id, outcome=success, latency_ms Given an existing preset is updated When the update commits Then an event action=preset.updated includes references to pre_state (previous JSON spec) and post_state (new JSON spec) and a machine-readable diff.ref pointing to stored diff artifact Given a preset is deleted When deletion finalizes Then an event action=preset.deleted includes pre_state reference and outcome=success or failure with error.code and error.message when applicable
Batch Job Lifecycle Correlated Events
Given a batch job is submitted via API When the submission is accepted Then an event action=batch.submitted is recorded with correlation.batch_id, correlation.request_id, actor.id, target.type=batch, target.id, timestamps, environment, outcome=accepted Given the batch transitions through queued -> started -> completed successfully When each transition occurs Then events are emitted for batch.queued, batch.started, batch.completed with consistent correlation.batch_id and non-decreasing timestamps And the batch.completed event includes metrics.duration_ms (started->completed), counts.total_images, counts.succeeded, counts.failed, and latency_ms for the API call Given the batch fails During processing When failure occurs Then batch.failed is emitted with error.code, error.message (sanitized), and outcome=failure
Export Generation and Delivery Audited
Given an export is requested for a completed batch When the export job is created Then export.requested event is recorded with correlation.export_id and correlation.batch_id Given export artifacts are generated When generation finishes Then export.generated event includes target.type=export, target.id, artifact checksums, size_bytes, and outcome=success Given delivery to a destination (e.g., S3) is attempted When the attempt completes Then export.delivery_attempted event records destination.type, destination.id (redacted if sensitive), attempt_number, provider_status_code, outcome=success|failure, and error fields on failure And for successful delivery Then export.delivered is emitted with consistent correlation IDs and timestamps in ISO8601 UTC
Override Requests Approval/Denial Audited
Given a user submits an override request on a SKU When the request is saved Then override.requested event is recorded with actor.id=requester, target.type=sku, target.id, correlation.sku_id, correlation.override_id, reason (required), pre_state reference, timestamps, environment Given an approver takes an action When the approval is granted Then override.approved event includes actor.id=approver, post_state reference, outcome=approved, and links back to override_id Given an approver denies the request When denial is saved Then override.denied event includes actor.id=approver, outcome=denied, reason required, and error fields absent
Synchronous Persistence or Durable Enqueue Enforcement
Given the audit store is healthy When a critical operation (role change, preset update, batch submit, export generate, override decision) is executed Then the corresponding audit event write is acknowledged by the durable store before the operation is reported successful to the caller Given the audit store is unavailable but the durable queue is healthy When the operation executes Then the event is enqueued with an acknowledged receipt (idempotency_key present) before the operation is reported successful Given both the store and durable queue are unavailable When a critical operation is attempted Then the operation fails with HTTP 503 (or equivalent) and no state change is committed And in all cases Then emitted events are idempotent: retries with the same idempotency_key produce no duplicate readable events And p95 added latency from event persistence/enqueue is <= 75 ms for synchronous paths in prod
SDK/Middleware Coverage and Schema Validation
Given the official SDKs/middleware are integrated in web app, public API, background workers, and third-party integration adapters When any enumerated critical action occurs Then an audit event is emitted with the standard schema and passes validation against the versioned JSON Schema And automated tests simulate each critical action and assert 100% coverage: at least one event per action type with required fields present And events include environment (dev|stage|prod), schema_version, and pass linters that enforce ISO8601 UTC timestamps and non-empty correlation IDs (request_id or batch_id or sku_id as applicable)
Before/After Diff & Artifact Previews
"As a project manager, I want to see exactly what changed before and after an edit so that I can quickly validate the impact without opening raw files."
Description

Capture and render human-readable diffs for structured entities (e.g., presets, policy configs, export settings) using field-level JSON diffs with highlighting for added, removed, and changed values. For image operations, generate secure, time-limited links to lightweight before/after thumbnails with side-by-side or overlay comparison, while storing references to full-resolution assets. Support semantic diffs for pipeline steps and parameters, emphasizing changes with potential visual impact. Implement lazy loading, caching, and adherence to data retention policies for artifacts, ensuring tenant-scoped, access-controlled retrieval.

Acceptance Criteria
Field-Level JSON Diff Rendering for Structured Entities
Given a preset, policy config, or export settings record is modified, When an authorized user opens the corresponding Audit Ledger event, Then the UI displays a field-level JSON diff that highlights added (+), removed (−), and changed (~) values and shows old vs new values for each changed field. Given a value changes from null to a concrete value or is removed entirely, When rendering the diff, Then null and missing are distinguished and labeled correctly. Given arrays within the entity, When items are added, removed, or reordered, Then the diff indicates index-level changes and preserves context for nearby unchanged items. Given the user selects Copy old JSON or Copy new JSON, When triggered, Then the clipboard receives the exact serialized JSON of that version.
Semantic Diff for Pipeline Steps and Parameters
Given a pipeline configuration change (step added/removed/reordered or parameter updated), When viewing the audit event, Then a semantic diff summarizes the change with human-readable labels and highlights parameters with potential visual impact. Given a change has no visual impact (metadata-only), When rendering, Then it is grouped under Non-visual impact and not highlighted as high impact. Given a step reorder without parameter changes, When rendering, Then the order change is explicitly shown (e.g., moved from #3 to #1). Given both JSON and semantic diffs exist, When the user switches views, Then the UI toggles without re-fetching the underlying data.
Secure Time-Limited Before/After Thumbnail Links
Given an image operation audit event with before/after assets, When a user clicks View Before/After, Then the system issues HTTPS signed URLs scoped to the tenant and user permissions that expire per the configured TTL. Given the TTL is configured to 60 minutes, When attempting access after 61 minutes, Then the request is rejected with 403 and the UI prompts to request a new preview link. Given a user from a different tenant or workspace attempts access, When using a signed URL not scoped to their tenant, Then access is denied with 403 and no image content is returned. Given thumbnails are served, When fetched, Then they conform to the configured thumbnail profile (dimensions/size) and no direct full-resolution URLs are exposed.
Side-by-Side and Overlay Comparison Modes
Given a before/after preview is opened, When displayed, Then side-by-side mode is the default with clear Before and After labels. Given the user selects overlay mode, When toggled, Then a control allows wipe or opacity comparison with the images aligned and without layout shift. Given the user switches modes multiple times, When toggling, Then the preview does not re-request the underlying assets and maintains current viewport fit.
Lazy Loading and Cache Control for Artifact Previews
Given an audit events list, When preview thumbnails are off-screen, Then they are not requested until entering the viewport threshold (lazy load). Given the same thumbnail is requested again within its cache-valid window, When fetched, Then it is served from cache per HTTP headers without a new network fetch. Given a thumbnail link has expired or the asset was purged per retention policy, When requested, Then the response is non-cacheable and the UI offers a refresh action to obtain a new signed URL.
Tenant-Scoped References and Access-Controlled Retrieval
Given audit entries reference full-resolution assets, When viewed in the Audit Ledger, Then only opaque asset identifiers are shown and direct full-resolution downloads are not rendered in this view. Given a user with Audit.ViewArtifacts permission in Tenant A, When requesting previews for Tenant A events, Then access is granted; When requesting Tenant B artifacts, Then access is denied with 403. Given any artifact is accessed via a signed URL, When served, Then the access is logged with tenant ID, user ID, artifact ID, timestamp, and purpose for compliance tracking.
Justification & Policy Context Snapshot
"As a security lead, I want changes to include the reason and the rules in effect so that I can assess appropriateness in context."
Description

Collect user-provided justifications, ticket references, and classification (e.g., emergency change) on sensitive actions, and bind them to an immutable snapshot of the applicable policy and permission model at the time of change. Persist rule evaluations, exception paths taken, and risk flags, and render this context inline with each ledger entry. Make justification text and policy attributes searchable and filterable. Version and protect snapshots to enable accurate time-travel reviews, ensuring that subsequent policy changes do not rewrite past context.

Acceptance Criteria
Justification Prompt on Sensitive Action
Given a user initiates a sensitive action (e.g., role grant, policy override, preset edit, batch export with override) When the confirmation modal appears Then the system requires a justification text of 20-1000 characters And requires a classification selected from {Standard, Elevated, Emergency} And optionally accepts a ticket reference matching regex [A-Z]{2,10}-\d{1,8} And disables the Confirm action until required fields are valid And records actor ID, target resource, action type, timestamp (UTC), and client/workspace IDs
Immutable Policy Context Snapshot at Commit
Given the user submits a valid sensitive action When the action is committed Then the system captures a read-only snapshot containing effective policies, role assignments, permission model, feature flags, and organization settings used in authorization And persists a decision trace of rules evaluated with their inputs and outcomes And binds the snapshot ID to the resulting ledger entry And prevents any subsequent update or delete of the snapshot (immutable) And later changes to current policies do not alter the stored snapshot content or its integrity hash
Exception Path and Risk Flags Recorded
Given the action uses an exception path (e.g., break-glass, admin override) or triggers risk rules When the ledger entry is created Then the snapshot stores the exception type, approver user IDs, escalation ticket link, and expiry if applicable And records risk flags and a risk score with the rule IDs that produced them And marks the ledger entry with visible Exception and/or Risk indicators
Inline Rendering of Snapshot Context in Ledger
Given a reviewer opens a ledger entry detail view When the panel loads Then the UI displays justification text, ticket reference, classification, before/after diffs, and a snapshot summary (effective policy/version, decision trace, risk flags) And sensitive or secret values are redacted according to display rules And the detail panel loads in under 800 ms at the 95th percentile for the last 30 days of data And the decision trace can be expanded or collapsed without page reload
Search and Filter by Justification and Policy Attributes
Given a reviewer enters a search query or applies filters in the Audit Ledger When the query targets justification text or ticket reference Then results include entries whose justification or ticket reference match case-insensitively with stemming And filters are available for classification, risk flag presence, exception type, effective role, permission checked, and policy version And the first page of results returns in under 1.5 seconds at the 95th percentile for up to 1,000,000 entries And the query parameters are reflected in a shareable URL
Versioned Time-Travel Review Integrity
Given policies are modified after a ledger entry was recorded When a reviewer opens the original ledger entry or downloads its snapshot Then the content exactly matches the snapshot integrity hash recorded at commit time And the UI labels the snapshot with its version identifier and capture timestamp And a comparison view shows differences between current policy and the snapshot policy And any attempt to modify or delete the snapshot is rejected and logged as a tamper attempt
CSV/PDF Export Includes Snapshot Context and Signatures
Given a reviewer exports ledger entries to CSV or PDF When the export includes entries with snapshots Then each exported record contains justification text, ticket reference, classification, snapshot version ID, key policy attributes, decision trace summary, exception type, and risk flags And each record includes a SHA-256 integrity hash of the snapshot payload and the ledger entry ID And the export completes within 60 seconds for up to 50,000 entries And all timestamps are formatted in ISO 8601 UTC
Multi‑Dimensional Filtering & Saved Queries
"As a support analyst, I want to filter the audit timeline by user, SKU, and batch within a date range so that I can isolate relevant events quickly."
Description

Provide performant, combinable filters across client, workspace, user, SKU, batch, event type, outcome, and time range, with full-text search over justifications and metadata. Support Boolean operators, range queries, pagination, and stable sorting. Allow users to save, name, and share filter configurations with role-based access. Back the UI with an indexed, denormalized read store to deliver sub-second responses at typical volumes, and include safeguards and query cost limits to prevent runaway scans.

Acceptance Criteria
Compound Filter Across Dimensions
Given a ledger containing events across multiple clients, workspaces, users, SKUs, batches, event types, outcomes, and timestamps And a user selects client = "ClientA", workspace = "W1", user = "U5", SKU IN ["S-1001","S-1003"], batch = "B42", event type IN ["PresetEdited","ExportPushed"], outcome = "Success", time range = 2025-01-01T00:00:00Z..2025-01-31T23:59:59Z When the filter is applied Then only events matching all selected constraints are returned And the total count reflects the filtered result set And no events outside the selected dimensions appear on any page And the active filter state is serializable to an idempotent, shareable query string
Boolean Search Over Justifications and Metadata
Given a user enters the full-text search: (refund OR "policy exception") AND NOT test And metadata fields are included in the search corpus When the query is executed with the current filters Then results include only events whose justification or metadata satisfy the boolean expression And quoted phrases match contiguous terms, case-insensitive and diacritics-insensitive And NOT excludes matching tokens across justification and metadata And parentheses determine precedence over default AND/OR precedence And an empty or whitespace-only query returns no full-text filter (i.e., does not alter results)
Range Queries and Time Zone Boundaries
Given a user with profile time zone America/Los_Angeles And the time range is set to 2025-03-01 to 2025-03-31 inclusive When the query is executed Then events with timestamps from 2025-03-01T00:00:00-08:00 through 2025-03-31T23:59:59.999-07:00 are included (accounting for DST transition) And switching the boundary operators to > and < correctly excludes start and end boundary events And specifying a numeric range (e.g., batch.size 10..100) includes only events whose field values fall within the range And invalid range inputs yield a 422 with field-specific validation errors
Pagination and Stable Sorting
Given default sort is timestamp DESC with a deterministic tiebreaker event_id ASC And the user requests pageSize = 100 via cursor-based pagination When the user retrieves pages 1..N under steady inserts to the ledger Then no item appears on more than one page and no items are skipped due to concurrent inserts And re-running the same query and cursor yields identical items and order (until filters or sort change) And changing sort order (e.g., event type ASC, then timestamp DESC, then event_id ASC) produces a stable, deterministic ordering And requesting an invalid/expired cursor returns 400 with a recoverable error and a link to restart from the beginning
Performance SLO on Indexed Read Store
Given typical production volumes (up to 2M events per client, 50 concurrent active users) And queries use only supported filters/sorts on indexed fields When executing representative queries that return ≤ 500 rows Then p95 end-to-end response time ≤ 750 ms and p99 ≤ 1.5 s over a 24-hour window And first-query (cold cache) p95 ≤ 1.2 s And server returns results without triggering timeouts or degrading pagination accuracy And latency SLOs are observable via dashboards with alerts when breached
Saved Queries: Create, Share, RBAC Enforcement
Given a user with Editor role composes a filter and names it "High-Risk Overrides" When the user saves the query Then the system persists it with a unique immutable ID, human-readable name, owner, scope (workspace), and role-based access list And default visibility is Private to owner; owner can grant Viewer (run-only) and Editor (modify) access to users/roles in the same workspace And a Viewer can list and run the saved query but cannot edit, rename, or delete it (receives 403 if attempted) And a user without access cannot discover or execute it by ID (receives 404/403 per policy) And updates create a new version; running the saved query executes the latest version unless a specific version is pinned
Query Safeguards and Cost Limits
Given the system estimates query cost before execution When a query is estimated to scan >5% of the relevant partition or exceed a 5 s budget Then the API rejects the request with 422 and error code QUERY_COST_LIMIT, returning guidance to narrow filters And any executing query is hard-capped at 3 s per shard and 5 s overall with a clear timeout error if exceeded And results are capped at 10,000 rows; if exceeded, the API returns a truncation flag and next-page cursor And all rejections/timeouts emit metrics and an audit event of type QuerySafeguardTriggered
Auditable Export (CSV/PDF) with Branding & Redaction
"As an agency account manager, I want to export a branded, redactable audit report so that I can share compliant evidence with clients and auditors."
Description

Enable export of filtered ledger entries to CSV and paginated PDF suitable for client and compliance reporting. Include configurable columns, a chain integrity summary, and an embedded verification token/hash to validate authenticity. Support optional PII redaction based on role, and add branded headers, footers, and cover pages. Implement asynchronous export jobs with progress, notifications, and download expiration, and log export initiation and downloads as audit events. Respect retention settings and enforce tenant isolation on generated files.

Acceptance Criteria
CSV Export with Configurable Columns and Filters
Given a user in tenant T with Audit Export permission and active filters (client=C1, workspace=W1, user=U1, SKU=SKU-123, date range [D1,D2]) are applied in the Audit Ledger When the user requests a CSV export and selects columns [Timestamp, Actor, Action, Target, Before, After, Justification] with a specified order and header labels Then the exported CSV contains only rows that match all active filters And the CSV includes exactly the selected columns in the specified order with the provided header labels And timestamps are formatted as ISO-8601 UTC (e.g., 2025-09-26T14:30:00Z) And fields are RFC 4180–compliant (comma-delimited, quoted when needed, UTF-8 without BOM) And the number of data rows equals the count shown in the UI for the same filters And the file name includes tenant slug, date range, and a monotonic job ID
Branded PDF Export with Pagination
Given a user selects PDF export with branding options (logo, header text, footer text, cover page enabled) and active filters When the PDF export job completes Then the PDF begins with a branded cover page showing tenant/client name, logo, date range, filter summary, job ID, and export timestamp And each page renders the configured header and footer and displays page X of Y And tabular entries are paginated with repeating table headers and no row truncation; overflowing cells wrap within the page And table columns and order match the user’s selection And the document is text-searchable (embedded text, not image-only) and opens without errors in Acrobat and system PDF viewers
Chain Integrity Summary and Verification Token
Given an export (CSV or PDF) completes successfully When a verifier scans the PDF cover page QR code or submits the CSV bundle’s manifest token to the /api/verify-export endpoint Then the endpoint returns status=valid with matching job ID, tenant ID, file SHA-256, filter signature, and creation timestamp And the export package includes a human-readable chain integrity summary (first record ID, last record ID, total count, chain root hash) And if any byte of the exported file is altered post-generation, the verification endpoint returns status=invalid and the integrity summary no longer matches the file hash And the CSV download bundle includes a manifest.json containing verification token, file SHA-256, and filter signature And verification tokens have at least 128 bits of entropy and expire per retention settings; expired tokens return status=expired
Role-Based PII Redaction
Given PII redaction policy is enabled and a user without View PII permission requests an export When the export completes Then PII fields (e.g., email, IP address, full name, address) are redacted per policy and replaced with the placeholder [REDACTED] and a reason code And a Redaction-Policy-Version and redaction applied=true indicator are included in export metadata (PDF cover, CSV manifest) And verification tokens and file hashes reflect the redacted content (i.e., differ from an unredacted export) And a user with View PII permission exporting the same filters within the same time window receives unredacted values And no PII values are leaked via file names, headers/footers, or audit events
Asynchronous Export Job with Progress and Notifications
Given an export request meets async criteria (PDF or CSV with >10,000 rows) and the user starts the export When the job is queued and executed Then the UI displays status transitions (Queued → Running with percentage → Succeeded/Failed/Cancelled) And progress percentage increases monotonically and updates at least every 5 seconds during Running And the user can cancel while Running; cancellation stops processing and marks the job as Cancelled And on Succeeded or Failed, the user receives an in-app notification and email with a link to the result or error details And failures include an error code and retry guidance; retries maintain a link to the original job ID
Download Expiration, Tenant Isolation, and Retention
Given an export job succeeds and produces a downloadable asset When a user attempts to download the asset Then the asset is served via a signed URL scoped to the user’s tenant and expires after the configured TTL (e.g., 24 hours) And access after expiration returns HTTP 410 Gone and is logged as an audit event And access by users outside the tenant or without permission returns HTTP 403 Forbidden and is logged And assets are deleted per data-retention settings and are no longer retrievable thereafter And generated files are stored under tenant-scoped storage prefixes to prevent cross-tenant enumeration or access
Audit Events for Export Initiation and Download
Given a user initiates an export from the Audit Ledger When the export job is created Then an audit event is recorded with who, when, tenant, filters snapshot, selected columns, format (CSV/PDF), brand options, job ID, and expected row count And when the file is first downloaded (or a download attempt occurs), an audit event is recorded with who, when, IP, user agent, job ID, file hash, verification token, and outcome (success/expired/forbidden) And audit events never persist PII values even if the export was unredacted; only the redaction status and policy version are recorded And these export-related audit events respect global retention settings and are themselves exportable via the Audit Ledger

Workspace Walls

Keep client data air‑gapped by default. Assets, presets, and logs live in separate workspaces with search and export restricted to authorized roles. Need collaboration? Issue time‑boxed Safe Share links with scoped permissions (view, annotate, approve) and optional watermarking/NDA acknowledgment—preventing cross‑client leakage while enabling controlled reviews.

Requirements

Workspace Isolation & Data Segmentation
"As an account owner, I want each client’s images, presets, and logs kept in isolated workspaces so that no data can be accessed or discovered across clients."
Description

Implement hard isolation of assets, presets, and logs by workspace to enforce client data air-gapping. Each workspace functions as a first-class security boundary with unique identifiers, per-workspace encryption keys, tenant-aware storage buckets/paths, and isolated compute queues. All API calls must include a workspace context and reject cross-workspace resource references. Caches, background jobs, and search indexes are partitioned by workspace to prevent bleed-through. Data at rest is encrypted with KMS-managed keys per workspace; data in transit uses TLS with strict token scoping. The outcome is guaranteed separation that prevents cross-client leakage while preserving normal PixelLift editing and batch pipelines within each workspace.

Acceptance Criteria
API Enforces Workspace Context and Rejects Cross-Workspace Access
Given a request to any protected API without a workspace_id in path or header, When processed, Then return 400 with error_code=MISSING_WORKSPACE_CONTEXT and no data returned. Given an access token scoped to workspace A, When it requests any resource belonging to workspace B by ID or URL, Then return 403 with error_code=CROSS_WORKSPACE_ACCESS and do not reveal existence via timing or message content. Given a job creation payload for workspace A that references an asset/preset/log from workspace B, When validated, Then reject with 422 and do not enqueue any job.
Per-Workspace Storage Segmentation and KMS Keys
Given a newly created workspace, When storing its first asset, Then the object path matches a workspace-specific bucket/prefix (e.g., {bucket}/{workspace_id}/assets/...) and uses SSE-KMS with a KMS key uniquely assigned to that workspace. Given two distinct workspaces A and B, When retrieving their KMS key ARNs, Then ARNs differ and key policies restrict decrypt to PixelLift services acting in the respective workspace scope. Given a principal from workspace A, When attempting to list or get objects under workspace B's prefix, Then access is denied (HTTP 403) and an audit event is recorded in workspace A.
Partitioned Caches and Indexes Prevent Bleed-Through
Given two workspaces A and B with assets having the same filename and checksum, When asset A is edited and cached, Then requesting the corresponding asset in workspace B returns B's original version, not A's cached variant. Given a cache purge in workspace A, When executed, Then only A's cache entries are removed; B's cache hit rate is unchanged within ±2% over the next 1,000 requests. Given a search query in workspace A, When run, Then results contain only documents tagged with workspace_id=A; the same query in B returns only B-tagged documents.
Isolated Compute Queues and Background Workers
Given jobs enqueued for workspaces A and B, When workers process queues, Then a worker configured for A never dequeues jobs from B and vice versa, verified over 10,000 jobs with zero cross-queue picks. Given a job in workspace A that references an asset ID from workspace B, When picked by a worker, Then the worker fails the job with reason=INVALID_CROSS_WORKSPACE_REFERENCE before any processing and does not access B's storage. Given autoscaling events, When new workers join, Then they inherit workspace scoping and do not process jobs from other workspaces.
Role-Scoped Search and Export Within Workspace
Given a user with role=Editor in workspace A, When performing search, Then results include only A and exclude B; Given the same user tries to export results, Then export succeeds only if the role has Export permission in A. Given a user without access to workspace B, When attempting any search or export in B by ID or UI switch, Then return 403 and no files are generated. Given an audit review, When checking logs for export operations, Then each export event is tagged with the initiating user, workspace_id, resource count, and destination, with no events mixing multiple workspace_ids.
TLS Enforcement and Token Workspace Scoping
Given any API endpoint, When accessed over HTTP (non-TLS), Then connection is rejected (HTTP 426 Upgrade Required) and no tokens are processed. Given a JWT without a workspace_id claim, When presented to a protected endpoint, Then return 401 with error_code=INVALID_TOKEN_SCOPE. Given a JWT scoped to workspace A, When used to call endpoints with workspace_id=B, Then return 403 and no resource metadata is leaked; HSTS and TLS 1.2+ with modern ciphers are enforced on all responses.
Workspace RBAC & Permission Matrix
"As a workspace admin, I want to assign roles with least-privilege permissions so that collaborators only see and do what their role allows."
Description

Provide role-based access control at the workspace scope with least-privilege defaults. Define standard roles (Owner, Admin, Editor, Viewer, Auditor) and map them to granular permissions for actions such as search, upload, edit, export, manage presets, create Safe Share links, approve, and view logs. Enforce authorization at the API gateway and service layers using workspace-scoped tokens/claims; gate corresponding UI elements. Support per-workspace user membership, SSO/SCIM group mapping, and audit of role changes. Deny by default for unassigned permissions to reduce risk of accidental exposure. This integrates with existing PixelLift pipelines so that only authorized roles can trigger edits, exports, and shares within the active workspace.

Acceptance Criteria
Least-Privilege Default Access on New Workspace Membership
Given a workspace with default role assignment configured When a new user is invited or provisioned to the workspace without an explicit role Then the user is assigned the least-privilege default role (Viewer) And API requests by that user to upload, edit, export, manage presets, create Safe Share links, approve, or view logs return 403 Forbidden And corresponding UI controls for those actions are hidden or disabled with an authorization tooltip And search results show only asset metadata and preview thumbnails with no download controls And an audit entry records the membership add event with actor, target user, role, timestamp, and IP
Role Permission Matrix Enforcement
Given a user with Owner role in a workspace When they attempt search, upload, edit, export, manage presets, create Safe Share links, approve, and view logs Then all actions are permitted and return success (2xx) and all UI controls are enabled Given a user with Admin role in the same workspace When they attempt the same actions Then all actions are permitted and return success (2xx) and all UI controls are enabled Given a user with Editor role in the same workspace When they attempt search, upload, edit, export, and manage presets Then those actions are permitted and return success (2xx) And when they attempt create Safe Share links, approve, or view logs Then those actions return 403 Forbidden and corresponding UI controls are hidden/disabled Given a user with Viewer role in the same workspace When they attempt to search and view asset previews/metadata Then those actions are permitted and return success (2xx) And when they attempt upload, edit, export, manage presets, create Safe Share links, approve, or view logs Then those actions return 403 Forbidden and UI controls are hidden/disabled Given a user with Auditor role in the same workspace When they attempt to view and export audit logs Then those actions are permitted and return success (2xx) And when they attempt asset search, upload, edit, export, manage presets, create Safe Share links, or approve Then those actions return 403 Forbidden and UI controls are hidden/disabled
Workspace-Scoped Token Claims at API Gateway and Service Layer
Given an access token containing user identity, roles, and workspace_id=W1 When the user calls an API for resources in workspace W1 with a permitted action Then the API gateway authorizes and forwards the request and the service layer re-validates the workspace scope and role before executing Given the same token When the user calls an API for resources in workspace W2 (not in token scope) Then the API gateway returns 403 Forbidden and no service call is executed Given a token missing workspace_id or with an invalid/expired signature When any protected API is called Then the API gateway returns 401 Unauthorized And all authorization decisions are recorded in security logs with request id, subject, workspace_id, action, decision, and reason And service data access queries include mandatory workspace filters to prevent cross-workspace reads/writes (verified via integration tests)
Authorization-Gated UI and Deep-Link Protection
Given a signed-in Editor in workspace W1 When the app renders action controls Then controls for create Safe Share links, approve, and view logs are not rendered or are disabled with an authorization tooltip When the Editor visits a deep link to a restricted route (e.g., /w/W1/logs) Then the client shows an authorization error page and the API returns 403 Forbidden And modifying the DOM or API payload client-side cannot enable restricted actions (server enforces authorization) And feature flags or cached permissions do not grant access beyond server-evaluated roles (validated by permission refresh on route change)
Per-Workspace Membership and SSO/SCIM Group Mapping
Given a SCIM group to role mapping exists: group "W1-Editors" -> Editor role in workspace W1 When a user assigned to "W1-Editors" signs in via SSO Then the user is added to workspace W1 as Editor if not present or updated to Editor if role differs And the membership appears in the workspace members list with role=Editor When the user is removed from the SCIM group or the mapping is changed to Viewer Then the user’s role in workspace W1 is updated within 60 seconds And removal from all mapped groups revokes workspace access within 5 minutes and terminates active sessions for that workspace And all provisioning changes (add, update, remove) are captured in the audit log with actor system=SCIM/SSO
Audit Trail for Role and Membership Changes
Given any role add, remove, or change occurs in a workspace When the change is committed Then an immutable audit record is written with fields: workspace_id, actor, target user, previous role, new role, action, timestamp (UTC), IP/user-agent, and reason/comment And audit records are append-only, tamper-evident via hash chain or equivalent integrity mechanism, and queryable by time range, actor, and target user And Owners, Admins, and Auditors can filter and export audit logs as CSV/JSON And unauthorized attempts to change roles or view audit logs are blocked with 403 and logged as security events
Safe Share Creation Permission and Scope Enforcement
Given a user role in a workspace When the user attempts to create a Safe Share link Then only Owner and Admin roles succeed; Editor, Viewer, and Auditor receive 403 Forbidden When a Safe Share is created Then the link is scoped to the creating workspace only, includes explicit permissions (view, annotate, approve), and an expiration timestamp And optional watermarking and NDA acknowledgment can be toggled; recipients must acknowledge NDA before access when enabled And recipients can only perform actions granted by the scope; export is disallowed unless explicitly included by policy And after expiration or revocation the link returns 410 Gone or 403 Forbidden and access is removed within 60 seconds And creation, access, approvals, and revocations are recorded in the audit log with actor and recipient identifiers
Scoped Search and Export Enforcement
"As an editor, I want search and export limited to my current workspace so that I can’t accidentally surface or export another client’s data."
Description

Constrain all search, listing, and export operations to the active workspace with server-side enforcement. Partition or tag search indexes by workspace and require a verified workspace filter on every query. Prevent global search unless explicitly enabled for meta-admins. Exports are restricted to authorized roles and produce a signed manifest; apply rate limits and size thresholds to reduce exfiltration risk. Validate that requested asset IDs and presets belong to the active workspace and reject mismatches. The UI prominently displays the current workspace context and disables cross-workspace selection. This ensures users can quickly find and export what they need without risking cross-client data exposure.

Acceptance Criteria
Server-Side Workspace Filter Required for Search and Listing
- Given an authenticated user whose active workspace is W, when they issue any search or listing request without an explicit workspaceId filter, then the API responds 400 with error_code=WORKSPACE_FILTER_REQUIRED and no data payload. - Given an authenticated user with active workspace W, when they issue a search or listing request with workspaceId set to W and valid authorization, then the API responds 200 and every returned record across assets, presets, and logs has workspaceId=W; no record from any other workspace is present. - Given an authenticated user whose token is scoped to workspace W, when they issue a query with workspaceId set to X ≠ W, then the API responds 403 with error_code=WORKSPACE_MISMATCH.
Partitioned/Tagged Search Index Enforces Workspace Isolation
- Given indexed documents exist for workspaces W and X, when a search is executed with workspaceId=W, then documents with workspaceId=X are not retrievable even if the query directly targets their IDs; the API returns 404 for direct fetch or omits those documents from results. - Given a query attempts to use wildcards, joins, or unsupported operators to bypass workspace scoping, when executed, then the API enforces workspaceId=W and returns only documents from W. - Given a backend service executes internal queries on behalf of a user in workspace W, when the service omits workspaceId, then the server injects/enforces workspaceId=W from the caller context, preventing cross-workspace leakage.
UI Workspace Context Indicator and Cross-Workspace Selection Disabled
- Given a user with access to multiple workspaces switches active workspace to W, when viewing assets or presets, then the UI header prominently displays W’s name and identifier, and item pickers/listings only show entities from W. - Given the user attempts to multi-select items across W and X (via saved selections, clipboard, or cross-tab), then cross-workspace items are automatically deselected and an inline notice explains cross-workspace selection is disabled. - Given the user pastes or enters an ID from workspace X while active workspace is W, then the UI blocks the action and the API rejects the request with 403 WORKSPACE_MISMATCH.
Export Authorization and Signed Manifest
- Given role-based access control is configured, when a user without the Export permission requests an export from workspace W, then the API responds 403 with error_code=EXPORT_NOT_AUTHORIZED and no export job is created. - Given a user with Export permission requests an export from workspace W, then an export job is created (status=queued) and a manifest is generated including exportId, workspaceId=W, requesterUserId, requesterRole, assetIds, presetIds, createdAt (ISO-8601), itemCounts, and sha256 of the payload. - Given the manifest is generated, then it is cryptographically signed by the platform signing key and the signature is verifiable by clients; the signed manifest is attached to the export bundle and recorded in audit logs.
Asset and Preset Ownership Validation on Export
- Given an export request from workspace W includes any assetIds or presetIds belonging to workspace X, then the export is rejected with 422 error_code=WORKSPACE_OWNERSHIP_VIOLATION and details list the offending IDs; no partial export occurs. - Given an export request from workspace W includes only assetIds and presetIds owned by W, then the API accepts the request and the resulting export contains exactly the requested items; none from other workspaces are present. - Given an export request references non-existent IDs, then the API returns 404 NOT_FOUND for those IDs and the export is not created.
Export Rate Limits and Size Thresholds to Reduce Exfiltration Risk
- Given the test configuration sets per-workspace export rate to 3 requests per minute and per-user concurrent exports to 2, when a 4th request occurs within the minute or a 3rd concurrent job is started, then the API responds 429 with a Retry-After header indicating the next allowable time. - Given the test configuration sets maximum export size to 5,000 items or 5 GB (whichever is lower), when a request exceeds either threshold, then the API responds 413 with error_code=EXPORT_SIZE_LIMIT_EXCEEDED and no export is created. - Given a request is within configured limits, then the API accepts it and the audit log records limit counters and the configuration snapshot used for the decision.
Meta-Admin Global Search Explicit Enablement
- Given a user has the MetaAdmin role but lacks the global_search feature flag, when they attempt a global search (no workspaceId or workspaceId='*'), then the API responds 403 with error_code=GLOBAL_SEARCH_DISABLED. - Given a user has the MetaAdmin role and the global_search feature flag enabled, when they perform a global search, then the API responds 200 and results may include records from multiple workspaces; response metadata includes workspaces[] listing distinct workspaceIds present. - Given any non-MetaAdmin user attempts a global search, then the API responds 403 and the attempt is recorded in security audit logs with userId, ip, timestamp, and reason.
Safe Share Links (Time-boxed & Scoped)
"As a project manager, I want to send a time-limited review link with only view and annotate permissions so that external clients can approve edits without accessing the full workspace."
Description

Enable creation of secure, time-bound Safe Share links for assets, batches, or collections with scoped permissions (view, annotate, approve) and an optional download toggle. Shares issue signed, short-lived tokens with configurable expiry (e.g., 24h–30d), max views, IP allowlists, and revocation. Recipients cannot browse the workspace; access is limited to shared resources only. Provide a guided invite flow (email message, role-like permission selection) and a lightweight review UI supporting comments and approvals. Include real-time revocation and the ability to extend or narrow permissions post-creation. This enables controlled external reviews while preserving workspace isolation.

Acceptance Criteria
Generate Signed, Time-Bound Share Links for Assets, Batches, and Collections
Given a workspace user with Share permission selects one or more resources (asset, batch, or collection) and sets an expiry between 24 hours and 30 days, When they create a Safe Share, Then the system generates a unique, signed, short‑lived tokenized URL for the selected resources and displays it to the user. Given the Safe Share link is accessed before expiry with a valid token, When the recipient opens the link, Then only the shared resources are visible and load successfully, and no workspace navigation or search is available. Given the current time is past the configured expiry or the token is invalid, When the link is accessed, Then access is denied with an “Expired or invalid link” state and the resources are not retrievable.
Scoped Permissions and Download Toggle Enforcement
Given a Safe Share is created with permissions {view} and download toggle = off, When a recipient opens the link, Then the UI allows viewing only, hides/blocks annotate and approve actions, and disables any download controls. Given a Safe Share is created with permissions {view, annotate} and download toggle = on, When a recipient opens the link, Then annotation tools are enabled and downloads are permitted only for the shared resources. Given a Safe Share is created without {approve} permission, When a recipient attempts to approve, Then the approve action is not visible or returns a permissions error. Given a Safe Share is created with {approve}, When the recipient approves an item, Then the approval state is recorded for that specific resource and is reflected to the share owner within 5 seconds.
Guided Invite Flow with Role-Like Permission Selection
Given a user initiates Safe Share creation, When they enter one or more recipient emails, select a scoped permission set (view/annotate/approve), set expiry, and (optionally) toggle download, Then the system validates inputs, creates the share, and presents a confirmation with copyable link and recipient list. Given recipients were added, When the share is created, Then an email invitation is dispatched per recipient including the link, expiry date/time, and the selected permissions summary, and the share details show an “Invited” state for each email. Given at least one required field is missing or invalid (e.g., no resources selected, bad email), When the user submits, Then the system blocks creation and surfaces inline validation messages specifying the issues.
IP Allowlist and Max View Limits
Given the creator configures an IP allowlist for a Safe Share, When a recipient accesses from an IP not matching the allowlist, Then access is denied with an “Access restricted by IP” message and no asset data is returned. Given the creator sets a maximum view count N for a Safe Share, When the link is opened resulting in the Nth successful session load, Then the view counter reaches N; When the next access occurs, Then the system blocks access with a “View limit reached” message. Given no allowlist is configured, When a recipient accesses from any IP, Then IP does not block access (subject to other controls).
Real-Time Revocation of Safe Share Links
Given a Safe Share exists, When the owner revokes the share, Then new accesses are blocked immediately and active recipient sessions are invalidated within 5 seconds, preventing further navigation, downloads, annotations, or approvals. Given a revoked share link is accessed, When a recipient attempts to open it, Then the system displays a “Link revoked” state and does not reveal any resource metadata beyond the share title if present.
Post-Creation Modification of Expiry and Permissions
Given a Safe Share exists, When the owner extends the expiry (not exceeding 30 days from the modification time) or shortens it, Then the new expiry takes effect for both new and active sessions within 5 seconds. Given a Safe Share exists, When the owner narrows permissions (e.g., from annotate to view) or broadens them (e.g., add approve), Then the UI and API enforce the updated scope on subsequent actions without requiring a new link, and an onscreen notice informs active recipients of the change. Given the owner disables the download toggle post‑creation, When a recipient attempts to download, Then downloads are blocked immediately with a permissions message.
Lightweight Review UI with Comments/Annotations and Workspace Isolation
Given a recipient has {annotate} permission, When they add a comment or annotation on a shared resource, Then the note is saved, timestamped, attributed to the recipient identifier, and visible to the share owner within 5 seconds. Given a recipient has {approve} permission, When they mark a resource as Approved, Then the approval is persisted per resource, idempotent on repeat attempts, and visible in the share owner’s activity view. Given a recipient accesses a Safe Share, When they attempt to search or navigate outside the shared resources, Then the UI prevents it and no non-shared assets, presets, or logs are discoverable.
Watermarking & NDA Acknowledgment
"As a brand owner, I want shared previews watermarked and gated by an NDA acknowledgment so that early assets aren’t leaked or misused."
Description

Offer configurable watermarking for Safe Share previews and optional NDA acknowledgment gates. Allow per-share settings for watermark text (including recipient email, timestamp), placement, size, and opacity; render watermarks on-the-fly for previews without modifying originals. Gate access with a customizable NDA screen that requires recipient acknowledgment or e-sign before viewing; record consent hash, timestamp, IP, and user agent. Optionally require watermarking for any share with downloads enabled. Persist all settings and acknowledgments to the audit log. This deters leaks and provides legal and forensic assurance during external reviews.

Acceptance Criteria
Per‑Share Watermark Configuration for Previews
Given a Safe Share with watermark settings: text "Confidential — {email} — {timestamp}", placement "bottom-right", size "12pt", opacity "40%" And the original asset exists in the workspace When the recipient john@example.com opens the share preview at 2025-09-26T10:30:00Z Then the preview displays a watermark overlay at the bottom-right with font size 12pt and opacity 40% And the watermark text equals "Confidential — john@example.com — 2025-09-26T10:30:00Z" And the original stored file remains unmodified (checksum unchanged)
NDA Gate Required Before Viewing Safe Share
Given a Safe Share configured with an NDA requirement using template "External Review NDA v3" and e-sign enabled When a recipient visits the link Then an NDA screen is shown with template title, version, full text, and an e-sign control And asset previews are inaccessible until the recipient completes acknowledgment/e-sign And on decline, access is blocked with HTTP 403 and an "NDA not accepted" message And on acceptance, the system records consent hash, acceptance timestamp (ISO 8601 UTC), IP address, user agent, share ID, and recipient identifier And only after recording consent is access to previews granted
Require Watermarking When Downloads Are Enabled
Given a Safe Share with downloads enabled and "Require watermark for downloads" set to On, and watermark settings configured When the recipient downloads an image from the share Then the downloaded file is a watermarked derivative matching the configured text, placement, size, and opacity And the original file in storage remains unchanged (checksum matches pre-share value) And when "Require watermark for downloads" is toggled Off for the same share, a new download produces an unwatermarked file while preview watermarking follows its own preview setting
Dynamic Tokens Resolved in Watermark Text
Given watermark text template "Reviewed by {email} at {timestamp}" When the share is opened by recipient jane@buyer.com at 2025-09-26T12:00:00Z Then the rendered watermark text equals "Reviewed by jane@buyer.com at 2025-09-26T12:00:00Z" in all previews And only the supported tokens {email} and {timestamp} are substituted; any other braces render literally And token substitution does not alter the original asset
Audit Log Captures Watermark Settings and NDA Consent
Given a Safe Share is created with watermark settings and an NDA requirement When the share is created and later updated, then viewed, and the NDA is accepted Then the audit log contains records for share_created and share_updated with a snapshot of watermark settings (text, placement, size, opacity) and NDA template/version And the audit log contains an nda_accepted record with consent hash, acceptance timestamp, IP address, user agent, share ID, and recipient identifier And audit log entries are immutable and queryable by share ID
Verifiable Consent Hash and Client Context Recorded
Given an NDA template/body and recipient identifier are stored for a Safe Share And the recipient accepts the NDA at a known timestamp When the consent hash is recomputed from the stored NDA body/version, share ID, recipient identifier, and acceptance timestamp using the documented method Then the recomputed hash matches the stored consent hash And the stored IP address and user agent for the consent event match the values captured at acceptance time
Audit Logging & Access Trails
"As a compliance officer, I want a searchable audit trail of access, exports, and share activity per workspace so that I can prove client data was handled according to policy."
Description

Capture immutable, workspace-scoped audit logs for sensitive actions: authentication, membership and role changes, searches, asset views, exports/downloads, Safe Share creation/usage/revocation, approvals, and NDA acknowledgments. Each entry includes actor, workspace, resource, action, parameters, IP, user agent, and timestamp. Store logs in append-only storage with retention policies and tamper-evidence. Provide a filterable audit UI and export endpoint for compliance reviews, and optional anomaly alerts for patterns like bulk exports or foreign IP access. Ensure logs themselves remain isolated per workspace and are accessible only to authorized roles.

Acceptance Criteria
Append-Only, Tamper-Evident Audit Log Writes
Given a sensitive action is performed in a workspace When the action is committed Then exactly one audit log entry is appended (no overwrite) containing: actor_id, actor_type, workspace_id, resource_type, resource_id, action, parameters (serialized JSON), ip_address (IPv4/IPv6), user_agent, timestamp (UTC ISO 8601 with millisecond precision) And the entry includes a hash and previous_hash enabling chain verification over the workspace’s log stream And any attempt to modify or delete an existing entry is rejected (HTTP 403/409) and leaves the original entry intact And entries can only be purged by an automated retention policy, which writes a non-repudiable tombstone marker referencing the purged range And hash-chain verification over any 24h slice returns no breaks (tamper-evidence intact)
Complete Event Coverage and Field Fidelity
Given each of the following events occurs: authentication (success/failure), membership add/remove, role change, search executed, asset view, export/download queued/completed, Safe Share create/open/expire/revoke, approval (approve/reject), NDA acknowledgment When the event occurs Then a log entry is recorded with action-specific parameters, including at minimum: - auth: outcome, method, mfa_used - membership/role: target_user_id, previous_role, new_role - search: query_string, filters, result_count - view: asset_id - export/download: job_id, asset_count, destination - safe_share: share_id, permissions, ttl, target_scope - approval: asset_id, decision, comment_optional - nda: version_id, acknowledged=true And all required base fields are non-null and pass schema validation And events are recorded within 2 seconds of occurrence (p95)
Workspace-Scoped Isolation and Authorized Access Only
Given logs are stored per workspace When a user with audit.read permission in Workspace A opens the audit UI or queries the audit API Then only Workspace A’s logs are returned And attempting to access Workspace B’s logs returns HTTP 403 and zero records And users without audit.read cannot view or search audit logs even if they can view assets And cross-workspace searches, aggregations, or exports are not possible And Safe Share links never grant access to audit logs
Audit UI Filtering, Sorting, and Pagination
Given an authorized user opens the audit UI in a workspace When they apply filters by actor_id, action, resource_type/resource_id, ip_address, and date range Then the results reflect all filters accurately and exclude non-matching entries And results can be sorted by timestamp ascending/descending And pagination returns stable, non-duplicated results using a cursor with a configurable page size (default 50, max 1000) And the UI displays the exact filter set used and total count (or >= count when >10k with truncation notice)
Compliance Export Endpoint with Scoped Data
Given an authorized user with audit.export permission requests an export via API When they call GET /workspaces/{workspace_id}/audit-logs/export with filters (date_from, date_to, actor_id, action, resource_id) and format=csv|json Then the response streams only logs from the specified workspace and filter window And the export includes header metadata (workspace_id, generated_at UTC, filter summary, record_count) and a SHA-256 checksum of the payload And large exports (>1M rows) are delivered via an asynchronous job with a signed, time-limited download URL And rate limits (e.g., 5 exports/hour/workspace) are enforced with HTTP 429 on excess
Anomaly Alerts: Bulk Exports and Foreign IP Access
Given anomaly alerts are enabled for a workspace When exports totaling >1000 assets occur within 10 minutes or >3 export jobs start within 5 minutes Then an anomaly alert is generated within 60 seconds and delivered to configured channels (email/webhook) with actor_id, counts, and links to audit details When a login or Safe Share access originates from an IP geolocated outside the workspace’s allowed countries list Then an alert is generated with country, ip_address, user_agent, and timestamp And duplicate alerts for the same pattern are suppressed for 15 minutes per workspace
Safe Share and NDA Acknowledgment Audit Trail
Given a Safe Share is created for assets in a workspace When the share is created, accessed, downloaded, annotated, approved, expired, or revoked Then each event writes a log entry with share_id, actor (creator or accessor if authenticated, else anonymous token_id), permissions granted (view/annotate/approve), watermark_enabled flag, ttl/expiry, accessed_asset_id (when applicable), ip_address, user_agent, and timestamp And when an NDA is presented, the user must acknowledge before access and an nda_acknowledgment event is logged with version_id and acknowledged=true And revocation prevents any further access and subsequent access attempts are logged as denied
Workspace Context UX & Safeguards
"As a frequent operator, I want clear workspace context and guardrails so that I don’t accidentally act on the wrong client’s workspace."
Description

Deliver a UX that makes workspace context unmistakable and prevents misdirected actions. Display a persistent, color-coded workspace badge in the header, include workspace names in action confirmations (e.g., “Export from: Acme Retail”), and provide a quick switcher with search. Default to the last active workspace on login, and reset context on logout. Add guardrails for sensitive operations (export, share) with confirmation dialogs and role hints. Ensure drag-and-drop, clipboard, and batch selection cannot cross workspaces. Scope local cache and recent items per workspace to avoid cross-contamination. These safeguards reduce human error and reinforce isolation.

Acceptance Criteria
Persistent Workspace Badge in Header
- Given the user is viewing any page within workspace X, Then a persistent header badge displays the workspace name and assigned color for X with contrast ratio >= 4.5:1. - Given the user switches to workspace Y via the switcher, When the workspace changes, Then the header badge updates to Y within 300 ms and all page data scopes to Y. - Given a screen reader focuses the badge, Then its accessible name is "Workspace: {workspaceName}" and it is announced as a button that opens the switcher. - Given any modal or full-screen view, Then the workspace context remains unmistakable via the header badge or a modal header label that matches the current workspace.
Workspace-Scoped Confirmation for Sensitive Actions
- Given the user initiates Export/Share/Delete in workspace X, Then the confirmation dialog title includes the action and "from: X" and shows the workspace color accent. - Given the confirmation dialog is open for workspace X, When the user attempts to switch workspaces, Then switching is blocked and a notice states "Close dialog to switch workspace". - Given the user confirms the action, Then the API request includes workspaceId = X and the operation is refused if the IDs mismatch. - Given the user cancels, Then no changes are applied and no cross-workspace state persists.
Quick Switcher with Search and Context Persistence
- Given a returning user with last active workspace X, When they log in, Then X loads by default within 2 seconds of authentication. - Given a first-time user, When no last workspace exists, Then they are prompted to choose a workspace before accessing assets. - Given the user opens the quick switcher via the header badge or shortcut Ctrl/Cmd+K, Then they can search by workspace name and see only workspaces they are authorized to access. - When the user selects workspace Y in the switcher, Then context updates to Y, the selection persists as the new last active workspace, and the switcher closes. - Given the user logs out, Then workspace context is cleared so that subsequent logins do not display stale context.
Block Cross-Workspace Drag-and-Drop, Clipboard, and Batch Selection
- Given items are selected in workspace A, When the user attempts to drag them into a view scoped to workspace B, Then the drop is refused with a tooltip "Cross-workspace moves are not allowed" and 0 items are moved. - Given the user copies asset references in workspace A, When attempting to paste into fields in workspace B that accept assets, Then the paste is rejected and an inline message explains the restriction. - Given the user attempts to multi-select items from both A and B, Then selection is constrained to the active workspace and cross-workspace items are auto-deselected. - Given telemetry and server logs, Then no client or server payload contains mixed workspace IDs for a single operation.
Per-Workspace Recent Items, Cache, and Search Scoping
- Given the user views the Recent Items panel in workspace X, Then only items from X are shown; switching to Y immediately replaces the list with Y-only items. - Given local cache keys, Then they are namespaced by workspace ID; clearing cache for X does not affect Y. - Given global search within workspace X, Then results are limited to X, labeled "searching in X", and no items from other workspaces appear even when names match. - Given the user opens a direct item URL from workspace A while in workspace B, Then the app blocks access and prompts to switch to A or requests authorization.
Role-Aware Guardrails and Hints
- Given the current user role lacks permission to Export/Share in workspace X, Then respective actions are disabled or hidden and a tooltip explains "Requires Exporter or Admin". - Given the user opens a confirmation dialog for a sensitive action, Then the dialog shows the user's role and allowed roles for the action and disables the primary action if unauthorized. - When the user's permissions are elevated during the session, Then the UI enables the controls within 60 seconds without a full reload. - Given an unauthorized attempt via API, Then the server responds 403 and the UI surfaces a non-ambiguous error tied to workspace X.

Review Queue

Centralize approvals with a checklist‑driven task board for presets and batches. Annotate images, request fixes, assign owners, and bulk‑approve when checks pass. Built‑in gates tie to Compliance Gate and Text Guard so reviewers see and clear policy flags in one place—shortening back‑and‑forth and speeding safe, consistent sign‑offs.

Requirements

Review Kanban Board & Smart Filters
"As a reviewer, I want a single board to see and triage all items needing approval so that I can prioritize and action work without hunting across batches."
Description

A centralized, real-time task board organizing presets and batch jobs into review states (To Review, Changes Requested, Blocked, Approved). Supports per-marketplace and per-preset views, saved filters, search, and sorting by flags, due date, owner, or impact. Bulk selection enables multi-asset actions while preserving asset-level state. Integrates with PixelLift job and asset models and reflects upstream processing status (e.g., Background Removed, Variants Generated). Board updates via websockets to avoid stale queues.

Acceptance Criteria
Real-time Websocket Updates Keep the Board Fresh
Given a reviewer is viewing the board with an active websocket connection When an upstream job or asset status changes or another user changes a card state Then the affected cards and column counts update on screen within 2 seconds without page refresh Given the websocket connection drops When the client detects the disconnect Then a reconnect banner appears, a 15-second fallback poll begins, and a full data resync occurs within 30 seconds of reconnect Given filters and sorting are applied When live updates arrive Then inserted/removed/moved cards respect the current filters and sort order
Per-Marketplace and Per-Preset Views with Saved Filters
Given the user applies marketplace and preset filters When the filters are applied Then only matching cards are displayed and column totals reflect the filtered set Given a combination of filters and sort is configured When the user saves it as a named view Then the view is stored for that user, persists across sessions, is selectable from Saved Views, and can be set as the default view Given a saved view is deleted by the user When deletion is confirmed Then the view is removed and cannot be selected thereafter
Search and Sort by Flags, Due Date, Owner, and Impact
Given a search term is entered When searching the board Then results match on batch name, preset name, asset SKU, and marketplace listing ID Given the user chooses Sort by Flags When sorting is applied Then items with policy flags appear before non-flagged items, ordered by severity (critical, warning, none) Given the user sorts by Due Date, Owner, or Impact When sorting is applied Then ordering is ascending by default with a toggle to descending, and ties are secondarily sorted by created date (newest first) Given no results match search or filters When the list is rendered Then a "No matches" message is shown with a clear option to reset filters
Bulk Selection Actions Preserve Asset-Level State
Given multiple cards across states are selected When Assign Owner is executed Then the owner field updates on each selected card and a system activity entry logs the actor, timestamp, and new owner Given multiple cards are selected including some with blocking flags When Approve is executed Then only eligible cards transition to Approved, ineligible cards remain unchanged with inline reasons shown, and a summary toast reports success and skipped counts Given multiple cards are selected When Move to Changes Requested is executed Then each card is validated against transition rules; cards failing validation are skipped with reasons and not partially updated
Column State Transitions and Activity Logging
Given the board has columns To Review, Changes Requested, Blocked, and Approved When a card is dragged or actioned to a new column Then the review_state is persisted, column counts update, and an activity entry records actor, from_state, to_state, and timestamp Given a card has blocking Compliance Gate or Text Guard flags When attempting to move it to Approved Then the transition is blocked with a clear error message and link to view flags Given a card is in Changes Requested and upstream fixes complete with no remaining blocking flags When the upstream processing event is received Then the card automatically returns to To Review and the activity feed records the auto-transition
Display and Filter by Upstream Processing Status
Given upstream processing emits statuses (e.g., Background Removed, Variants Generated, Upscaled) When viewing a card Then badges display the latest completed steps and in-progress steps show a spinner Given the user applies a Processing Status filter for Background Removed When filters are applied Then only cards whose assets have Background Removed completed are shown Given a card represents multiple assets When summarizing processing status Then the card displays a progress indicator in the form "x/y steps complete" based on the least-complete asset
Policy Flags Visibility and Gate Clearing for Approval
Given a card has Compliance Gate or Text Guard flags When opening the card details panel Then a flags section lists each flag with severity, rule name, excerpt, and resolution status Given a reviewer resolves or waives a flag with required justification When the action is saved Then the flag state updates, card indicators refresh in real time, and the event is recorded in activity Given all blocking flags are resolved When bulk-approving or moving a card to Approved Then the transition succeeds and completes within 2 seconds
Configurable Review Checklists & Approval Gates
"As a QA lead, I want enforceable checklists tied to marketplace rules so that approvals are consistent and compliant."
Description

Checklist templates per marketplace/preset attach to batches and images at review time. Items can be auto-evaluable (e.g., image dimensions, background transparency) or manual (e.g., brand guidelines). Completion is enforced: bulk approval unblocks only when all required checks pass. Checklist results are stored per asset; auto checks run on upload and re-run on update. UI shows pass/fail with tooltips and links to the offending region where applicable.

Acceptance Criteria
Template Attachment by Marketplace and Preset
Given a checklist template exists for marketplace "Amazon" and preset "Apparel" And a batch is created with marketplace "Amazon" and preset "Apparel" When the batch enters the Review Queue Then the "Amazon-Apparel" checklist is attached to the batch and to each image in the batch And each item is marked as Required or Optional according to the template And auto-evaluable items are queued for evaluation
Automatic Checks on Upload and Re-run on Update
Given auto-evaluable checklist items exist (e.g., dimensions, background transparency) When an image is uploaded into a batch that will enter the Review Queue Then all auto-evaluable checks execute and their pass/fail results are stored on the image before the image can be bulk-approved Given an image already in the Review Queue When the image file is updated or reprocessed Then all auto-evaluable checks are re-run And previous auto-check results are superseded with the latest results And a new evaluation timestamp is recorded
Manual Checks with Annotations and Owner Assignment
Given a manual checklist item is present for an image When a reviewer marks it Pass or Fail Then the selection is saved with reviewer ID and timestamp Given a reviewer adds an annotation to the image for a failing item When the annotation is saved Then the annotation shows as a pin or bounding box linked to that checklist item And the item displays the annotation count Given a checklist item needs work When a reviewer assigns an owner from the team list Then the assignment appears on the item and in task board filters
Bulk Approval Gate Enforcement
Given a set of images is selected for bulk approval When one or more selected images have any Required checklist item failing or incomplete Then the Bulk Approve action is disabled and a tooltip lists the blocking checks per image Given all selected images have all Required items passing When the reviewer triggers Bulk Approve Then all selected images transition to Approved state And an approval record is stored with a read-only snapshot of checklist results at the time of approval
Per-Asset Storage and Audit Trail of Results
Given an image has undergone multiple review cycles When retrieving its review history in the UI Then the system shows each evaluation with item status, evaluator (system or user), timestamp, and linked image version Given a reviewer returns to an image after session restart When the image is opened in the Review Queue Then the last saved checklist statuses and annotations are restored without loss
UI Pass/Fail Indicators with Tooltips and Region Links
Given an auto-evaluable item fails due to a specific region (e.g., non-transparent background area) When the reviewer clicks the failure indicator or tooltip link Then the image viewer navigates to and highlights the offending region Given any checklist item has a pass or fail state When the reviewer hovers over the status icon Then a tooltip explains the rule, threshold, and last evaluation timestamp
Compliance Gate and Text Guard Integration
Given the Compliance Gate or Text Guard detects a policy flag for an image When the image enters the Review Queue Then a corresponding Required checklist item appears showing the policy flag details and status as Fail Given a reviewer addresses the issue and triggers Re-check When Compliance Gate or Text Guard returns Pass Then the checklist item updates to Pass and the approval gate is cleared for that image And manual override is not allowed while the external check is failing
Compliance Gate & Text Guard Integration
"As a compliance reviewer, I want to see and clear policy flags in the same view so that I can approve safely without switching tools."
Description

Surface automated policy flags (nudity, prohibited text, watermark, claims) from Compliance Gate and Text Guard directly in the review pane. Display confidence, rule references, and highlighted regions/text spans. Block approvals until critical flags are resolved or explicitly overridden by authorized roles with reason capture. Support one-click rerun of checks after fixes. Store outcomes to feed compliance reports and model feedback loops.

Acceptance Criteria
Block Approval on Critical Flags
Given an asset has at least one critical-severity policy flag from Compliance Gate or Text Guard When a reviewer attempts to approve the asset or its parent batch Then the Approve action is disabled and a blocking message lists the critical flags by rule name and source And when all critical flags are either resolved or overridden by an authorized user, the Approve action becomes enabled And non-critical flags do not block approval And the blocking state is enforced equally for single-item and batch contexts
Display Flag Details and Highlights in Review Pane
Given an asset contains policy flags When the asset is opened in the review pane Then each flag displays source system (Compliance Gate/Text Guard), rule ID, rule name, severity, confidence (0–100% to one decimal), and timestamp And image-region flags render visible bounding boxes that can be toggled on/off and clicking a flag pans/zooms to its region And text-span flags highlight the exact substring with character offsets and clicking a flag scrolls to the span And hovering a flag shows its rule reference and a link to the policy documentation And the list supports multiple flags and multiple sources without overlap or truncation
Authorized Override with Reason Capture
Given a user with role Compliance Approver or higher views a critical flag When the user selects Override on that flag Then the system requires a non-empty reason (minimum 10 characters) before confirming And upon confirmation, the flag status becomes Overridden and it no longer blocks approval And the audit log stores user ID, role, timestamp, flag IDs, prior status, reason text, and any attached evidence And users without the required role cannot see or invoke the Override control
One-Click Rerun of Compliance Checks
Given an asset with previously detected flags When the reviewer clicks Rerun Checks Then the system triggers fresh evaluations in Compliance Gate and Text Guard and updates results within 10 seconds or shows a retriable error And resolved flags are marked Resolved, new flags are added, unchanged flags remain Open, and all statuses display the latest run timestamp And a diff summary shows counts of Open, Resolved, and New by source system And the Rerun button is disabled with a spinner while a run is in progress to prevent duplicates
Bulk Approval Gate in Review Queue
Given a reviewer selects multiple items for bulk approval When any selected item contains at least one critical flag that is not resolved or overridden Then the Bulk Approve action is disabled and a tooltip indicates the number of blocking items And when all selected items have no critical flags or have them resolved/overridden, the Bulk Approve action becomes enabled And initiating Bulk Approve approves all selected items in a single operation and records an approval event per item
Persist Outcomes for Reporting and Feedback Loops
Given any compliance check run completes for an asset or batch When results are saved Then the system persists per asset: run ID, source system, rule IDs, rule names, severity, confidence, flagged regions/spans, status (Open/Resolved/Overridden), reviewer actions (resolve/override), actor IDs, timestamps, and approval outcome And data is queryable via the compliance reporting endpoint and included in nightly exports And when a reviewer submits flag feedback (e.g., incorrect/false positive) it is recorded with reason code and does not change gating unless an authorized override occurs
Visual Annotations & Threaded Comments
"As an editor, I want precise visual feedback on what to fix so that I can make accurate edits faster."
Description

In-image markup tools (pin, box, arrow, freehand) with color coding, zoom/pan, and per-annotation threads. Mentions, emojis, and attachments supported. Annotations are version-aware and persist across re-uploads with soft diffs. Comments can be resolved, reopening moves asset back to Changes Requested. All actions recorded with timestamps and authors.

Acceptance Criteria
Create and edit annotations with color‑coded tools
Given a reviewer with Edit permission is viewing an asset in the Review Queue When they select Pin, Box, Arrow, or Freehand and a color, then click/drag on the image Then an annotation is created at the clicked coordinates with the chosen shape and color, appears in the sidebar with author and timestamp, and is autosaved within 500 ms And the annotation remains correctly anchored when zoomed between 25% and 400% and during pan on any DPI display And Box/Arrow annotations are resizable, Freehand supports smoothing, and Pin/Box/Arrow can be repositioned via drag And undo (Ctrl/Cmd+Z) and redo (Ctrl/Cmd+Shift+Z) work for the last 20 actions And updates are visible to other viewers within 2 seconds
Threaded comments with mentions, emojis, and attachments on an annotation
Given an annotation exists on an asset When a reviewer opens its thread and types a comment Then Enter posts the comment and Shift+Enter inserts a newline And emojis can be inserted via picker or shortcodes and render in the posted comment And typing @ shows auto‑complete of workspace members; selecting a user inserts a mention and triggers an in‑app notification immediately and email within 1 minute And attachments up to 25 MB each (jpg, png, gif, webp, pdf) can be uploaded; oversize or disallowed types show a clear error and are blocked And authors can edit or delete their own comment within 10 minutes; edits are timestamped with an edited indicator
Resolve and reopen threads update asset state and bulk‑approval eligibility
Given an asset has one or more annotation threads When a reviewer resolves a thread Then the thread status changes to Resolved, it is hidden by default (with a Show Resolved toggle), and the asset’s Open Threads count decrements And if Open Threads reaches 0 and there are no open Compliance/Text Guard flags, the asset becomes eligible for Bulk Approve When any user reopens a resolved thread or posts a new comment in it Then the thread status becomes Open and the asset workflow state is set to Changes Requested And all state changes are captured in the audit log with author and timestamp
Version‑aware annotation persistence with soft diffs after re‑upload
Given an asset with existing annotations is re‑uploaded as a new version When the new version finishes processing Then all prior annotations are reprojected onto the new version using image alignment And at least 95% of annotations land within 10 px of their prior relative position; any exceeding the threshold are flagged with Check Placement And each thread displays a version badge and a toggle to view on vN vs vN+1 And a soft‑diff overlay can be toggled to visualize pixel changes between versions And all thread content and resolution state persist across versions
Audit logging of all annotation and comment actions
Given users perform actions on annotations and threads When an annotation or comment is created, edited, deleted, resolved, reopened, mentioned, or has attachments added/removed Then an immutable audit entry is recorded with ISO‑8601 timestamp, user ID, action type, asset ID, annotation/thread ID, and version ID And the entry appears in the Activity panel within 2 seconds and is included in exports (CSV/JSON) And audit entries cannot be modified or deleted by non‑admins; admin redactions (if any) are themselves logged
Policy flags linked to annotations and clearable from the thread
Given an annotation has an associated Compliance Gate or Text Guard flag When a reviewer opens the thread Then the flag is displayed with rule ID and severity, and the asset shows a badge indicating open flags When a reviewer with Clear Flag permission clears the flag by selecting a reason and posting an explanatory comment Then the gate status updates within 2 seconds, the flag badge is removed, and the action is logged And assets with any open flags cannot be bulk‑approved; attempting to do so shows a blocking message referencing the flags
Assignment, SLAs & Workload Balancing
"As a project coordinator, I want to assign and balance review tasks with deadlines so that throughput stays predictable."
Description

Assign owners and secondary reviewers to batches or individual assets. Set due dates, priority, and SLA targets with color-coded badges and escalations. Auto-assign rules distribute incoming items based on capacity, skill tags, or marketplace expertise. Provide My Queue and Team Workload views with aging metrics and expected completion.

Acceptance Criteria
Manual Assignment to Batch and Assets with Secondary Reviewer
Given a batch with 25 assets exists and the user has Assign permission When the user assigns an owner and a secondary reviewer at the batch level Then all 25 assets inherit the owner and secondary reviewer unless an asset already has an explicit assignment Given an asset has an explicit owner When the batch owner is changed Then the asset retains its explicit owner and only unassigned assets update to the new batch owner Given an assignment is created or changed When the action is saved Then the owner and secondary reviewer receive notifications and the item appears in their My Queue within 30 seconds Given a secondary reviewer is assigned When the owner marks the item Ready for Review Then the item moves to Secondary Review state and is blocked from bulk-approval until secondary review is completed Given an item has assigned owner or secondary reviewer When that user is deactivated Then the item moves to Unassigned and admins receive an alert within 60 seconds
SLA Due Dates, Priority and Color Badges with Escalations
Given a due date and SLA target are set When time remaining is greater than or equal to 25% of the SLA window Then the SLA badge displays Green and no escalation is triggered Given time remaining is less than 25% of the SLA window or due date is within 24 hours Then the SLA badge displays Amber and a heads-up notification is sent to owner and secondary reviewer Given current time exceeds the due date or SLA target is breached Then the SLA badge displays Red and an escalation is posted to the team escalation channel and manager is notified Given item priority is High When queues are sorted by priority Then High items appear before Medium and Low and display a High label Given due dates are stored in UTC When displayed in UI Then show in viewer local timezone with the source timezone in a tooltip
Auto-Assign by Capacity, Skill Tags, and Marketplace Expertise
Given an incoming item has required skill tags and marketplace M When auto-assign runs Then assign to an eligible user whose skills include the tags and marketplace M and who has the lowest projected workload Given two eligible users have equal projected workload When tie-breaking occurs Then select the user with the least recent assignment; if still tied, assign randomly Given no eligible user matches required skills or marketplace Then leave item Unassigned and send a rule-failure alert to admins within 60 seconds Given a user has daily capacity C items and the average effort per item is E When auto-assign distributes items Then do not assign more than C items per day to that user; High priority items may overflow up to 20% with manager notification Given an auto-assign attempt fails due to transient error When retry policy executes Then retry up to 3 times with exponential backoff and log the error context
My Queue View with Aging Metrics and Expected Completion
Given a user opens My Queue with up to 500 assigned items When the view loads Then initial load completes in under 2 seconds (API latency under 300 ms) and shows age since intake, time in current state, due date, priority, and ETA per item based on 14-day throughput Given the user completes an item When completion is recorded Then the item is removed from My Queue within 15 seconds and ETAs recalculate for remaining items Given the user applies filters by status, priority, or due window When filters are applied Then results update within 1 second and filter selections persist for the session Given the user sorts by due date ascending When sorting is applied Then items are ordered strictly by due date then priority without ties misordered Given the user has no assigned items When My Queue is opened Then an empty state is shown with an option to view Unassigned items if the user has claim permission
Team Workload View for Balancing and Bulk Reassignment
Given a team lead opens Team Workload When the view loads Then show per-user assigned count, utilization percentage (assigned effort/capacity), average item age, and SLA risk count Given the lead selects 20 items across three assignees When bulk reassign to a new owner is confirmed Then ownership of all 20 items updates, secondary reviewers are preserved, and notifications are sent within 60 seconds Given any user exceeds 100% utilization When Team Workload is viewed Then a warning indicator displays and suggested reassignees are listed based on skills and available capacity Given the lead uses Rebalance action When executed Then redistribute items from over-utilized users to eligible under-utilized users until all are at or below 90% utilization or no eligible users remain Given any reassignment occurs When the action completes Then an audit log entry records actor, timestamp, items affected, previous and new owners, and reason
Batch vs Asset Assignment Inheritance and Overrides
Given a batch owner and secondary reviewer are set When new assets are added to the batch Then the new assets inherit the batch owner and secondary reviewer by default Given an asset has an explicit owner different from the batch owner When the batch owner changes Then the asset retains its explicit owner unless Force propagate is selected Given Force propagate is selected When applied Then all assets update to the new owner except assets locked by active review; a summary of changes is shown after completion Given a batch is split into two new batches When the split completes Then each asset retains its current owner and secondary reviewer in the new batches Given a batch is archived When archiving completes Then ownership is cleared and pending SLA escalations for its items are canceled within 60 seconds
Bulk Approvals & Structured Fix Requests
"As a reviewer, I want to process similar assets in bulk and issue consistent fix requests so that I minimize repetitive work and speed sign-off."
Description

From the board or detail view, approve, reject, or request fixes on multiple assets at once. Use standardized reason templates and custom notes; selected fixes generate actionable tasks for editors with linked annotations and checklist items. Status transitions respect gates and permissions; partial approvals permitted with clear roll-up state at batch level.

Acceptance Criteria
Bulk Approve from Board View
Given a reviewer with Approver role selects multiple assets in status "Ready for Review" on the board and all selected assets have no unresolved Compliance Gate or Text Guard flags and all required checklist items are complete for each asset When the reviewer clicks "Approve" and confirms the bulk action Then each selected asset transitions to status "Approved" in a single operation and an approval record is stored per asset including actor, timestamp, and method="bulk" and the batch roll-up reflects the new approvals according to roll-up rules
Bulk Reject with Standardized Reasons
Given a reviewer selects one or more assets from board or detail view and chooses the bulk action "Reject" and selects a standardized reason template and enters custom notes When the reviewer confirms the bulk rejection Then every selected asset transitions to status "Rejected" and each asset stores reason_template_id matching the chosen template and the custom notes and the recorded reason and notes are visible to editors on the asset card and detail view
Bulk Request Fixes Creates Linked Tasks
Given a reviewer selects multiple assets and chooses "Request Fixes" and selects standardized fix items and optionally adds annotations and assigns an owner (user or team) When the reviewer confirms the request Then the system creates one actionable task per asset assigned to the chosen owner with task type "Fix Request" including links to the asset, its annotations, and the selected fix checklist items and each asset transitions to status "Changes Requested"
Permission and Gate Enforcement on Bulk Actions
Given a mixed selection containing assets the reviewer can approve, assets the reviewer lacks permission to approve, and assets with unresolved Compliance Gate or Text Guard flags When the reviewer attempts a bulk "Approve" Then only eligible assets transition to "Approved" and ineligible assets remain unchanged with per-asset error reasons (e.g., "Insufficient Permission", "Policy Flags Unresolved") and a post-action summary lists counts of Approved and Not Processed with reasons and successful approvals are not rolled back
Batch Roll-up After Partial Approvals
Given a batch contains assets across multiple statuses including Approved, Changes Requested, Rejected, and In Review When any bulk approval, rejection, or fix request is completed Then the batch roll-up state updates immediately using these rules: Approved if 100% Approved; Partially Approved if 1–99% Approved; Changes Requested if 100% Changes Requested; Rejected if 100% Rejected; Mixed if no single state is 100% and 0% Approved and the roll-up displays counts per state (e.g., Approved X/Y)
Checklist Gating on Bulk Approval
Given the reviewer selects assets where some have incomplete required checklist items When the reviewer attempts bulk "Approve" Then assets with all required checklist items completed transition to "Approved" and assets with incomplete checklists remain in their current status and are listed with the specific incomplete checklist items in the post-action summary
Policy Flag Visibility and Clearance in Bulk Review
Given the reviewer selects assets that include items with Compliance Gate or Text Guard flags When the reviewer opens the bulk action panel Then the panel shows per-asset flag indicators and provides in-place access to view/clear flags and only assets with all flags cleared during the session are eligible for approval while assets with unresolved flags cannot be approved and are listed with their flag reasons in the result summary
Audit Trail & Exportable Review Logs
"As an account admin, I want a complete audit trail and exports so that I can prove compliance and analyze review throughput."
Description

Immutable, queryable history of checklist outcomes, annotations, comments, assignments, approvals, overrides, and compliance checks with timestamps, user, and version IDs. Export CSV/JSON for audits and marketplace disputes; provide API endpoints and webhooks for status changes. Retention and PII policies align with PixelLift data governance.

Acceptance Criteria
Immutable Logging of Review Actions
Given a reviewer performs an action in Review Queue (annotate, comment, assign, checklist update, approve, override) When the action is saved Then the system appends a log entry with fields: event_id (unique), event_type, object_type (image|batch|preset), object_id, version_id, actor_user_id, actor_role, timestamp (UTC ISO 8601 with ms), previous_status, new_status, rationale (optional up to 500 chars), request_id And the entry is retrievable via API/UI with identical values to the write response And no API/UI exists to update or delete existing log entries; attempts return 405 and are logged as security events And any correction is recorded as a new log entry referencing corrected_event_id And the log is append-only: later reads of an existing event_id always return the original payload
Filterable, Paginated Audit Log Search
Given a user with Review Manager or Admin role opens Audit Logs When they filter by any combination of date range, actor_user_id, batch_id, image_id, preset_id, event_type, and compliance_state Then the results include only matching events and display total_count for the current filter And results are sorted by timestamp desc, tie-broken by event_id desc, and are stable across pages And pagination uses opaque cursors; requesting next/prev cursor returns the correct adjacent page And page_size parameter accepts 1–500; values outside the range are coerced to nearest bound And for queries returning ≤5,000 events, the 95th percentile response time is ≤2 seconds And empty-result queries return HTTP 200 with total_count=0 and no records
Capture and Resolution of Compliance Flags
Given Compliance Gate or Text Guard flags an asset during review When the flag is created Then a log event is recorded with fields: policy_id, policy_version, rule_id, severity, confidence (0–1), flagged_excerpt_hash, asset_reference, gate_state=blocked When a reviewer clears or overrides a flag Then a log event is recorded with outcome in {cleared, overridden}, reviewer_user_id, reason (min 15 chars), and gate_state=open And bulk-approve is blocked while any associated asset has gate_state=blocked; attempts to bulk-approve are rejected with HTTP 409 and logged And audit log filters support event_type in {compliance_flag.created, compliance_flag.cleared, compliance_flag.overridden}
UI and API Export to CSV/JSON with Integrity
Given a user selects filters and chooses CSV or JSON format When an export is requested Then the export contains only records matching the filters, with timestamps in UTC and a fixed column/field schema And CSV output is UTF-8 RFC 4180 compliant with quoted fields and escaped delimiters; JSON output is NDJSON (one JSON object per line) And synchronous exports support up to 100,000 rows and complete within 60 seconds or return an async job id And async exports up to 10,000,000 rows complete within 15 minutes 95th percentile and notify the user upon completion And each export includes metadata: generated_at, filter_parameters, row_count, and sha256_checksum And every export request is itself logged with actor_user_id, format, filter hash, and delivered row_count
Webhooks and REST Endpoints for Status Changes
Given a developer registers webhooks for review_status.changed, compliance_flag.created, compliance_flag.cleared, comment.added, assignment.changed When any subscribed event occurs Then a POST is delivered to the callback within 30 seconds with an event payload including event_id, occurred_at (UTC), and resource references And each webhook includes an HMAC-SHA256 signature header over the raw body; consumers can verify with the shared secret And delivery is at-least-once with exponential backoff retries for non-2xx responses for up to 24 hours; duplicate deliveries share the same idempotency_key And GET /audit-logs supports the same filters as the UI, cursor pagination, and returns HTTP 401 for invalid credentials and 429 when rate limits (≥60 req/min/token) are exceeded
Retention, Redaction, and Access Controls
Given a workspace-level retention period is configured When an audit event reaches its retention horizon and is not under legal hold Then the event is purged by a scheduled job and a purge-summary event is recorded And PII fields (e.g., emails, IPs, free-text comments) are masked in API/UI/export outputs by default unless the caller has PII:read permission; masking is deterministic and reversible only for authorized roles And only Admin and Review Manager roles can access audit logs; unauthorized requests get HTTP 403 and return no records And exports and downloads are served from the workspace’s configured data region; cross-region transfers are disallowed without Admin override and are logged
Version IDs and State Reconstruction
Given an image has multiple edits and checklist outcomes over time When a user requests the state as of timestamp T via API Then the API returns the latest event-effective values at or before T for checklist outcome, assignment, approvals/overrides, annotations references, and compliance state And every audit event includes version_id and, when applicable, parent_version_id, enabling lineage traversal And all object_id and version_id references resolve; if a reference cannot be resolved, the write is rejected with HTTP 400 and the attempted write is not persisted And reconstructing state for a batch of up to 1,000 images completes within 10 seconds at the 95th percentile

Freeze Windows

Schedule change freezes around launches or audits. During a freeze, preset edits, role changes, and risky exports are blocked or require break‑glass approval with justification. Set start/end times per workspace, notify stakeholders, and track exceptions in the Audit Ledger—reducing last‑minute surprises and protecting campaign stability.

Requirements

Freeze Window Scheduler
"As a workspace admin, I want to schedule freeze windows with the right time zone and recurrence so that my team avoids risky changes during launches and audits."
Description

Provide workspace-level scheduling of change freezes with explicit start/end times, time zone selection, optional recurrence rules (one-off, weekly, custom), and configurable pre/post buffer periods. Support draft vs published freezes, conflict detection and resolution for overlapping windows, and effective-dates preview in local and workspace time zones. Allow scoping a freeze to specific policy templates (e.g., presets, role changes, exports) and environments/workspaces. Expose create/read/update/delete via UI and API with validation (no past starts without bypass, minimum duration, max horizon). Persist schedules durably with idempotent operations and resilient to clock drift. Display upcoming/active freezes in a calendar/list view and surface countdown badges across relevant product areas.

Acceptance Criteria
Schedule One-Off Freeze with Time Zone and Buffers
Given I select a workspace and time zone And I enter start/end times and optional pre/post buffer durations When I save the freeze as Draft Then the system validates start is not in the past unless bypass=true and the user has FreezeBypass permission And enforces minimum duration and maximum scheduling horizon And stores all timestamps in UTC with workspace/local display metadata When I Publish the draft Then the effective enforcement window equals [start - preBuffer, end + postBuffer] And the freeze appears in Upcoming/Active lists according to server-authoritative time
Define Weekly Recurring Freeze with Exceptions
Given I choose Recurrence=Weekly with selected weekdays and times in the workspace time zone And I add date exceptions/skips When I preview effective dates Then the preview lists the next N occurrences up to the max horizon in both local browser time and workspace time And DST transitions maintain the intended wall-clock times for the workspace time zone When I save Then only non-exception occurrences are persisted and visible in the calendar/list views
Detect and Resolve Overlapping Freeze Windows
Given an existing Published or Draft freeze scoped to the same targets When I attempt to create or publish a freeze whose effective window overlaps Then the system blocks the action with 409 Conflict and a list of conflicting window IDs and times And the action succeeds only after I adjust times, scope, or status so no overlaps remain within the same scope
Scope Freeze to Policy Templates and Environments
Given I select scope targets (e.g., Presets, Role Changes, Exports) and one or more environments/workspaces When the freeze is Active Then actions matching the selected policy templates within the scoped environments/workspaces are blocked with a FreezeActive error And actions outside the scope proceed normally And the Audit Ledger records blocked attempts with freeze ID, actor, action type, and scope
API CRUD with Validation and Idempotency
Given I call POST /freezes with a unique Idempotency-Key and a valid payload When I retry the same request Then only one freeze is created and the same resource is returned (201 on first, 200 on subsequent) And validation rejects past starts unless bypass=true and caller has FreezeBypass permission, enforces minimum duration and max horizon, and returns 400 with field-level errors When I PATCH with an If-Match version Then concurrent updates with stale versions are rejected with 409 When I DELETE a Draft or a Published future-dated freeze Then the resource is soft-deleted and no longer enforced, and GET returns 404 by default
Durable Persistence and Clock-Drift Resilience
Given a freeze is scheduled and the service restarts or nodes scale Then the freeze persists and resumes enforcement without manual intervention And client clock differences do not affect enforcement; server-authoritative time determines Active/Upcoming states And all storage operations are idempotent and retriable without creating duplicates
Calendar/List Views and Countdown Badges
Given one or more freezes exist across scopes When I open the Freeze calendar or list views Then I can filter by workspace, scope, status, and time range and see color-coded Upcoming/Active/Expired items And each item shows start/end, buffers, recurrence indicator, and scope badges When a freeze is Active or Upcoming within 24 hours Then relevant product areas display a countdown badge with time remaining that updates at least every minute and reaches zero exactly at start/end per server time
Freeze Policy Enforcement Engine
"As an operations lead, I want actions to be automatically blocked or gated during freezes so that campaign stability is protected without manual policing."
Description

Implement a centralized, low-latency enforcement layer that evaluates active freeze policies at runtime for targeted actions (preset edits, role changes, risky exports). Actions are either blocked with actionable messaging or routed to break-glass. Provide an extensible action registry with risk tags, per-workspace policy bindings, and precedence handling when multiple freezes apply. Ensure fail-safe behavior (default deny if policy evaluation is unreachable), idempotent decisioning, and consistent enforcement across UI, API, and batch jobs. Include structured error codes, feature flags for phased rollout, metrics and logs for allow/deny outcomes, and high availability with sub-50ms decision time at P95.

Acceptance Criteria
Real‑time Block or Break‑Glass on Risky Actions
Given an active freeze policy targeting preset_edit, role_change, or export:risky in workspace W When an actor initiates a matching action within the policy’s start/end window (UTC) Then the engine returns outcome in {DENY, BREAK_GLASS} per policy rule and action risk tag. Given allow_break_glass=true for the matched policy and the actor provides a non-empty justification When an authorized approver approves the break-glass request Then the action is executed exactly once and an exception record is created with policy_id, actor_id, justification, approver_id, and timestamp. Given allow_break_glass=false for the matched policy When an actor attempts the action Then outcome=DENY and no side effects occur. Given outcome=DENY When the response is returned to the caller Then it contains an actionable message and structured error code referencing the active policy and next steps.
Consistent Enforcement Across UI, API, and Batch
Given identical inputs (actor, workspace, timestamp, action_type, idempotency_key) When the action is invoked via UI, via public API, and via batch job Then all channels receive the same decision (ALLOW/DENY/BREAK_GLASS) and the same structured error code/message. Rule: All client pathways (UI, API, batch) must call the centralized enforcement API; bypass attempts are prevented by integration tests that fail the build if the call is missing. Given a decision is returned to any channel When logs are inspected Then a correlation_id is present and matches the id returned to the caller across all channels.
Multiple Freeze Precedence and Workspace Bindings
Rule: Precedence across overlapping freezes is DENY > BREAK_GLASS > ALLOW; the most restrictive applicable policy wins. Rule: Start boundary is inclusive and end boundary is exclusive; evaluation uses server UTC time. Rule: Only policies bound to the target workspace are considered during evaluation; policies from other workspaces are ignored. Given an action_type that maps via the Action Registry to risk tag T When a freeze targets risk tag T or wildcard * Then the policy is applicable to that action. Given multiple applicable freezes with different outcomes When evaluated Then the winning policy_id and outcome are returned in the decision payload.
Fail‑Safe Default Deny and Idempotent Decisions
Given the enforcement engine is unreachable or exceeds a 200ms evaluation timeout When a decision is requested Then outcome=DENY with error_code=FREEZE.EVAL_UNAVAILABLE and no side effects occur. Given the same action is submitted multiple times with the same idempotency_key within 24 hours When evaluated Then the engine returns the same decision and executes the action at most once if allowed. Given inputs (policy set, actor, workspace, timestamp, action_type) remain unchanged across retries When reevaluated Then the decision outcome is identical.
Observability: Structured Error Codes, Metrics, Logs, and Audit Ledger
Rule: Every DENY and BREAK_GLASS response includes a structured error code in the FREEZE.* namespace and a human-readable message containing policy_id and window (start/end). Rule: Metrics counters (allow_total, deny_total, break_glass_total) and latency histograms are emitted labeled by action_type, workspace_id, policy_id, and outcome. Rule: Structured logs capture correlation_id, actor_id (hashed where required), workspace_id, action_type, outcome, policy_ids_considered, winning_policy_id, latency_ms, and error_code (if any). Rule: Break-glass approvals/denials are written to the Audit Ledger with justification text, approver_id, approver_role, and timestamp; records are immutable and retrievable via API.
Performance and High Availability SLOs
Given representative production load When measuring server-side decision latency over a rolling 1-hour window Then P95 latency <= 50ms (including policy fetch and evaluation) and all decisions meet this SLO. Rule: Monthly service availability for the enforcement API is >= 99.9% as measured by external and internal probes. Given a single Availability Zone outage When traffic is routed to healthy zones Then enforcement remains available and continues to meet the 50ms P95 latency SLO.
Feature‑Flagged Rollout and Kill Switch
Rule: A per-workspace feature flag controls enforcement mode with values {disabled, shadow, enforce}; disabled and shadow modes never block actions, enforce mode applies real blocking per policy. Given feature flag mode=shadow for workspace W When risky actions are evaluated During an active freeze Then callers always receive ALLOW while metrics/logs record the would-be decision (DENY/BREAK_GLASS) with outcome_shadow=true. Given a global kill switch is activated When checked by the engine Then all workspaces receive ALLOW decisions within 60 seconds and logs annotate outcome_override=KILL_SWITCH.
Break-Glass Approval Workflow
"As an admin on-call, I want a break-glass flow that requires justification and approver sign-off so that urgent fixes can proceed with accountability during a freeze."
Description

Provide a controlled exception process that captures requester justification, links to the intended action, and routes to designated approvers (owners/admins) with optional multi-approver rules. Generate time-bound, single-use approval tokens to unlock a specific action or short-lived window. Require mandatory justification categories and free-text reason, and record approver decisions, timestamps, and scope unlocked. Include SLA timers, reminders, and escalation paths; auto-expire stale requests; and support emergency on-call groups. Enforce usage logging and attach resulting diffs to the originating request. Available via UI and API with audit-safe storage and permission checks.

Acceptance Criteria
Break-Glass for Risky Export during Active Freeze
Given a workspace has an active freeze that blocks exports And a user with export permissions attempts a bulk export When the user initiates a break-glass request and links the intended export action And selects a mandatory justification category and enters a free-text reason of at least 20 characters Then the system rejects the submission if the category is missing or the reason is too short And upon a valid submission, the export remains blocked and the request status is set to Pending with a unique request ID And stakeholders configured for the workspace receive a notification within 60 seconds And an Audit Ledger entry is created capturing requester, intended action, justification, and timestamps
Multi-Approver Rule with Owners and Admins
Given the workspace policy requires two approvals: one Owner and one Admin And a break-glass request is Pending When notifications are sent, they are routed to all eligible Owners and Admins Then when the first eligible approver approves with an optional comment, the request status becomes Partially Approved And when a second approver from the other required role approves, the request status becomes Approved And if any required approver rejects, the request status becomes Rejected and the action remains blocked And all approver identities, decisions, comments, and timestamps are recorded in the Audit Ledger
Time-Bound Token for Specific Action or Short-Lived Window
Given a break-glass request is Approved When the system issues an approval token with scope and TTL Then if scope is single-action, the token can unlock only the linked action exactly once within the TTL And if scope is time-window, the token unlocks only the approved action category for the specified workspace for the TTL duration And after a successful use or TTL expiry, the token becomes invalid and cannot be reused And token use records executor identity, timestamps, and outcome, and sets the request status to Consumed And attempts to use the token outside its scope or after expiration are denied and logged
SLA Timers, Reminders, and Escalation
Given an SLA policy of 30 minutes to decision with a reminder at 15 minutes and escalation at 30 minutes When a break-glass request becomes Pending Then a reminder is sent to all pending approvers at 15 minutes And if no decision is recorded by 30 minutes, the request escalates to the designated on-call group and sends an escalation notification And escalation approvers can approve or reject with the same effect as primary approvers And all reminders and escalations are recorded in the Audit Ledger
Auto-Expire Stale Requests
Given a policy that pending requests expire after 24 hours without a final decision When a request remains Pending or Partially Approved for 24 hours Then the request status changes to Expired and cannot be approved thereafter And notifications of expiration are sent to the requester and stakeholders And any subsequent attempt to use or issue tokens for the expired request is denied and logged
Emergency On-Call Break-Glass Path
Given a workspace has an active freeze and an emergency on-call group is configured When a requester selects the Emergency path and submits required justification details Then the request routes only to the on-call group with a 5-minute SLA and a reminder at 2 minutes And an on-call approval issues an appropriate token immediately and notifies stakeholders And the Audit Ledger flags the request as Emergency and records all actions And a post-approval review task is created for Owners and Admins
UI and API Parity with Audit-Safe Storage and Permission Checks
Given a user with permission creates a break-glass request via the UI And the same user creates an equivalent request via the API Then both paths validate required fields and permissions identically and return consistent errors for invalid input or unauthorized access And both requests are persisted in audit-safe storage with immutable logs and redaction of sensitive fields And after the unlocked action executes, the resulting diffs are attached to the originating request within 60 seconds And users without appropriate workspace permissions cannot create, view, approve, or consume break-glass requests
Stakeholder Notifications & Reminders
"As a stakeholder, I want timely notifications before, during, and after freezes so that I can plan my work and respond quickly to exceptions."
Description

Deliver proactive communications for freeze lifecycle events: creation, updates, upcoming start (e.g., T-24h, T-1h), started, ending soon, ended, and exception activity (requests, approvals, rejections). Support channels including email, in-app alerts, Slack/webhooks, and optional calendar invites (.ics). Provide per-user and per-role subscription preferences, quiet hours, and digesting to reduce noise. Ensure deduplication, retry with backoff, idempotent delivery, and message templates with localization. Surface notification history per freeze and expose webhook payload schemas for integrations.

Acceptance Criteria
Notify on Freeze Creation and Updates
- Given a user is subscribed to freeze.created for workspace W via email and in-app, When a new freeze F is created with start/end times, Then within 60 seconds the user receives exactly one email and one in-app notification containing F name, W name, start/end rendered in the user’s timezone/locale, and a deep link to F; if the user has calendar invites enabled, Then the email includes a valid .ics attachment. - Given freeze F is updated (e.g., start/end or title) and the user is subscribed to freeze.updated, When the update is saved, Then an update notification is sent within 60 seconds including a clear change summary (before → after) and, if dates changed, an .ics with the same UID and incremented SEQUENCE. - Given multiple updates to F occur within 2 minutes, When notifications are generated, Then only one consolidated update notification per user per channel is sent containing the latest values. - Given the user’s locale is es-ES, When a notification is rendered, Then all static text is localized to Spanish and date/time are formatted per es-ES. - Given the user has quiet hours active, When a creation or update occurs during quiet hours, Then non–in-app notifications are deferred to the end of quiet hours; in-app is delivered immediately.
Pre-Start Reminder Cadence and Quiet Hours
- Given a freeze F with start time T and a user subscribed to reminders, When current time reaches T−24h and T−1h, Then the user receives a reminder for each cadence via their enabled channels with no more than one message per cadence per channel. - Given T−1h occurs during the user’s quiet hours, When scheduling the reminder, Then the reminder is deferred to the earliest allowed time after quiet hours but no later than T−10m; if that window is missed, Then the reminder is skipped and recorded as skipped due to quiet hours. - Given the user has enabled a daily digest at time D, When the T−24h reminder window falls before D, Then the reminder is included in the digest; When the T−1h reminder occurs, Then it bypasses digesting and is sent individually (subject to quiet-hours deferral). - Given freeze F is canceled or the start time is moved past the trigger before it fires, When the scheduler evaluates reminders, Then the pending reminder is not sent.
Lifecycle Transition Notifications (Started, Ending Soon, Ended)
- Given a freeze F transitions to Started at time T, When the transition is detected, Then subscribed users receive a freeze.started notification within 60 seconds and duplicates are prevented via idempotency key {event_id}:{user_id}:{channel}. - Given a workspace sets ending-soon lead time L (default 15 minutes), When current time is T_end−L, Then subscribed users receive freeze.ending_soon with L indicated in the message. - Given a freeze F transitions to Ended, When the transition is detected, Then subscribed users receive freeze.ended including a summary count of exceptions approved/denied during the freeze window. - Given a freeze is edited to end early, When the new end time is saved, Then ending-soon and ended notifications are recalculated and any previously scheduled ones that no longer apply are canceled.
Exception Activity Alerts (Requests, Approvals, Rejections)
- Given a user submits an exception request R on freeze F, When R is created, Then approvers subscribed to exception.requested receive notifications within 60 seconds including requester, justification, target action, requested time, and a deep link to approve/deny. - Given an approver approves or rejects R, When the decision is saved, Then the requester and watchers subscribed to exception.approved/exception.rejected receive notifications including approver identity, decision, and optional comment. - Given Slack is configured for the workspace, When notifications for the same exception R are sent, Then all follow-up messages are threaded under the original Slack message using thread_ts so recipients see a single conversation. - Given a notification attempt is retried for the same event_id, When the same webhook or Slack message is re-sent, Then idempotency prevents duplicate user-visible messages.
Multichannel Delivery and Calendar Invites (.ics and Webhooks)
- Given supported channels are configured (email SMTP, in-app, Slack, generic webhooks), When an event is emitted, Then delivery is attempted per user preferences on each enabled channel and success/failure is recorded per channel. - Given an email is sent, When processed by the provider, Then SPF/DKIM/DMARC alignment passes and bounces/complaints are captured in delivery status; the From address matches the workspace setting. - Given a webhook endpoint responds with non-2xx or times out after 10s, When delivery is attempted, Then the system retries with exponential backoff up to 6 attempts over 24h with jitter. - Given a calendar invite is generated, When a freeze is created or updated, Then an .ics with a stable UID per freeze, METHOD=REQUEST, and incremented SEQUENCE is attached/available; When a freeze is deleted, Then an .ics with METHOD=CANCEL is sent. - Given webhooks are enabled, When documentation is accessed, Then the JSON payload schema and example payloads are published with a version; each webhook includes X-PixelLift-Signature (HMAC-SHA256) and X-Event-Id headers for verification and traceability.
Subscription Preferences, Role Defaults, Quiet Hours, and Digests
- Given role defaults are defined, When a new user joins a workspace, Then their notification subscriptions default per role (e.g., Admin: all events; Editor: lifecycle and exceptions; Viewer: none) and can be overridden per event type and channel. - Given a user updates preferences for an event type/channel, When the change is saved, Then it takes effect within 5 minutes for subsequent notifications and is recorded in the Audit Ledger with who/when/what. - Given quiet hours are set for a user in their timezone, When a notification is due during that window, Then in-app is delivered immediately and email/Slack/webhooks are deferred to the earliest allowed time unless the due time would be after event relevance, in which case the notification is skipped and marked deferred-skipped. - Given a user enables a daily digest at time D, When multiple eligible events occur before D, Then they are aggregated into a single digest with per-freeze counts and links; users receive no more than one digest per day per workspace.
Delivery Reliability, Deduplication, Idempotency, and Audit History
- Given each outbound message is assigned idempotency key {event_id}:{user_id}:{channel}, When duplicate deliveries are attempted, Then only one user-visible message results per key. - Given a transient failure (HTTP 5xx, SMTP temp failure, Slack 429), When delivery fails, Then retries follow exponential backoff starting at 1 minute doubling to a maximum of 60 minutes, up to 6 attempts, with jitter; success ends retries. - Given multiple freeze.updated events occur within 2 minutes, When notifications are queued, Then they are consolidated into one per user/channel containing the latest values and a list of changed fields. - Given normal operating conditions, When measuring delivery latency, Then P95 latency is <90s for in-app and <5m for email/webhooks across the last 24h; alerts are raised if thresholds are exceeded. - Given a freeze is selected in the UI, When viewing Notification History, Then the list shows each event, timestamp, event type, recipients count, per-channel delivery status, and webhook payload preview; data can be exported as CSV.
Audit Ledger: Freeze Events & Exceptions
"As a compliance auditor, I want a complete, immutable record of freeze events and exceptions so that I can prove control effectiveness and investigate incidents."
Description

Extend the immutable Audit Ledger to capture end-to-end freeze artifacts: schedule creation/updates, enforcement decisions (blocked/gated/allowed), attempted actions with actor/context, break-glass requests and approvals, justifications, tokens issued/used, and resulting changes. Include searchable fields (workspace, user, action type, policy, time range, outcome) with export to CSV/JSON and API access. Implement tamper-evident hashing or write-once storage, retention controls, and PII minimization. Provide correlation IDs to stitch related events and link ledger entries back to affected assets (presets, roles, exports) for investigation and compliance reporting.

Acceptance Criteria
Logging Freeze Schedule Lifecycle
- Given a workspace admin creates a freeze window with start/end/timezone and targets, When the request succeeds, Then the ledger records an immutable event schedule.created with fields: workspace_id, actor_user_id, schedule_id, start_at_utc, end_at_utc, timezone, targets, notification_targets_redacted, created_at, policy_version, correlation_id, seq_no, hash, prev_hash. - Given an existing freeze schedule, When it is updated, Then the ledger records schedule.updated with a JSON Patch of changed fields, prior values redacted for PII, and includes schedule_id, actor_user_id, updated_at, policy_version, correlation_id, seq_no, hash, prev_hash. - Given an existing freeze schedule, When it is canceled, Then the ledger records schedule.canceled with schedule_id, actor_user_id, canceled_at, reason_code, correlation_id, seq_no, hash, prev_hash. - Given the ledger is write-once, When any client attempts to modify or delete a prior ledger entry, Then the operation is blocked and a ledger event ledger.write_denied is recorded with reason_code=IMMUTABLE and the original entry remains unchanged. - Given a schedule is created/updated/canceled, When the action completes, Then the corresponding ledger event is persisted and retrievable via API within 2 seconds (p95) and appears in chronological order by seq_no for that workspace.
Enforcement Decision Logging (Blocked/Gated/Allowed)
- Given an action occurs during an active freeze window, When the enforcement engine evaluates policy, Then a ledger event enforcement.decision is recorded with fields: workspace_id, actor_user_id, action_type, asset_refs, policy_id, policy_version, decision in [blocked,gated,allowed], rationale_code, evaluated_at, correlation_id, seq_no, hash, prev_hash. - Given a decision is blocked, When the response is returned to the caller, Then the enforcement.decision event includes error_code and http_status, and the event timestamp is ≤ the API response timestamp. - Given a decision is gated (break-glass required), When the response is returned, Then the enforcement.decision event includes break_glass_required=true and pending_token_id set to null. - Given overlapping freeze windows, When a decision is computed, Then the recorded event includes all contributing schedule_ids and the applied_effect reflects the strictest outcome. - Given an exemption pre-approval exists, When an action is allowed during a freeze, Then the event includes exemption_id and approver_user_id, and decision=allowed_with_exemption.
Attempted Actions During Freeze: Actor and Context Capture
- Given a user attempts a preset edit, role change, or export during a freeze, When the request reaches the service boundary, Then the ledger records action.attempted with fields: action_id, action_type, actor_user_id, actor_role, auth_method, workspace_id, asset_refs, origin in [ui,api,cli], request_id, request_ip_truncated (/24 for IPv4, /48 for IPv6), user_agent_hash (SHA-256), received_at, correlation_id. - Given PII minimization is required, When action.attempted is recorded, Then direct identifiers (email, phone) are omitted, IPs are truncated, and user agents are hashed as specified. - Given high-throughput conditions, When 10,000 attempted actions occur in 60 seconds, Then ≥ 99.99% of action.attempted events are persisted and queryable within 5 seconds (p95) with no duplicates (by action_id).
Break-Glass Requests, Approvals, and Token Usage
- Given a user requests break-glass for a gated action, When the request is submitted, Then the ledger records breakglass.requested with fields: request_id, actor_user_id, workspace_id, action_type, asset_refs, justification_redacted (emails/phones masked), justification_hash, requested_at, correlation_id, seq_no, hash, prev_hash. - Given an approver reviews a break-glass request, When approval is granted or denied, Then the ledger records breakglass.reviewed with request_id, approver_user_id, decision in [approved,denied], reviewed_at, reason_code, correlation_id. - Given an approved request issues a token, When the token is created, Then the ledger records token.issued with token_id, scope, ttl_seconds, policy_constraints, issued_to_user_id, issued_at, request_id, correlation_id. - Given a token is used to perform a gated action, When the action executes, Then the ledger records token.used with token_id, used_by_user_id, action_id, used_at, outcome in [succeeded,failed], and links to resulting change event_id via correlation_id. - Given a token expires or is revoked, When a use is attempted, Then the ledger records token.denied with denial_reason in [expired,revoked,scope_mismatch] and the original action remains unexecuted.
Search, Filter, API Access, and Export to CSV/JSON
- Given an auditor queries the ledger, When filters workspace_id, user_id, action_type, policy_id, time_range, and decision/outcome are applied, Then the API returns only matching events with pagination (cursor), total_count, and p95 latency ≤ 2s for 1M-event day range. - Given a query result is displayed, When the user requests export CSV, Then a CSV is generated containing the visible fields, includes a header, UTF-8 encoding with RFC4180 compliance, and a SHA-256 checksum file; the export metadata embeds the original query parameters and time window. - Given a query result is displayed, When the user requests export JSON, Then a JSON Lines file is generated with one event per line, preserving field types, and includes a signed manifest with record_count and checksum. - Given access controls, When a non-auditor attempts to export, Then the request is denied (HTTP 403) and a ledger event export.denied is recorded with reason_code=INSUFFICIENT_SCOPE. - Given API access, When a client requests events via the public API with a valid token, Then only events for authorized workspaces are returned and rate limiting headers are present; rate-limit breaches are logged as api.rate_limited.
Tamper-Evidence, Write-Once Storage, and Retention
- Given the ledger uses write-once storage, When any mutation (update/delete) is attempted, Then the storage denies it and the system records ledger.write_denied with reason_code=WRITE_ONCE. - Given hash chaining is enabled, When a new event is written, Then it includes hash and prev_hash, and the daily root hash is signed; a verification endpoint can validate any contiguous range and returns 200 with verification_result=true for unaltered ranges. - Given retention is configured (default 365 days; workspace overrides allowed within 90–1825 days), When entries expire, Then a retention.purged event is recorded with workspace_id, from_to_timestamps, purged_count, executed_at, and purge_job_id. - Given legal hold is applied to a workspace, When retention would otherwise purge events, Then those events are preserved and a retention.skipped event is recorded with hold_id and scope. - Given clock skew, When events are written from distributed services, Then seq_no ordering is monotonic per workspace and does not regress even if event timestamps differ by up to ±5 minutes.
Correlation IDs and Asset Linking
- Given related freeze artifacts occur (decision, request, approval, token usage, resulting change), When events are written, Then all share a non-null correlation_id and can be retrieved in order via GET /ledger?correlation_id=... returning a contiguous sequence of events. - Given an event references affected assets, When viewed in the UI or via API, Then preset_id, role_id, and export_job_id are present as applicable, and each includes a deep link to the asset detail; links resolve 200 if asset exists or 404 with code=ASSET_TOMBSTONED if deleted, with tombstone metadata retained in the event. - Given an investigator starts from an enforcement.decision event, When they follow the correlation and asset links, Then they can reach the exact resulting change event (e.g., export.started/export.completed or preset.updated) within 3 clicks/API calls.
Risk Classification & Scope Configuration
"As a workspace owner, I want to configure which actions are in scope and considered risky so that freeze policies match our operational risk profile."
Description

Introduce a configurable action taxonomy and risk model to define what constitutes a risky export or sensitive change. Maintain a registry of actions with risk levels, default policy templates, and per-workspace overrides. Allow admins to simulate what-if evaluation to preview which actions will be blocked during a given freeze. Support versioning of configurations, change reviews, and safe rollout to workspaces. Ensure compatibility across UI, API, and batch pipelines so that enforcement can target precise scopes without false positives.

Acceptance Criteria
Action Taxonomy Registry with Risk Levels
Given I am an authenticated admin When I create an action type with fields {key, name, description, category, riskLevel in [Low, Medium, High, Critical]} Then the action is persisted in the global registry with a unique key, createdBy, createdAt, and riskLevel And the API returns 201 Created with the saved record When I attempt to create another action with the same key Then the API returns 409 Conflict and the registry remains unchanged When I update name, description, category, or riskLevel of an existing action Then the record version increments by 1 and an Audit Ledger entry captures actor, timestamp, and field-level diff When I archive an action type Then it is excluded from future policy evaluations, remains queryable via includeArchived=true, and the Audit Ledger records the archival
Default Policy Templates and Workspace Overrides
Given platform default policy templates map riskLevels and categories to effects {Allow, RequireBreakGlass, Block} When a new workspace is created Then the default template is applied as workspace policy version 1 When a workspace admin creates an override at category or action level with an effect in {Allow, RequireBreakGlass, Block} Then the override takes precedence over the platform default for that workspace only And precedence order is: action-level override > category-level override > platform default When an override is deleted Then evaluation falls back to the next lower precedence rule And all create/update/delete operations write entries to the Audit Ledger with actor, justification (if provided), and diff
What‑If Simulation of Freeze Impact
Given a proposed freeze window with {start, end, timezone}, a selected workspace, and a chosen policy version When I submit a simulation request via UI or API with a list of N action keys (N ≤ 1000) and optional user role and channel in {UI, API, Batch} Then the response returns, for each action, a decision in {Allowed, RequireBreakGlass, Blocked}, matchedRuleId, matchedScope {Default|Category|Action|Override}, effectivePolicyVersion, and explanation And for N ≤ 1000, the p95 response time is ≤ 2 seconds When the window is entirely outside the freeze period Then decisions reflect non-freeze behavior (no additional blocking) When a role or user is exempted by policy Then the simulation shows Allowed and identifies the exemption rule And the simulation performs no side effects beyond a SimulationRequested entry in the Audit Ledger
Policy Versioning, Review, and Safe Rollout
Given policy changes start as Draft When an admin submits a Draft for review Then the policy transitions to PendingReview and notifies designated reviewers When a reviewer (not the author) approves the change Then the policy transitions to Approved with an immutable version number and recorded approver When an owner schedules a rollout targeting a set of workspaces (by IDs or labels) with effectiveAt timestamp Then only those workspaces enforce the new version at/after effectiveAt; others remain on their current version When a rollback to a previous version is initiated Then the selected workspaces revert within 5 minutes and the rollback is captured in the Audit Ledger with justification And every state transition (Draft -> PendingReview -> Approved -> Released -> RolledBack) is recorded with actor, time, and diff
Enforcement Across UI, API, and Batch Without False Positives
Given a freeze is active for workspace W under policy version V When a risky export flagged as Block is attempted via UI Then the action is prevented, no images or listings are modified, and the UI shows a message with riskLevel, matchedRuleId, and link to request break-glass if applicable When the same Blocked action is attempted via API Then the API returns HTTP 423 Locked with a machine-readable error containing decision=Blocked, matchedRuleId, and correlationId When a RequireBreakGlass action is attempted via Batch pipeline Then the job is queued in a Paused state pending approval, no side effects occur, and an event is emitted with decision=RequireBreakGlass When a non-risky action is executed during the freeze Then it completes successfully without warnings And in a curated verification suite, 0 of 100 non-risky operations are erroneously blocked, and 100% of 50 risky operations are blocked or require justification per policy
Break‑Glass Approval with Justification and Single‑Use Scope
Given a policy marks specific actions as RequireBreakGlass When a requester submits a break‑glass request with free‑text justification (≥ 15 characters) and a reason code from a configured list Then the request is logged with requester, workspace, action, matchedRuleId, and SLA target (e.g., 2 hours) When an approver with the required role reviews the request Then they can Approve or Deny; self‑approval by the requester is rejected When approved Then a single‑use exemption token valid for 30 minutes is issued and allows exactly one execution of the specified action and scope; subsequent attempts require a new approval When the token is used or expires Then it cannot be reused, and all events (approve, execute, expire) are recorded in the Audit Ledger with correlationId

Preset Genie

Auto-builds a marketplace‑ready preset from your first 10 images, explaining each choice (background, margins, file type) in plain language. Accept as‑is or tweak with one‑tap options (Clean, Pro, Brand). Get a compliant setup in minutes without learning the rules first.

Requirements

Auto Preset Inference from Sample Set
"As a solo e-commerce seller, I want the system to auto-generate a marketplace-ready preset from my first 10 images so that I can start editing at scale without learning complex rules or settings."
Description

Automatically analyzes a seller’s first 10 uploaded product images to infer an optimal, marketplace-ready editing preset. The system detects subject boundaries, background type, dominant aspect ratios, and safe crop margins, then proposes background removal or replacement, margin percentages, output format (JPEG/PNG/WebP), color profile (sRGB), resolution targets, and compression quality. It aggregates signals across the sample set to choose defaults that generalize, flags outliers, and prepares a cohesive preset that can be applied to the full batch. Built on PixelLift’s segmentation and background removal engines, it runs asynchronously with progress feedback, caches intermediate results, and gracefully handles mixed lighting and product categories.

Acceptance Criteria
Baseline Preset Inference from 10-Image Sample
Given a seller uploads 10 product images where a single aspect ratio bucket from {1:1, 4:5, 3:4, 16:9} is observed in ≥70% of images within ±3% and ≥80% have a uniform background score ≥0.95 When Preset Genie runs auto inference Then it selects that aspect ratio as the default crop, proposes background removal = false and replacement = none, sets safe margins so that ≥95% of subject-mask pixels lie ≥3% of the shorter edge from all borders in at least 8/10 images, chooses color profile = sRGB, output format = JPEG at quality 85 unless transparency need is detected in ≥40% of images, and resolution target = median longer edge rounded to the nearest 100 px within [1200, 3000] px
Outlier Detection and Flagging in Mixed Sample Set
Given a 10-image sample where some images deviate in aspect ratio, subject area ratio (mask area ÷ image area), or background uniformity from the sample distribution by more than 1.5 IQR When auto inference runs Then each deviating image is flagged as an outlier with reason(s), and on a labeled test set the detector achieves ≥90% recall with ≤5% false-positive rate
Asynchronous Processing with Progress Feedback
Given a 10-image sample totaling ≤100 MB When auto inference starts Then a progress indicator appears within 2 seconds, updates at least every 1 second, shows completed/total images, and the job completes within ≤60 seconds at p95 under nominal load; if a step fails transiently, the UI shows a retriable error and offers Retry
Intermediate Results Caching and Resumability
Given auto inference is in progress and the user refreshes the page or loses network for ≤30 minutes When the user returns to Preset Genie Then processing resumes from cached intermediate results without reprocessing completed images, last-known progress is restored within 3 seconds, total recompute is reduced by ≥80% vs cold start, and caches older than 60 minutes are evicted
Preset Choice Explainability and One-Tap Tweaks
Given a generated preset is ready When the user opens Preset Details Then each chosen parameter (background policy, margins, aspect ratio, output format, resolution, quality, color profile) displays a plain-language explanation citing sample metrics (counts, medians, thresholds), and tapping Clean, Pro, or Brand applies deterministic adjustments (Clean: margin 3%, JPEG q=80; Pro: margin 5%, JPEG q=90; Brand: margin 5%, background = brand color or #F8F8F8, PNG if transparency detected) and renders a side-by-side preview within 2 seconds
Cohesive Preset Application to Full Batch and Export
Given a batch of ≥100 images from the same seller is queued after acceptance of the inferred preset When the preset is applied to the batch Then ≥95% of outputs meet the target aspect ratio within ±1% and safe margins within ±1% of configured values, 100% of files are tagged with sRGB, file format selection follows the preset rules (JPEG unless per-image transparency area >0.5%, else PNG; WebP if marketplace support is true and yields ≥20% size reduction at SSIM ≥0.98), and the ZIP export with manifest.json is downloadable within 2 minutes at p95 for 100 images
Marketplace Compliance Validation Engine
"As a seller publishing to specific marketplaces, I want the preset verified against each marketplace’s rules so that my images are accepted the first time and I avoid relisting delays."
Description

Maintains up-to-date rule packs for major marketplaces (e.g., Amazon, eBay, Etsy) and validates every parameter of the generated preset against those rules before application. Checks include background color thresholds (e.g., pure white), minimum longest-side resolution, product coverage/margin percentages, forbidden elements (watermarks, borders), aspect ratio constraints, file type and size limits, and color space. Users can select a target marketplace or allow auto-detection from listing metadata; the engine highlights any conflicts and proposes compliant adjustments with one click. Rules are versioned and remotely updateable without app redeploys.

Acceptance Criteria
Manual Marketplace Selection Validation
Given the user selects "Amazon-US" as the target marketplace in Preset Genie And a preset is generated from exactly 10 input images When the Validation Engine runs Then each image and the preset parameters are checked against the current Amazon-US rule pack for: background color threshold, minimum longest-side resolution, product coverage/margins, forbidden elements, aspect ratio, file type, file size, and color space And a per-parameter pass/fail result is displayed for each image And the preset overall status is "Compliant: Amazon-US" only if all parameters pass for all images And the Validation Report includes the rule pack name and semantic version used
Auto-Detect Marketplace From Listing Metadata
Given listing metadata contains marketplace=Etsy and region=US When the user chooses Auto-Detect Then the engine selects the "Etsy-US" rule pack And the selection is visibly surfaced to the user with the ability to override before applying And if metadata is missing or ambiguous, the engine prompts for manual selection and does not auto-apply any rule pack And if the user overrides the selection, the engine revalidates against the chosen rule pack
Background and Product Margin Compliance
Given a selected marketplace rule pack defines background color as pure white (#FFFFFF) and product coverage 85–95% of image area When an image background mean/variance exceeds the white threshold or product coverage is outside limits Then the engine marks those checks as Fail and highlights the offending regions on the preview And it proposes specific adjustments (e.g., set background to #FFFFFF; crop/pad to achieve coverage within 85–95%) And a one-click "Apply Fix" applies the proposed adjustments and re-runs validation And after applying fixes, the checks show Pass if thresholds are met
Forbidden Elements Detection
Given the rule pack forbids watermarks, text overlays, logos, borders, or corner badges When an image contains any forbidden element with confidence ≥ 0.90 Then the engine flags the image with Fail for "Forbidden elements" And it outlines detected regions and lists detected types with confidence scores And it proposes removals (inpainting), crops, or background re-rendering as available one-click fixes And images with no detections or confidence < 0.90 pass this check
Resolution, Aspect Ratio, File Type/Size, and Color Space
Given the rule pack specifies minimum longest-side resolution, allowed aspect ratios, allowed file types, maximum file size, and required color space (e.g., sRGB) When a preset is validated Then each image passes only if resolution ≥ minimum, aspect ratio within allowed set, file type in allowed list, file size ≤ maximum, and color space matches required And for any failing parameter the engine proposes a deterministic adjustment (resample, pad/trim, transcode, compress) with predicted post-fix values And a one-click "Make Compliant" re-encodes images to meet all file constraints in a single pass and re-validates
Conflict Highlighting and One-Click Remediation Flow
Given the validation report contains any Fail results When the user clicks "Make Compliant" Then the engine presents a checklist of proposed fixes per parameter with default selections to reach compliance And applying fixes updates previews, records a change summary (before/after values), and re-runs validation automatically And if all checks pass, the overall status updates to "Compliant" and the "Export" action is enabled And users can accept/skip fixes per image; skipped items remain Fail and block "Compliant" status
Remote Rule Pack Versioning and Updates
Given the server publishes a new rule pack version for a supported marketplace (e.g., Amazon-US) When the client checks for updates Then the new version is fetched and activated without application redeploy And the rule pack version and release date are visible in the Validation Engine UI And presets record the rule pack version used for validation at the time of save And re-validating an older preset uses the latest version unless the user locks it to the saved version And if compliance status changes due to a rule update, the user is notified with a diff of changed rules
Plain-Language Choice Explanations
"As a non-technical seller, I want clear explanations of each preset choice so that I can trust the output and tweak settings confidently."
Description

Generates human-readable explanations for each preset decision in a single summary panel and inline tooltips. For example: why white background was chosen, why margins were set to a specific percentage, why JPEG vs PNG, and how compression affects file size and clarity. Explanations reference the relevant marketplace rules in plain language and cite the observed patterns in the user’s images. Content is concise, non-technical, and consistent in tone, enabling trust and quick understanding. Supports hyperlinking to documentation and provides a brief rationale for any compliance-driven overrides.

Acceptance Criteria
Summary Panel Explains Each Preset Decision
Given a user completes Preset Genie on 10 images and opens the Summary panel Then the panel displays explanations for background choice, margin percentage, file format (JPEG or PNG), and compression level And each explanation states: the choice, a plain-language why, and the expected impact (e.g., on file size/clarity) And each explanation references the selected marketplace rule in plain language and includes at least one hyperlink to the source documentation And each explanation references at least one observed pattern from the user’s 10 images (e.g., "8/10 images had busy backgrounds") And each explanation is 30–60 words, grade level ≤ 8, and contains no jargon or acronyms beyond JPEG/PNG And the panel contains no unresolved placeholders or missing fields
Inline Tooltips Provide Contextual Explanations
Given the preset review screen is open When the user hovers, focuses (Tab), or taps the info icon next to Background, Margins, File Type, or Compression Then a tooltip appears within 200 ms positioned adjacent to the control And the tooltip contains 1–2 sentences (≤ 180 characters) that summarize the reason for the decision in plain language And the tooltip content is consistent with the corresponding Summary panel explanation for the same decision And tooltips are dismissible by moving pointer away, blurring focus, tapping outside, or pressing Esc And tooltips meet WCAG 2.2.2 (Hover or Focus) and use role="tooltip" with an accessible name
Marketplace Rule References and Links
Given an explanation references a marketplace rule (e.g., Amazon Main Image) Then it includes a human-readable rule label and a paraphrase in plain language And it contains at least one hyperlink with descriptive text (not 'click here') pointing to the specific rule page And links open in a new tab/window without breaking keyboard focus And each link returns HTTP 200 at render time; otherwise, an inline notice 'Link temporarily unavailable' is shown and the rest of the explanation still renders
Observed Image Pattern Citations Are Accurate
Given the system analyzes the first 10 images When explanations cite counts, percentages, or examples from those images Then the cited numbers match the computed analysis within ±1 for counts or ±2% for percentages And no cited metric is fabricated (every number maps to a stored analysis value) And explanations use concrete phrasing (e.g., '7 of 10') instead of vague terms (e.g., 'most') when a numeric value exists
Compliance-Driven Override Rationale Is Clear
Given a user’s initial preference conflicts with a marketplace rule When Preset Genie applies an automatic override (e.g., background forced to white or margins increased) Then the Summary panel shows a labeled 'Compliance override' explanation for the affected decision And the explanation states what changed, why it changed (plain-language rule), and links to the rule And the explanation includes a brief, safe next-step (e.g., 'You can adjust margins within 5–10% and stay compliant') And the explanation is ≤ 35 words and grade level ≤ 8
Readability and Tone Consistency
Given any explanation or tooltip is rendered Then the text reads in second person ('you'), present tense, and active voice And Flesch-Kincaid Grade Level ≤ 8 and average sentence length ≤ 20 words And a predefined jargon list (e.g., 'chrominance', 'lossy quantization', 'alpha premultiplication') does not appear And terminology for file types is limited to 'JPEG' and 'PNG' and compressions to 'low/medium/high' with no technical parameter codes
One-Tap Preset Variants: Clean, Pro, Brand
"As a busy seller, I want one-tap options like Clean, Pro, and Brand so that I can quickly align the preset with my style without digging into advanced settings."
Description

Provides three curated, one-tap variant overlays on top of the auto-generated preset: Clean (minimal, rule-first), Pro (adds subtle contrast, clarity, natural shadows, and color balance for visual pop), and Brand (applies brand color backdrop or gradient, branded margin style, and optional soft shadow) while staying within compliance constraints for the selected marketplace. Each variant defines a deterministic set of parameter deltas and can be toggled on/off with instant preview and easy revert. Variant selections are persisted in the final preset and compatible with batch processing.

Acceptance Criteria
Toggle Variant with Instant Preview
Given an auto-generated preset is loaded for a product image in Preset Genie When the user taps the "Clean", "Pro", or "Brand" variant Then the preview updates to reflect the selected variant within 800 ms for a 10 MP test image on a <150 ms latency network And exactly one variant chip is marked active matching the selection And the delta summary lists all parameters and their values applied by the selected variant When the user switches from one variant to another Then the preview updates within 800 ms per switch and the active state updates accordingly
Revert to Base Preset
Given a variant (e.g., "Brand") is active on the auto-generated preset When the user taps "Revert to Base" or toggles the active variant off Then all variant-specific parameter deltas are removed And the effective parameter set exactly equals the original auto-generated preset (bit-for-bit in serialized preset JSON) And the preview updates within 800 ms to match the base preset And the active variant indicator is cleared
Compliance Guardrails for Brand Variant
Given marketplace compliance rules are loaded for the selected marketplace And the configured brand backdrop/gradient or margin style would violate a rule When the user activates the "Brand" variant Then Save is disabled until no critical violations remain And the system either auto-adjusts to the nearest compliant alternative or blocks the violating setting and shows an inline explanation referencing the specific rule And after auto-adjustment, the saved preset passes the compliance validator with 0 critical violations
Deterministic Parameter Deltas per Variant
Given a base preset P and a variant V (Clean/Pro/Brand) at variant-config version v1 When V is applied to P in two separate sessions on two devices Then the computed parameter delta set is identical (same keys and values) and yields the same canonical hash And reapplying V is idempotent (no further changes after the first application) And the recorded variant identifier and version (e.g., V=v1) are stored with the preset
Persist Variant in Saved Preset and Batch
Given the user applies the "Clean" variant and saves the preset as "Amazon Shoes" When the user reopens Preset Genie and loads "Amazon Shoes" Then "Clean" is shown as the active variant and the effective parameters match the previously saved state When the user runs a batch job on 50 images using "Amazon Shoes" Then each output uses the "Clean" deltas and the job manifest/metadata records variant=Clean and the variant-config version And no per-image overrides are required to maintain the variant behavior And resuming a paused job preserves the same variant behavior
Batch Performance Overhead with Variants
Given a base preset and 100 test images (longest edge ≤ 3000 px) When processing the batch once with no variant and once each with Clean, Pro, and Brand Then the average per-image processing time with a variant is ≤ 5% slower than the no-variant baseline And the export success rate is 100% for all runs And peak memory usage per worker with a variant is ≤ 1.5× the no-variant baseline
Batch Preview and A/B Variations
"As a performance-focused seller, I want to preview and generate A/B-ready variations before full export so that I can choose the look most likely to lift CTR."
Description

Renders fast previews of the generated preset plus selected variants across a representative subset of the uploaded images (e.g., 6 of the 10 samples). Users can compare side-by-side, approve the base preset, and optionally generate A/B-ready variations with controlled differences (background tone, margin tightness, shadow style) for export. The system delays full processing until approval, reducing compute spend, and stores preview artifacts for quick replays. Integration with PixelLift’s bulk export tags each variation for easy tracking in downstream listings.

Acceptance Criteria
Preview Subset Selection & Speed
Given a user has uploaded at least 10 images and a base preset was generated by Preset Genie When the user opens Batch Preview Then exactly 6 preview images are selected deterministically from the first 10 to maximize diversity by aspect ratio and product size And the same 6 images are selected on replay unless the preset or upload set changes And preview resolution is 1200px on the longest edge And the first preview tile renders within 2 seconds and all six render within 8 seconds on a 20 Mbps connection
Side‑by‑Side Comparison and Base Preset Approval
Given the Batch Preview grid is visible When the user enters Compare mode on any item Then the base preset preview and up to 3 variant previews display side-by-side in aligned columns And a 1:1 zoom toggle is available and synchronized across columns And the Approve Base Preset action is enabled When the user clicks Approve Base Preset Then the batch state changes to Approved and a confirmation is shown And Export controls become enabled
Controlled A/B Variation Generation
Given the user has selected variation controls for background tone, margin tightness, and/or shadow style When the user creates A/B variations Then up to 4 variations (A–D) are created And variations differ only in the selected controls while all other preset parameters remain identical And background tone deltas are applied in ±5% luminance steps, margin tightness in ±3% canvas padding steps, and shadow style in {None, Soft, Grounded} And variant labels (A–D) are consistently shown in UI, filenames, and manifest And previews for all variants across the 6 preview images render within 12 seconds total
Deferred Full Processing Until Approval
Given the batch is in preview state Then no full-resolution processing jobs are queued or written to storage And only preview artifacts are generated When the user clicks Approve Base Preset Then full-resolution processing jobs for all images in the batch are enqueued within 3 seconds And if the user discards the preview, all preview jobs are cancelled within 5 seconds
Preview Artifact Caching and Replay
Given preview artifacts have been generated When the user reopens the same batch within 7 days without preset changes Then the preview grid loads from cache within 1.5 seconds And the selected subset and previews are identical to the prior session When the preset is modified Then only affected previews are re-rendered and the cache is updated accordingly
Bulk Export with Variation Tags and Manifest
Given the base preset is approved and variations A–D exist When the user exports the batch Then each original image is exported for each selected variant with filenames in the pattern {image_id}_{variant_label}.{ext} And each file and record is tagged with preset_id, batch_id, and variant_label metadata And a manifest (CSV and JSON) is generated containing image_id, variant_label, variant_deltas, preset_id, batch_id And the export includes equal counts per variant or warns the user if any are missing And PixelLift bulk export integration recognizes variant tags for downstream tracking
Preset Save, Versioning, and Reuse
"As a repeat seller, I want to save and version presets so that I can reuse and refine them across product batches and marketplaces without starting from scratch each time."
Description

Enables saving the auto-built preset as a named asset with version history, diffable parameter changes, and one-click rollback. Users can duplicate a preset to another marketplace target, share it with teammates in the same workspace, and set it as the default for future uploads. Each saved preset persists its chosen variant (Clean/Pro/Brand), compliance target, and the explanation snapshot for auditability. Backwards-compatible storage ensures older presets continue to work after rules update.

Acceptance Criteria
Save Named Preset with Variant and Compliance Snapshot
Given I have an auto-built preset from Preset Genie for marketplace "Amazon US" with variant "Pro" When I save it with the name "Amazon-Pro-2025" and optional description Then a preset is created with a unique ID and the saved name And the preset persists the chosen variant, compliance target, and the explanation snapshot for audit And the preset appears in My Presets within 2 seconds of save confirmation And saving a duplicate name within the same workspace is rejected with a clear, actionable error And the preset can be retrieved in the workspace presets list and via API by ID
Versioning on Edit with Diff View
Given an existing saved preset at version v1.0 with defined parameters When I modify any parameters (e.g., margins from 4% to 6%, background from #FFFFFF to #FAFAFA) and save Then a new version v1.1 is created without overwriting v1.0 And the diff view lists exactly the changed parameters with before/after values And unchanged parameters are not displayed in the diff And version metadata captures author, timestamp (UTC), and change note And the diff can be exported/downloaded as JSON
One-Click Rollback to Prior Version
Given a preset with versions v1.0, v1.1, and v1.2 When I select "Rollback" on v1.0 and confirm Then a new version v1.3 is created whose parameters match v1.0 exactly And v1.3 becomes the active version for all subsequent processing and API calls And an audit log entry records actor, timestamp, source version, and target version And the diff between v1.2 and v1.3 reflects all reverted parameters
Duplicate Preset to New Marketplace Target
Given a preset configured for compliance target "Amazon US" When I choose "Duplicate" and select target "Etsy" Then a new preset copy is created with identical parameters and chosen variant And the compliance target is set to "Etsy" on the copy And marketplace-specific incompatibilities, if any, are flagged with inline guidance and must be resolved before save And the new preset metadata references the source preset ID And duplication completes within 3 seconds under normal load
Workspace Sharing and Access Control
Given a workspace with members Owner, Editor, and Viewer roles When the Owner shares a preset with the workspace Then all members can view and use the preset immediately And only Owner and Editor can modify or version the preset; Viewer is view/use-only And non-members cannot access the preset via UI or API even with a direct link And revoking sharing removes access within 60 seconds and cancels pending share links And share/unshare actions are recorded in the audit log
Set Preset as Default for Future Uploads
Given multiple presets exist in the workspace When I set preset "Amazon-Pro-2025" as the default Then subsequent uploads via Web UI and API auto-apply this preset unless an explicit override is provided And the uploader UI displays the active default clearly And if the default preset is deleted or disabled, the system reverts to "No preset" and notifies the user And a workspace admin can set and clear a workspace-level default that supersedes personal defaults
Backwards Compatibility After Compliance Rule Updates
Given a saved preset created before a marketplace compliance rules update When the platform updates the global rules for that marketplace Then processing with the old preset produces identical outputs to a baseline run captured with that preset version And the preset remains usable without forced migration And the UI displays a non-blocking notice suggesting migration for improvements And the original explanation snapshot remains retrievable for audit purposes
Performance and Cost Guardrails
"As a cost-conscious seller, I want preset creation to be fast and predictable so that I can iterate quickly without unexpected delays or charges."
Description

Sets explicit performance and cost targets for Preset Genie: inference from 10 images completes under 90 seconds at P95, previews render under 2 seconds per image, and the process surfaces an estimated per-image processing cost before batch apply. Implements concurrency controls, caching of segmentation masks, and adaptive quality to stay within SLA and budget limits. Provides real-time progress, timeouts with safe fallbacks, and graceful degradation for oversized or corrupt inputs.

Acceptance Criteria
Preset inference P95 under 90s for 10 images
Given Preset Genie is invoked with 10 valid product images When measured over at least 100 independent runs in a production-like environment Then the P95 end-to-end inference time per job is <= 90 seconds And no more than 1% of jobs exceed 120 seconds And any job exceeding 120 seconds triggers a timeout and safe fallback with user notification within 5 seconds
Preview render latency under 2s per image
Given a generated preset is available and previews are requested for 10 images When rendering previews over at least 200 requests Then the P95 time to first full-resolution preview per image is <= 2.0 seconds And no more than 1% of previews exceed 3.0 seconds And cached repeat preview requests have P95 latency <= 0.5 seconds
Per-image cost estimate before batch apply
Given Preset Genie has computed a preset When the user opens the Apply Preset step Then a per-image processing cost estimate is displayed before the Apply action is enabled And the estimate includes currency code, per-image, and total for the selected batch size And the estimate is computed and shown within 500 ms of step load And if estimation is unavailable, the UI displays a clear message and requires explicit user acknowledgement to proceed
Concurrency controls maintain SLA under load
Given multiple tenants submit Preset Genie jobs concurrently When incoming concurrency exceeds configured per-tenant and global limits Then the service enforces per-tenant concurrency cap and global cap as configured And excess jobs are queued with fair scheduling (no tenant is starved; max wait variance <= 2x between tenants at the same queue depth) And SLA targets for in-limit jobs are maintained (inference P95 <= 90s; preview P95 <= 2s)
Segmentation mask caching accelerates repeats
Given an image previously processed within 24 hours with unchanged segmentation-affecting parameters When the same image is processed again Then the previously computed segmentation mask is reused And the end-to-end processing time for that image is reduced by at least 30% compared to a cold run And the cache is invalidated when the image content hash or relevant parameters change And cache memory stays within the configured limit and evicts least-recently used entries when exceeded
Adaptive quality to meet SLA and budget limits
Given the estimated cost or predicted latency for a job exceeds configured limits When Preset Genie is executed Then the system automatically adjusts quality parameters (e.g., model variant, resolution, post-processing) within defined minimum floors to meet limits And the applied adjustments and expected impact are shown to the user in plain language before processing continues And if limits still cannot be met, the user must explicitly confirm to proceed, and the job is tagged as out-of-SLA in logs
Progress updates, timeouts, and safe degradation for problematic inputs
Given a Preset Genie job is running When any processing stage exceeds its timeout threshold Then that stage is aborted, a safe fallback path is executed, and the user is notified within 5 seconds And real-time progress is displayed with stage names and percentage, updating at least once per second with an ETA And oversized inputs (>25 MB or >12000 pixels on any side) are automatically downscaled to the nearest supported size with a notice to the user And corrupt or unreadable images are skipped, clearly flagged in results, and do not block processing of other images And the job completes with partial results where possible or fails gracefully with a clear error within 3 minutes

Draft Connect

Authenticate your marketplaces in one step and send your first export as safe drafts/unlisted items. Verifies permissions, naming, and variant mapping before anything goes live—so your “hello world” publish is risk‑free and reversible.

Requirements

One-Step Marketplace OAuth Linking
"As a solo e-commerce seller, I want to connect my marketplaces in one step so that I can export draft listings without manual API setup."
Description

Enable users to authenticate supported marketplaces (e.g., Shopify, Amazon, eBay, Etsy) in a single guided flow using OAuth and API keys where applicable. Store tokens securely (KMS/secret vault), handle token refresh and rotation, and support multiple accounts per marketplace within a PixelLift workspace. Perform preflight account ownership checks and associate each connection with a sandbox/draft-capable environment. Provide a unified connections dashboard and surface connection state to other Draft Connect components for seamless export readiness.

Acceptance Criteria
One-Step OAuth/Key Linking Flow Completion
Given a workspace user selects a supported marketplace to connect When the user initiates the one-step connect flow Then the user is redirected to the provider’s OAuth consent or API key entry as required And on successful consent/key validation, PixelLift exchanges the code/credentials for tokens and records provider, account ID, and granted scopes And the user is returned to PixelLift with a visible “Connected” state within 60 seconds of starting the flow (p95) And if required scopes are missing, the flow blocks completion and prompts the user to re-consent with an explicit list of missing scopes And common errors (denied consent, invalid client/secret, network) display actionable guidance and a retry option within two clicks And all redirects are protected against CSRF/state mismatches and the flow aborts with no token stored if validation fails
Secure Token Storage and Secret Management
Given tokens and credentials are issued during connection When PixelLift persists these secrets Then they are encrypted at rest using a KMS-managed key and stored in the secret vault And secrets are never logged, returned by APIs, or exposed to client-side code And access is restricted to least-privileged service roles with auditable access logs for create/read/update/delete including actor and timestamp And client credentials and webhooks support rotation with zero downtime; an overlap window of up to 24 hours is supported; latest version is fetched on first use And on connection deletion, tokens are revoked with the provider (when supported) and removed from the vault within 5 minutes, with outcome recorded in audit logs
Automatic Token Refresh and Failure Handling
Given a connected account with an access/refresh token pair When the access token is within 10% of expiry or a request returns 401/invalid token Then PixelLift attempts a refresh using the stored refresh token before retrying the original request once And refreshed tokens are stored atomically and previous tokens are invalidated And on three consecutive refresh failures within 10 minutes, the connection state becomes “Needs Reauth”, an alert is queued for the workspace owner, and downstream exports are blocked And refresh attempts respect provider rate limits and are idempotent And no user-facing task experiences more than 2 minutes of disruption per incident (p95)
Multiple Accounts per Marketplace in a Workspace
Given a workspace has at least one connection to a marketplace When the user adds another account for the same marketplace Then the new connection is created without overwriting existing ones and must have a unique alias within the workspace And the dashboard displays provider, account name, and a stable identifier (e.g., store domain or seller ID) for each connection And a default connection can be set per marketplace and updated without affecting others And up to 10 accounts per marketplace can be connected per workspace; attempts beyond the limit are prevented with a clear error And disabling or deleting a connection affects only that account and leaves others intact
Preflight Ownership and Permission Verification
Given a connection is established When PixelLift runs preflight checks Then it verifies account ownership using a provider API that returns the stable account identifier and stores this identifier on the connection And it validates presence of the provider-specific minimum scope set required for Draft Connect and lists any missing scopes by key And if minimum scopes are missing, the connection state is set to “Missing Permissions” and export actions are gated until re-consent succeeds And the preflight result (pass/fail, scopes validated, timestamp) is displayed in the dashboard and exposed via API
Sandbox/Draft Environment Association
Given a provider supports drafts, unlisted items, or a sandbox environment When a connection is created or edited Then PixelLift associates the connection with a draft-capable or sandbox environment by default and records the selected environment And a test no-op operation (e.g., create-and-immediately-delete placeholder draft) succeeds without public visibility before the connection can be marked Export Ready And if the provider lacks a draft-capable mode, the connection cannot be marked Export Ready and is labeled “Not Draft-Capable” with guidance And go-live actions are blocked by default for new connections until an explicit user-confirmed toggle is enabled by an admin
Unified Connections Dashboard and State Propagation
Given one or more connections exist in a workspace When the user opens the Connections dashboard or queries the Connections API Then each connection shows provider, account alias, stable ID, environment (draft/sandbox), and state (Connected, Needs Reauth, Missing Permissions, Sandbox Verified, Export Ready) And state changes are reflected in the UI and API within 15 seconds of the underlying event (p95) And other Draft Connect components receive connection state via event bus/webhook so that export flows are gated when state != Export Ready And users can filter by provider and state, and can trigger Reauthenticate or Fix Permissions actions directly from a connection row
Permissions Scope Verification & Health Check
"As a seller, I want PixelLift to confirm my permissions are correct so that I don’t accidentally attempt actions my account can’t perform."
Description

Automatically verify that each connected marketplace grants the minimum required scopes for draft/unlisted item creation, media upload, product read, and variant management. Present a pass/fail checklist with missing scopes and guided remediation, and block live publishing features until all checks pass. Run scheduled background health checks to detect revoked permissions or expired tokens, notify the user, and pause risky operations. Expose scope state to the UI and export pipeline to ensure only safe, reversible actions are performed.

Acceptance Criteria
Initial OAuth Scope Verification and Live Publish Gating
Given a user completes OAuth connection to a marketplace When the connection callback is received Then the system compares granted scopes against the minimum set: draft/unlisted item creation, media upload, product read, and variant management And a pass/fail checklist per required scope is displayed within 3 seconds And Live Publish actions are disabled until all required scopes pass And draft/unlisted exports are allowed only if their respective scopes pass
Guided Remediation and Re-Authentication for Missing Scopes
Given one or more required scopes are missing for a connected marketplace When the user opens the remediation flow Then the UI lists each missing scope, its impact, and required action And a re-authenticate button initiates an OAuth request with the missing scopes pre-selected And upon successful re-authentication, scope status re-evaluates automatically and updates the checklist within 5 seconds And if re-authentication fails or is cancelled, Live Publish remains disabled and the checklist remains Fail for the missing scopes
Scheduled Background Health Check and Risk Pausing
Given at least one marketplace connection exists When the scheduled health check runs every 6 hours (configurable) Then the system validates token validity and re-fetches granted scopes And if a token is expired or scopes are reduced, the connection is marked At Risk, risky operations (Live Publish, variant updates) are paused, and only draft/unlisted exports remain allowed And the user is notified via in-app banner and email within 2 minutes And an event is recorded with details of the detected issue
Export Pipeline Safety Gating Based on Scope State
Given an export job is queued for a marketplace When the export pipeline validates preconditions Then if any required scope for the job’s intended action is missing, the action is not executed And if draft/unlisted creation scopes are present but live publish or variant scopes are not, the job is constrained to safe, reversible actions only And the job summary reports blocked actions and missing scopes And no irreversible marketplace changes occur when required scopes are missing
UI Exposure of Scope State and Actionable Indicators
Given the user views the Draft Connect destinations list or settings When the page loads Then each marketplace displays a status badge (Pass, At Risk, Action Required), last-checked timestamp, and count of missing scopes And a Fix CTA is present when Action Required or At Risk And statuses are exposed via aria-live and non-color indicators for accessibility And list filters allow users to show only Action Required or At Risk connections
Token Expiry Detection, Refresh, and User Alerts
Given a marketplace supports refresh tokens When a token is within 7 days of expiry Then the system surfaces a proactive alert and offers a one-click refresh And an automatic refresh is attempted on next API call; on success, status remains Pass and next check is scheduled; on failure, status becomes At Risk and risky operations are paused And export jobs do not fail silently due to token expiry; a user-visible error with remediation is attached to the job
Audit Logging of Scope Checks, Notifications, and Gating Decisions
Given any scope check, status change, notification, or pipeline gating decision occurs When the event is processed Then an audit log entry is written with timestamp, marketplace identifier, event type, previous status, new status, affected scopes, actor (system/user), and related job ID (if any) And audit logs are retained for at least 90 days and retrievable via admin API and support console search within 2 seconds for a 30-day window
Draft Export Pipeline (Unlisted/Draft Items)
"As a seller managing many listings, I want to send optimized images to marketplaces as drafts so that I can review and adjust before anything goes live."
Description

Implement a scalable, worker-based pipeline to export PixelLift-processed images and listing metadata to connected marketplaces as drafts or unlisted items only. Support bulk operations, idempotency keys, rate-limit awareness, retry with backoff, and a dead-letter queue for irrecoverable errors. Attach A/B image variations as separate media sets per marketplace schema, map PixelLift SKUs and attributes, and ensure no listings are published live. Provide progress tracking, partial success handling, resumable jobs, and structured error reporting for each item.

Acceptance Criteria
Draft-only export across supported marketplaces
Given authorized marketplace connections with draft/unlisted capability and a batch of N items When the export job runs Then each created listing is Draft/Unlisted and not buyer-visible And marketplace APIs confirm draft/unlisted state per item Given a marketplace lacking a draft/unlisted mode When an export is attempted Then the item is skipped with error code MARKETPLACE_NO_DRAFT_MODE and no listing is created
Idempotent bulk export and safe retries
Given an idempotency key KEY1 and payload P When the export job is retried up to 3 times due to worker restarts Then no duplicate drafts are created And external marketplace IDs for previously created drafts remain unchanged And job metrics include deduped_count equal to retries avoided Given the same idempotency key KEY1 with a modified payload P' When the job is submitted Then the request is rejected with HTTP 409 and error code IDEMPOTENCY_PAYLOAD_MISMATCH
Rate limiting, exponential backoff, and dead-letter handling
Given marketplace responses return 429 with an optional Retry-After header When processing requests Then the worker delays per Retry-After or applies exponential backoff (1s, 2s, 4s … max 60s) with jitter And global concurrency throttles to keep 429 error rate < 1% over any rolling 5-minute window Given a transient error (5xx, network timeout) When processing an item Then the system retries up to 5 attempts with exponential backoff and records attempt_count per item Given an irrecoverable error (4xx validation, permission denied) When encountered Then the item is not retried and is placed in the dead-letter queue with fields: item_id, marketplace, error_code, error_message, timestamp, payload_hash And DLQ items are not retried automatically but can be manually re-queued
A/B image variations attached per marketplace schema
Given a listing with base images and two variation sets A and B When exporting to a marketplace supporting media groups Then two distinct media groups are created per schema and image order matches PixelLift order Given a marketplace that does not support media groups When exporting Then both variations are attached as distinct images with variant tags in metadata or filenames per mapping And primary image defaults to variation A unless configuration overrides it Given more than two variations exist When exporting Then only A and B are attached and excess variations are skipped with a non-fatal warning per item
SKU, variant, and attribute mapping
Given a configured mapping between PixelLift SKUs/attributes and marketplace fields When exporting Then each draft contains SKU, variant options, and attributes populated per mapping, including size/color matrix and GTIN when available Given a required mapping is missing or invalid When validating items Then the item fails validation with error code MAPPING_VALIDATION_FAILED and is not exported And the job continues with remaining items Given attribute constraints (e.g., title length limits, allowed value sets) When exporting Then values are transformed (truncate/normalize/map) per rules and a warning is recorded if modifications occur
Progress tracking, partial success, and resumable jobs
Given a batch of N items When the job runs Then progress percentage updates at least every 2 seconds and equals processed_count/N within a margin of 1 item Given some items fail and others succeed When the job completes Then job status is PARTIAL_SUCCESS with per-item statuses and counts (succeeded, failed, skipped) And successful items remain as drafts/unlisted Given a paused or interrupted job When resuming with the same job_id and idempotency key Then only unprocessed items are enqueued and duplicates created equals 0
Structured per-item error reporting and job summaries
Given any item fails at any stage (validation, upload, attach, finalize) When recording the error Then the item error includes fields: item_id, job_id, marketplace, stage, error_code, error_message, http_status, attempt_count, correlation_id Given the job completes (success, partial, or fail) When generating reports Then a downloadable JSONL and CSV summary are available within 60 seconds, one row per item with status and error fields if applicable
Naming & Variant Mapping Validator
"As a seller, I want PixelLift to catch title and variant issues before export so that my drafts import cleanly without errors."
Description

Validate listing titles, descriptions, and variant attributes against marketplace-specific rules before export. Enforce limits (length, forbidden characters), SKU uniqueness, required attributes (size/color), image count and aspect ratio requirements, and naming conventions derived from PixelLift templates. Provide inline fixes, previews of transformed names, and a pre-submit checklist showing exactly what will be created per channel. Expose a rules engine that can be updated without code deploys to keep pace with marketplace policy changes.

Acceptance Criteria
Marketplace-Specific Title and Description Validation
Given marketplace rules are configured (e.g., ShopBay: title ≤ 80 chars; MegaMall: title ≤ 150 chars; forbidden characters: <, >, {, }, |, emoji) When a user runs validation for a batch of listings mapped to ShopBay and MegaMall Then violations are shown per channel with rule name, limit, offending value, and character position And auto-fix suggestions include safe truncation at word boundaries with an ellipsis and removal/replacement of forbidden characters And description length violations are reported per channel with exact overage count And revalidation after applying suggested fixes shows no blocking errors for corrected fields
SKU Uniqueness Across Listings and Variants
Given a listing with multiple variants and SKUs mapped to one or more channels When validation runs Then duplicate SKUs within the same listing and channel are blocked with an error identifying each conflicting variant And duplicate SKUs across different listings within the same channel are flagged as blocking with references to the conflicting listing IDs And duplicates across different channels are allowed unless a cross-channel uniqueness rule is enabled, in which case they are flagged per that rule And an auto-fix option to append a numeric suffix is offered and previews the resulting SKUs And after applying the fix, all previously conflicting SKUs validate as unique
Required Variant Attributes by Category and Channel
Given category-specific rules are active (e.g., Apparel on ShopBay requires Color and Size for variants) When a variant is missing any required attribute or has an unmapped attribute name Then a blocking error identifies the missing attribute, the channel, and the variant ID And the mapper UI suggests best-fit mappings from PixelLift attributes (e.g., "Hue" → "Color") with confidence scores And selecting a suggested mapping or providing a manual mapping satisfies the requirement and passes revalidation And if a required attribute value is present but empty/invalid (e.g., Size=""), the error specifies the field and exact variant row
Image Count, Resolution, and Aspect Ratio Compliance
Given image rules are configured (min 1, max 8 per variant; primary image min resolution 1000×1000 px; acceptable aspect ratio 1:1 to 5:4) When validation runs on variants with assigned images Then each variant outside image count limits is flagged with current count and required range And noncompliant primary images are flagged with measured resolution and aspect ratio And the system proposes auto-fixes where possible (e.g., pad/crop to 1:1, downselect to first 8 images) with a preview And after applying auto-fixes, revalidation passes for image-related rules
Template-Based Name Transformation with Inline Fixes
Given a PixelLift naming template (e.g., "{Brand} {Product} - {Color} {Size}") with Title Case and forbidden character removal rules is selected When a user previews name transformations Then a side-by-side diff shows original vs transformed title for each channel, including applied casing and sanitization And inline "Apply All" applies the transformations to all affected listings without altering source master data And after application, revalidation shows transformed names meet all channel-specific naming rules
Pre-Submit Per-Channel Draft Checklist Accuracy and Performance
Given all blocking validation errors are resolved for a multi-channel batch When the user opens the Pre-Submit Checklist Then for each channel the checklist displays: number of listings to create as drafts, total variants per listing, per-variant SKU, final title, and image count And the checklist clearly differentiates Warnings from Errors and blocks export if any Errors remain And for batches up to 500 variants, checklist generation and final revalidation complete within 10 seconds And the counts and identifiers in the checklist match the payload sent to the channel draft API at export time
Rules Engine Hot-Update, Versioning, and Rollback Safety
Given an admin publishes a new rule set version R2 via the rules engine UI When R2 is published Then validators begin using R2 within 60 seconds without application redeploy And each validation result is annotated with the active rule set version And if R2 contains a schema error or unsafe rule, publication is rejected with a descriptive message and the system continues using the last good version And an admin can roll back to R1, after which new validations use R1 and the audit log records publisher, timestamp, and version change
Export Dry Run & Publish Simulation
"As a cautious user, I want to simulate an export so that I can verify exactly what PixelLift will send before it touches my marketplace accounts."
Description

Provide a dry-run mode that validates payloads against marketplace schemas without writing any data. Show per-marketplace request payloads, estimated API calls, and expected outcomes, including detected conflicts (duplicate SKUs, missing attributes). Allow users to download the simulated payloads (JSON/CSV) for audit and share. Integrate with the validator to highlight required fixes and confirm that the run will create only drafts/unlisted items with zero live exposure.

Acceptance Criteria
Start Dry Run Without Data Mutation
Given at least one marketplace connection is authenticated and selected And a batch of N items is prepared for export When the user clicks "Simulate Export" Then the system performs no write operations to any marketplace (no POST/PUT/PATCH/DELETE) And only validation/read-only endpoints are invoked where needed And a banner indicates "Dry run — zero live exposure" And the execution log records 0 write calls and the total count of read/validation calls
Schema Validation with Inline Errors
Given marketplace M has a published listing schema S When the simulation runs Then each item is validated against S (required fields, data types, enums, max lengths, nested attributes) And validation failures are displayed inline per item/attribute with severity (Error/Warning) and message And a summary shows total Errors and Warnings per marketplace and per item And the "Run export" action remains disabled until all Errors are resolved or acknowledged per policy
Conflict Detection: Duplicate SKUs and Missing Attributes
Given the batch contains items with SKU values And marketplace M enforces SKU uniqueness per seller When the simulation runs Then duplicate SKUs within the batch are flagged with item references And potential duplicates against marketplace catalog are flagged where API support exists And missing required attributes are listed per item with attribute names And the simulation result status is "Blocked" if any duplicate SKU Errors or missing required attribute Errors exist
Payload Preview and API Call Estimates by Marketplace
Given the simulation completes Then for each marketplace the UI shows: planned endpoints and HTTP methods, paginated payload preview (first 50 items + total count), estimated API call count and pages, estimated duration, rate-limit headroom, expected operation types (create draft/update draft) with counts, and expected status codes And all estimates are computed from marketplace-specific limits and batch size And the totals reconcile with the number of items in the batch within ±1%
Download Simulated Payloads (JSON/CSV)
Given a successful simulation When the user clicks "Download Payload" Then per-marketplace JSON and CSV files are generated with consistent column/field ordering And file names include workspace, marketplace, timestamp, and run ID And a SHA-256 checksum is provided for each file And downloaded content matches the on-screen preview for the first 100 rows/items and total counts match exactly And an expiring share link (valid ≥72 hours) can be created and revoked by the user
Draft/Unlisted Outcome Assurance
Given marketplaces that support draft or unlisted listing states When the simulation prepares operations Then all create/update operations are configured to target draft/unlisted states only And the summary explicitly shows "Live exposure: 0 items" And if any operation cannot be forced to draft/unlisted, the simulation displays a blocking error naming the marketplace and affected items And the confirmation modal for a real run uses "Create Drafts" wording and shows per-marketplace draft counts
Safe Rollback & Revert Drafts
"As a seller, I want the ability to undo a draft export so that I can quickly recover from mistakes without harming my live catalog."
Description

Maintain a linkage between PixelLift jobs and created draft/unlisted items to enable one-click rollback. Support bulk deletion or revert-to-previous-state for drafts created by a job, with safeguards to prevent removing items that have since gone live or been edited by the merchant. Provide a time-to-live policy for stale drafts and a recovery log showing what was reverted. Ensure operations are transactional per marketplace constraints to keep inventory and media consistent.

Acceptance Criteria
One-Click Rollback from Job Details
Given a completed PixelLift job with linked draft/unlisted items across one or more marketplaces When the user clicks "Rollback" from the job details and confirms Then a preflight summary displays per-marketplace counts for items to Delete, Revert, and Skip, and requires explicit confirmation And only items linked to that job are targeted; no unlinked items are modified And 100% of eligible linked items are deleted or reverted as predicted by the preflight summary And the operation completes for up to 1,000 items within 5 minutes, showing per-marketplace progress and a completion summary with counts and duration
Revert vs Delete Based on Item Origin
Given a job that created new drafts and also updated existing drafts/unlisted items When rollback executes Then job-created new drafts are deleted And job-updated existing drafts are reverted to the captured pre-job snapshot for naming, media, and variant mapping And restored media checksums and variant mappings match the snapshot exactly; any mismatch is flagged and the item is marked Skip with reason SNAPSHOT_MISMATCH And inventory quantities and SKU identifiers remain consistent with the pre-job snapshot
Safeguards: Protect Live or Merchant-Edited Items
Given some job-linked items are now live or have merchant edits after the job completed When rollback runs Then those items are not deleted or reverted and remain unchanged And each skipped item is reported with a reason code: LIVE_ITEM or MERCHANT_EDITED And the summary clearly separates Skipped counts and provides item IDs and reasons And no override is permitted for these protected cases
Transactional Rollback per Marketplace with Idempotency
Given a rollback affecting multiple marketplaces When execution begins Then within each marketplace the operation is atomic: if any eligible item fails, all changes made in that marketplace during this rollback are reverted to the pre-operation state And for marketplaces without native atomic batch support, compensating rollback completes within 60 seconds to restore the pre-operation state And marketplaces execute independently; failure in one does not affect the committed outcome in others And the operation is idempotent using an idempotency key per job; re-running a completed or partially completed rollback makes no additional changes and reports reason ALREADY_ROLLED_BACK for previously handled items And concurrent rollback attempts for the same job are prevented; a second attempt receives a 409-equivalent conflict and no changes are made
Auto-Expiry (TTL) of Stale Drafts
Rule: Default TTL is 14 days from draft creation for items linked to a job Rule: 24 hours before TTL expiry, the owner is notified in-app and by email with counts per marketplace Rule: On TTL expiry, an automatic rollback runs during the 00:00–04:00 UTC window applying the same safeguards and transactionality Rule: Live or merchant-edited items are excluded from auto-rollback and reported as Skipped with appropriate reason codes Rule: Users can extend TTL once per job up to a maximum of 30 days total before expiry; extensions are audited Rule: Auto-rollback produces a completion summary and recovery log identical to manual rollback
Recovery Log of Rollback Actions
Given any rollback (manual or TTL) completes When the user opens the job’s Recovery Log Then the log contains: job ID, initiator (user/system), timestamp, marketplaces, item IDs/SKUs, action per item (Deleted, Reverted, Skipped), reason codes, API request IDs, and before/after media checksums and variant mapping diffs for reverted items And the log is filterable by marketplace, action, and reason code, and exportable as CSV and JSON And the log is retained for 90 days and is immutable And each item entry links to the marketplace listing or draft where available
Audit Trail & Notifications
"As a user, I want clear logs and alerts about my draft exports so that I can track progress and resolve issues quickly."
Description

Record a detailed audit trail of connections, validations, dry runs, and draft exports at the item and job level, including who initiated actions, timestamps, API endpoints called, and outcomes. Surface in-app activity feeds, email alerts for failures or permission changes, and webhooks for external automation when jobs complete. Provide searchable logs and exportable reports to support compliance, troubleshooting, and collaboration across teams.

Acceptance Criteria
Connection Authentication Audit Log
Given a user initiates marketplace authentication via Draft Connect When the OAuth flow completes with success or failure Then an audit record is created capturing userId, workspaceId, marketplace, action="connect", timestamp (ISO 8601 UTC), apiEndpoints invoked, requestId, outcome (success|failure), and errorCode/message if failure And the record is immutable and visible in the audit log within 5 seconds And the record is filterable by date range, user, marketplace, and outcome
Validation and Dry Run Job Logging
Given a user runs validation or dry run for a batch When the job starts, progresses, and completes Then a job-level log is created with jobId, type (validation|dryRun), start/end timestamps, total items, passed/failed counts, and status (queued|running|completed|failed|canceled) And each API call made is captured with endpoint, method, response status, and latency And logs are correlated via jobId and searchable by jobId and status And log entries appear in the system within 10 seconds of the event
Draft Export Item-Level Outcomes
Given a draft export job is executed When items are processed Then each item has an item-level outcome record with itemId, sourceSku, target marketplace listingId (if created), variant mapping status, and outcome (draftCreated|skipped|failed) And failures include errorCode and normalized message from marketplace API And all item outcomes roll up to job summary counts visible on the job detail
In-App Activity Feed with Filters
Given a user opens the Activity Feed When they apply filters by event type (connection|validation|dryRun|draftExport), date range, user, marketplace, or status Then the feed returns results within 2 seconds for up to 10,000 events with pagination (25 per page by default) and newest-first ordering And each entry displays a concise summary, timestamp in user’s timezone, and a deep link to job or item detail And clearing filters restores the last 24-hour default view
Email Alerts for Failures and Permission Changes
Given email notifications are enabled at the workspace level When any job fails, any item fails, or marketplace permissions scope changes or expires Then an email is sent to configured recipients within 1 minute containing workspace, marketplace, event type, jobId/itemId, summary, and remediation link And duplicate alerts for the same incident are suppressed within a 15-minute window And emails include an unsubscribe/manage preferences link
Webhooks on Job Completion with Reliability
Given a webhook endpoint is configured and verified When a validation, dry run, or draft export job completes (success or failure) Then PixelLift POSTs a signed JSON payload including workspaceId, jobId, jobType, status, itemCounts, startedAt, finishedAt, and a checksum And deliveries use exponential backoff retries for up to 24 hours on HTTP 3xx, 4xx (>=429), and 5xx failures And idempotency is ensured via a unique eventId and Idempotency-Key header And webhook delivery status (attempts, last response, next retry) is recorded in the audit log
Searchable Logs and Exportable Reports
Given a user searches logs via query builder or keywords When they filter by time range, user, marketplace, jobType, status, errorCode, or sku Then matching logs are returned within 3 seconds for datasets up to 100,000 records And the user can export the current result set to CSV and JSON, up to 1,000,000 rows via an async export job, delivered with a download link that expires in 7 days And exports include columns: userId, workspaceId, eventType, jobId, itemId, timestamps, endpoint, outcome, errorCode, message And access is role-restricted so viewers cannot see API payload bodies

Checklist Coach

A guided, time‑boxed checklist that walks you from upload to first export. Inline micro‑lessons, auto‑detect step completion, and clear next actions keep momentum. Skip or “Do It For Me” on advanced steps to stay on the five‑minute path.

Requirements

Guided Upload‑to‑Export Checklist
"As a new seller, I want a clear step-by-step checklist from upload to first export so that I can finish in minutes without guessing the next action."
Description

A single, guided checklist that maps the first‑time user journey from image upload to first export within one screen. It presents a sequenced list of steps (Upload, Select Marketplace Preset, Background Removal, Bulk Edits, Variation Generation, Export) with clear labels, contextual CTAs, and visual states (To Do, In Progress, Done, Skipped, Auto). A persistent progress bar and step counter communicate momentum, while each step expands inline to surface the minimal controls needed. The component integrates with PixelLift’s project model to load current batch context, with responsive layouts for common desktop breakpoints. It exposes events for analytics and listens to backend job updates to reflect real‑time status, ensuring users always know the next best action and how close they are to completion.

Acceptance Criteria
Checklist renders in one screen with sequenced steps and visual states
Given a first-time user opens PixelLift to the Guided Upload‑to‑Export Checklist When the checklist loads Then it displays a single, ordered list of steps: Upload, Select Marketplace Preset, Background Removal, Bulk Edits, Variation Generation, Export And each step shows a clear label and a contextual primary CTA relevant to that step And each step supports and visibly indicates one of the states: To Do (default), In Progress, Done, Skipped, Auto And the checklist resides within one screen (no page navigation) with vertical scrolling allowed but all steps accessible within the same view container And a persistent progress bar and step counter (X of 6) are visible at the top of the checklist at all times When any step state changes Then the progress bar percentage and step counter update within 300ms to reflect the new completion state
Real-time backend job sync auto-detects step completion
Given a backend job (e.g., Background Removal, Bulk Edits, Variation Generation, Export) is initiated from a step When the backend emits status updates (queued, running, succeeded, failed) Then the corresponding step transitions to In Progress within 500ms of receiving a running status And transitions to Done within 2s of receiving a succeeded status And transitions to To Do with an inline error state and Retry CTA within 2s of receiving a failed status And the step shows a spinner/progress indicator while running Given the project context already satisfies a step’s requirements (e.g., a marketplace preset already selected) When the checklist loads Then that step is marked Auto with a tooltip explaining it was auto-detected
Skip and Do It For Me maintain five-minute path to first export
Given advanced steps (Bulk Edits, Variation Generation) are presented When the user selects Skip on an advanced step Then the step transitions to Skipped and the next recommended step is brought into view with its CTA focused When the user selects Do It For Me on an advanced step Then default recommended settings are applied and the step proceeds automatically, showing In Progress followed by Done on completion And the total time from first checklist load to first successful export is ≤ 5 minutes under baseline test conditions (20 JPEG images, 2000x2000px, ~2MB each, 50 Mbps connection, no backend errors) And skipped steps do not block the Export step
Inline step expansion with minimal controls and responsive desktop layouts
Given the checklist is displayed on desktop breakpoints (1280x720, 1366x768, 1440x900, 1920x1080) When a step header is clicked or its CTA is activated Then the step expands inline to reveal only the minimal controls required to complete that step and collapses when completed or when another step is expanded And there are no blocking modal dialogs required to complete any step And the layout remains usable without horizontal scrolling at the listed breakpoints And keyboard navigation (Tab/Shift+Tab) moves focus through steps and inline controls in a logical order, with visible focus states
Project context loads and presets configure downstream steps
Given a user opens the checklist within an existing project with a current batch When the checklist initializes Then it loads batch metadata (image count, thumbnails) and any previously selected marketplace preset And the Select Marketplace Preset step is pre-populated accordingly and can be changed When a marketplace preset is selected Then downstream steps (Background Removal, Bulk Edits, Export) display derived defaults (e.g., background color, canvas size, file format, naming template) from that preset And changing the preset updates those defaults immediately without requiring a page reload And the checklist state (step statuses and settings) persists across browser refresh within the same project
End-to-end first export completes with clear guidance and error handling
Given a first-time user follows the checklist from Upload to Export When they click Export with at least one image successfully processed Then a bulk export job is created and progress is shown in the Export step And upon completion, the Export step shows Done with a success message including the count of exported files and a Download/Copy Link CTA And the user can retrieve a single archive or structured folders per marketplace as configured When any step encounters an error Then the step displays a concise error message, a Retry CTA, and a link to minimal help content, and the checklist highlights the next best action And errors in an optional step do not block progression to Export if prerequisites are met
Analytics event emission for interactions and state changes
Given analytics is enabled When a user views, starts, completes, skips, or auto-completes a step Then an analytics event is emitted with fields: eventName, projectId, batchId, stepName, previousState, newState, timestamp, and anonymous user identifier When export starts, completes, or fails Then export_started, export_completed, or export_failed events are emitted respectively with counts and duration metrics And events are deduplicated per transition and dispatched within 1s of the action without blocking the UI And no PII is included in payloads and event transmission failures do not affect step progression
Auto‑Detect Step Completion
"As a busy seller, I want steps to auto-complete when actions finish so that I don’t waste time checking boxes or wondering what’s done."
Description

Automatically mark checklist steps complete by subscribing to client and server events (e.g., upload finalized, background removal job succeeded, variations generated, export delivered). The checklist listens to PixelLift’s job queue/webhooks and local UI actions, applies debouncing and idempotency, and updates step states without manual user input. Completion logic accounts for batch thresholds (e.g., 100% of required images processed or a defined minimum viable subset) and gracefully handles partial failures with retry and surface-level error badges. This reduces cognitive load, removes checkbox‑click friction, and keeps progress accurate and real‑time across sessions.

Acceptance Criteria
Upload Finalized Auto-Completion
Given a user uploads a batch of images and all required files reach status "finalized" on both client and server, When the last qualifying upload_finalized event is received, Then the Upload step is marked Complete within 5 seconds without any user action. Given duplicate upload_finalized events for the same files are received within 2 seconds, When events are processed, Then the step transitions to Complete only once and no duplicate state updates are emitted. Given some files fail to finalize but the count of finalized files is greater than or equal to the configured Minimum Viable Subset (MVS) threshold, When event processing settles, Then the Upload step is marked Complete and an info badge indicates partial completion (e.g., "42/50 ready"). Given finalized count remains below the MVS threshold after up to 3 automatic retries, When retries conclude, Then the step remains In Progress and an error badge displays the failed count and a Retry action.
Background Removal Job Success Auto-Completion
Given background removal jobs are queued for all uploaded images, When webhooks report status "succeeded" for all required images or the total succeeded meets/exceeds the configured MVS threshold, Then the Background Removal step auto-marks Complete within 5 seconds. Given out-of-order or duplicate webhook deliveries for the same items, When events are processed, Then completion is idempotent and debounced (no more than one state transition for identical payloads within 2 seconds). Given some jobs fail, When automatic retries (maximum 3 attempts) finish, Then if the threshold is met the step is Complete with a warning badge; otherwise it remains In Progress with an error badge and a visible Retry control.
Variations Generated Threshold Completion
Given the user has configured N variations per image to be generated, When variation generation reports at least N variations per image for all required images or meets/exceeds the configured MVS threshold across the batch, Then the Variations step marks Complete within 5 seconds. Given fewer than N variations are produced for some images due to failures, When retries complete, Then the step is Complete only if the batch meets the MVS threshold and a warning badge indicates shortfalls; otherwise it remains In Progress with error details. Given duplicate or replayed variation webhooks for the same outputs, When they are received, Then only one completion transition is recorded (idempotent) and duplicates are ignored.
Export Delivered Confirmation Completion
Given an export job has been requested for the current checklist batch, When the server reports status "delivered" and the export bundle URL responds with HTTP 200 and is downloadable, Then the Export step marks Complete within 5 seconds. Given a delivered event arrives for an export job not associated with the current checklist context, When events are processed, Then the step remains unchanged and no completion occurs. Given multiple export jobs are triggered sequentially, When each completes, Then only the latest job linked to the checklist marks the step Complete and prior job events are ignored if already completed. Given duplicate delivered webhooks are received, When processed, Then only one completion transition and one analytics event are recorded.
Idempotency and Debounce Enforcement
Given any checklist step has already reached the Complete state, When older, duplicate, or replayed events arrive, Then the state does not regress and no additional state transitions are emitted. Given success and failure events for the same item arrive out of order, When reconciliation runs, Then the final state reflects the latest authoritative server status and is consistent across all active sessions. Given identical events are received multiple times within 2 seconds, When processed, Then at most one state mutation occurs and duplicates are skipped.
Respect Skip and Do It For Me States
Given the user selects Skip on an advanced step, When qualifying success events for that step arrive later, Then the step remains Skipped and is not auto-marked Complete. Given the user selects Do It For Me and the system processes the step end-to-end, When processing completes successfully per configured thresholds, Then the step is marked Complete with a "Completed by PixelLift" badge. Given the user toggles a previously Skipped step back to Active, When sufficient qualifying events arrive to meet the completion thresholds, Then the step auto-marks Complete within 5 seconds.
Inline Micro‑Lessons
"As a non-designer, I want short tips inline so that I understand why a step matters and how to do it right without leaving the flow."
Description

Short, contextual tips embedded within each checklist step explain the what and why in under 20 seconds. Content appears as lightweight callouts or micro‑modals with an image/GIF and one actionable best practice tailored to the selected marketplace preset (e.g., background color rules, safe margins). Lessons are dismissible, non‑blocking, and remember their viewed state. Content is delivered via a CMS/config file to enable quick iteration and localization. This improves user confidence, reduces support tickets, and accelerates first‑export success without pulling users out of flow.

Acceptance Criteria
Preset-Tailored Content Rendering
Given a user has selected a marketplace preset and opened a checklist step with a micro‑lesson When the micro‑lesson is opened Then the lesson title, body, media, and CTA correspond to the selected preset’s content version And no content from other presets is displayed And the content source ID matches the CMS/config entry for that preset and step And changing the preset updates the displayed lesson within 500 ms without page reload
Non-Blocking, Dismissible Presentation
Given a first-time viewer lands on a step with a micro‑lesson When the micro‑lesson is visible as a callout or micro‑modal Then all primary step controls remain clickable and keyboard-focusable And the lesson can be dismissed via close button, ESC key, or clicking outside (for micro‑modal) And dismissal completes within 100 ms and does not scroll or navigate the page And a persistent "Learn more" control remains to reopen the lesson
Viewed State Persistence Per User/Step/Preset
Given a signed-in user dismisses the micro‑lesson for Step X with Preset Y When the user returns to Step X with Preset Y on the same or a different device/browser Then the micro‑lesson does not auto‑open And the "Learn more" control indicates "Viewed" And reopening shows the same content version V if unchanged And if the CMS version increments to V+1, the lesson auto‑opens once and the viewed flag resets
CMS-Driven Content Delivery and Hot Update
Given content for Step X and Preset Y exists in the CMS/config with locale Z and version V When the checklist step is loaded Then the micro‑lesson fetches and renders version V from the CMS/config endpoint And publishing an update to version V+1 is reflected in-app within 5 minutes without redeploy And the UI records the rendered version ID for audit in client logs
Localization with Fallback
Given the user’s UI locale is set to L When the micro‑lesson opens Then localized text and media for L are displayed And if L is unavailable, the lesson falls back to en‑US And switching the UI language updates the lesson content within 500 ms without reload And all lesson media include localized alt text for the active locale
Accessibility and Keyboard/Screen Reader Support
Given a keyboard-only user navigates to the micro‑lesson trigger When the micro‑lesson opens in micro‑modal mode Then focus moves to the modal, is trapped until close, and returns to the trigger on close And the lesson is fully operable via Tab, Shift+Tab, Enter/Space, and ESC And screen readers announce role, title, and description via ARIA labels And text and interactive elements meet WCAG 2.1 AA contrast requirements
Graceful Degradation and Caching on CMS Failure
Given the CMS/config endpoint errors or times out When the micro‑lesson is requested Then a cached copy from the last successful load within 24 hours is rendered And if no cache exists, the lesson area collapses with a non‑blocking "Content unavailable" message And the request times out after 2 seconds and does not block checklist interactions And no uncaught errors appear in the console
Skip & Do‑It‑For‑Me Options
"As a time-constrained user, I want to skip or let PixelLift handle advanced steps so that I stay on track to export quickly with acceptable defaults."
Description

Every advanced step offers two momentum‑preserving choices: Skip (advance without completing, with sensible defaults where safe) and Do It For Me (apply recommended automatic settings and execute the step). DIFM triggers preconfigured presets tuned to the chosen marketplace (e.g., standard background removal strength, shadow, crop, and size), queues jobs, and returns a concise result summary. Both actions are reversible with an easy undo or edit later. The checklist clearly labels when a step was skipped or automated and adjusts downstream recommendations accordingly, keeping users on the five‑minute path to export while still allowing depth for power users later.

Acceptance Criteria
Skip Applies Safe Defaults and Advances Step
Given an advanced step is marked skippable and has safe defaults When the user clicks Skip Then the checklist advances to the next step within 1 second And the skipped step is labeled "Skipped" with a tooltip describing which defaults were applied And the safe defaults are applied to the working image set without starting background jobs And downstream steps receive parameters derived from these defaults Given an advanced step is not safe to skip When the user hovers or attempts to click Skip Then the Skip control is disabled or a confirmation explains why skipping is unsafe and requires explicit confirmation
Do It For Me Executes Marketplace Preset
Given a marketplace is selected and an advanced step supports automation When the user clicks Do It For Me Then the system displays the preset name and key settings that will be applied And a background job is queued within 500 ms and the step shows an In Progress state And upon completion the step is labeled "Automated" with a summary including settings applied, images processed count, and elapsed time And any errors are surfaced with a failures count and a Retry action
Checklist Labels and Adjusts Recommendations
Given a step was Skipped or Automated When the user views the checklist Then the step shows a persistent badge ("Skipped" or "Automated") and is clickable to view details And downstream steps update recommendations based on the applied defaults or preset outputs And the time-to-export indicator remains on or under the five-minute path for a batch of up to 50 images And the Next action CTA reflects the adjusted recommendations (e.g., "Proceed to Export" or "Review Crop")
Undo and Redo for Skip and DIFM
Given a step was Skipped or Automated When the user clicks Undo within the current session Then the prior state of the step and its outputs are restored and downstream recommendations are recalculated And the action history records the Undo event Given the user has undone a Skip or Automated action When the user clicks Redo Then the Skip or Automated state is reapplied with the same settings and labels
Batch DIFM Processing and Result Summary
Given Do It For Me is invoked on a batch of N images (N up to 200) When processing completes Then the result summary shows counts for processed, skipped (if any), and failed images And the summary lists the top 3 error reasons with counts and provides a downloadable CSV of failures with image IDs and error messages And the job duration and average per-image processing time are displayed
Edit Later and Versioned Reversibility
Given a step is labeled Skipped or Automated When the user opens the step later and edits settings manually Then the label changes to "Edited" and a new version of outputs is created without overwriting prior outputs And the user can roll back to the previous version in one click, restoring prior outputs and labels And downstream steps are recalculated to reflect the current version
Time‑Boxed Flow & Countdown
"As a user on a deadline, I want a visible five-minute countdown so that I can pace myself and ship on time without getting stuck."
Description

A visible five‑minute countdown and per‑step time nudges keep the first‑export journey paced. The timer starts on checklist initiation, pauses during blocking backend operations, and resumes on user‑action steps to avoid penalizing system wait time. Gentle prompts recommend DIFM or skipping when a step risks exceeding its suggested duration. The timer never blocks completion; it motivates and informs. Events from the timer are exposed to analytics to measure pacing efficacy and identify friction points. This mechanism reinforces PixelLift’s promise of a fast first export and helps users maintain momentum.

Acceptance Criteria
Countdown Initialization & Visibility on Checklist Start
- Given the user clicks "Start Checklist", when the first step view renders, then a countdown labeled "5:00" is visible in the checklist header within 500 ms. - Given the countdown is visible during an active user-action step, when 1 second elapses, then the displayed time decrements by exactly 1 second with jitter <= 200 ms. - Given the user navigates between steps within the checklist, when the route/view changes, then the countdown continues without resetting or skipping time. - Given the countdown is displayed, then its format is mm:ss with leading zeros (e.g., 04:09).
Pause/Resume Around Blocking Backend Operations
- Given a blocking backend operation begins for the current step, when the operation state transitions to "processing", then the countdown pauses within 500 ms and displays a "Paused" indicator. - Given the countdown is paused due to a blocking backend operation, when the operation completes successfully or fails, then the countdown resumes within 500 ms and no seconds are decremented during the paused interval. - Given multiple blocking backend operations occur in sequence or overlap, then the total paused duration is fully excluded from the countdown and no negative or duplicate decrements occur over a 2-minute test window.
Per-Step Time Nudges and DIFM Recommendations
- Given each checklist step has a suggested duration S (in seconds), when active time on the step reaches 0.8*S without completion, then show a non-blocking nudge with "Skip" and "Do It For Me" actions. - Given a user lands on a step where the remaining global countdown is less than that step's suggested duration, when the step view renders, then show the nudge within 2 seconds. - Given the nudge is shown, when the user clicks "Do It For Me", then the automated action runs and the step is marked complete upon success; on failure, the user remains on the step with an error message and the timer continues. - Given the nudge is shown, when the user clicks "Skip", then the step is marked as skipped and the next actionable step is focused within 500 ms. - Given the nudge is shown, then it is dismissible, and it is not shown again for the same step within the same session unless the user re-enters the step after 30 seconds.
Non-Blocking Behavior at and Beyond Countdown Zero
- Given the countdown reaches 00:00, then no controls are disabled and the user can continue to complete steps and export. - Given the countdown reaches 00:00, then a one-time non-blocking notification "You're over 5 minutes—keep going" appears and can be dismissed; it does not reappear in the same session. - Given the countdown has elapsed, then the displayed time does not go negative and remains at 00:00.
Analytics Event Emission for Pacing Efficacy
- Given a checklist session, when key timer-related actions occur, then the client emits analytics events: timer_start, timer_pause, timer_resume, nudge_shown, nudge_action_skip, nudge_action_difm, step_completed, export_started, export_completed. - Given an analytics event is emitted, then its payload includes: checklist_id, user_id_hash, step_id (nullable), timestamp_ms (UTC), remaining_seconds, suggested_step_seconds (nullable), and source (web). - Given normal connectivity, when events are emitted, then 95% are received by the analytics endpoint within 10 seconds; on offline, up to 100 events are queued and flushed within 10 seconds of reconnect. - Given idempotency keys are used per event, when retries occur, then no duplicate events are stored downstream. - Given the user has analytics disabled, then no timer analytics events are emitted.
State Persistence and Multi-Tab Consistency
- Given the user refreshes the page or navigates away and returns within the same session for checklist_id X, when the checklist reloads, then remaining_seconds and pause/resume state are restored within ±1 second. - Given the same checklist_id X is open in two tabs, when the user interacts in one tab, then only that tab decrements the countdown and the other tab shows a "View only" timer state without decrement. - Given multi-tab usage over 2 minutes, then the countdown does not decrement faster than real time (no double decrement) and the two tabs remain within 1 second of each other.
Accessibility and Responsive UX for Timer and Nudges
- Given the timer and nudge UI are rendered, then text and key UI elements meet WCAG 2.1 AA color contrast (>= 4.5:1) and are readable on screens as small as 320px width without overlap or truncation. - Given a screen reader user starts the checklist, then the timer's accessible name is announced and subsequent updates are announced no more than once per minute and on pause/resume, using aria-live="polite". - Given keyboard-only navigation, when tabbing through the checklist, then the timer, nudge actions, and export controls are reachable, have visible focus states, and do not steal focus on tick updates. - Given the timer displays, then it uses mm:ss with leading zeros and includes an accessible label "Time remaining" that updates as time changes.
Progress Persistence & Resume
"As a user switching devices or returning later, I want my checklist progress saved so that I can resume without redoing steps."
Description

Persist checklist state, step outcomes, timers, and context to the user’s active project so progress survives refreshes, sign‑outs, and device changes. State is stored server‑side with lightweight client caching, keyed by user and project, and versioned to tolerate checklist updates. On return, the user lands exactly where they left off with accurate step statuses and next‑action guidance. Conflicts from parallel sessions are resolved by last‑write wins plus reconciliation prompts for critical divergences. This reduces rework, supports real‑world interruptions, and increases completion rates for first‑time exports.

Acceptance Criteria
Resume After Browser Refresh
Given a signed-in user with an active project and in-progress Checklist Coach state persisted server-side keyed by user+project When the user hard-refreshes the browser or the tab reloads Then the app fetches server state and restores the checklist within 2 seconds of load, focusing the last active step And every step displays its accurate status (Not Started, In Progress, Completed, Skipped, Do It For Me) and preserved outputs/notes And the Next Action CTA reflects the restored state and is actionable And lightweight client cache renders a placeholder state within 300 ms and is reconciled with server state upon arrival And if local cache and server state differ, server state is applied (server-wins) without duplication or loss
Resume Across Device Sign-In
Given a user saves progress on Device A at timestamp T and signs in on Device B to the same project When the project loads on Device B Then the most recent server state (last write) is displayed within 3 seconds, including step order, statuses, timers, and outputs And the UI scrolls/expands to the last active step and shows a non-blocking "Resumed from T" indicator And no stale local cache from Device B overrides the server state
Checklist Version Migration
Given the checklist definition updates from version vN to vN+1 while a project has saved progress on vN When the user resumes the project Then state is migrated in under 1 second preserving completed/validated steps that are unchanged between versions And renamed or rekeyed steps are mapped using the supplied version map; if unmapped, their data is placed in an "Archived (vN)" container without loss And new steps in vN+1 are initialized to Not Started and visible And removed steps are marked Archived with access to their outputs/notes And the user sees a non-blocking banner noting the migration; no blocking errors occur
Parallel Session Conflict Resolution
Given two active sessions (A and B) edit the same project in parallel When session B saves after session A (later write at T2) and session A resumes or attempts to save Then server applies last-write-wins and session A fetches the latest server state before proceeding And any critical divergences (step outputs, exports, deletions) made only in session A after T2 are detected and listed in a reconciliation prompt within 1 second And the prompt offers per-item choices: Keep Server, Keep Mine, or Merge (when applicable), defaulting to Keep Server And after the user resolves, the resulting state is consistent and saved, and an audit entry records the choices
Timer Persistence and Accuracy
Given a checklist step with an active time-box timer started at t0 When the user refreshes, signs out/in, or resumes on another device at t1 Then the elapsed time shown equals (t1 - t0) within ±1 second And if the timer exceeded its limit while away, the step displays a "Time elapsed" state with suggested next action (proceed or extend) And paused timers remain paused after resume; manual adjustments persist and are logged
Next-Action Guidance Restoration
Given a user resumes an in-progress checklist with multiple incomplete steps and dependencies When restored state loads Then the Next Action CTA targets the first unblocked, incomplete step based on dependency rules and navigation moves focus there on click And if the computed next step is blocked, a guidance banner explains the prerequisite with a link to the required step And across a suite of dependency graphs, 100% of test cases navigate to the correct next step on resume
Persistence of Skipped and Do It For Me Choices
Given the user marks steps as Skipped or selects Do It For Me (DIFM) and progress is saved When the user resumes later on any device Then those steps retain their chosen state with who/when metadata visible And DIFM-generated outputs are available if completed; if in progress, the step displays "In Progress" with last update timestamp And the user can revert Skip/DIFM to Not Started, and the prior choice is recorded in history

ReadyScore

A live readiness meter that shows exactly how close you are to a compliant first export. It flags missing pieces (preset, marketplace link, file naming) and offers one‑click fixes. Hit 100% with confidence and no guesswork.

Requirements

Real-time Readiness Scoring
"As a solo e-commerce seller, I want a live score that tells me exactly what’s missing so that I can reach a compliant first export without trial and error."
Description

Implements a rule-driven engine that calculates a live readiness percentage for a target marketplace export. The engine evaluates mandatory criteria (e.g., chosen preset, connected marketplace, valid file-naming template) and compliance checks (dimensions, background policy, format, color profile, max file size) across the current batch. It supports weighted scoring, distinguishes blockers from warnings, and recalculates instantly on user actions or processing events. Exposes an internal service to query score, failed checks, and recommended next actions, integrating with PixelLift’s processing pipeline and export workflow to eliminate guesswork and accelerate first-time compliant exports.

Acceptance Criteria
Live score refresh on user actions and processing events
1) Given a batch draft with a displayed ReadyScore, When the user selects a valid preset, Then the UI score updates within 300 ms and equals the integer score returned by the readiness service. 2) Given a batch with pending background removal, When a processing job completes for any image, Then the readiness service recalculates within 500 ms of the event and the UI reflects the new score without a page refresh. 3) Given an invalid file-naming template, When the user corrects it to a valid template, Then the related failed check disappears and the score increases accordingly in both UI and service. 4) Given a marketplace is not connected, When the user connects it, Then the UI and service scores match exactly after rounding (0–100 integer), and a recalculated_at timestamp changes. 5) Given concurrent user actions (e.g., preset change and template fix), When both complete, Then only the latest state is scored and no stale score is displayed (verified by monotonic timestamp and final score consistency).
Mandatory configuration blockers gating export with one‑click fixes
1) Given no preset is selected, Then a blocker "Preset not selected" is listed, the score < 100, and the Export button is disabled. 2) Given no marketplace is connected, Then a blocker "Marketplace not connected" is listed, the score < 100, and the Export button is disabled. 3) Given an invalid file-naming template, Then a blocker "Invalid file naming template" is listed with a validation error message, the score < 100, and the Export button is disabled. 4) Given any blocker is present, When the user clicks the corresponding one-click fix (e.g., "Choose Preset", "Connect Marketplace", "Use Recommended Template") and completes the flow, Then the blocker is removed, the score recalculates, and the Export button state updates within 300 ms. 5) Given all blockers are cleared, Then the Export button becomes enabled and blockers count is zero in the failed checks list.
Compliance checks across batch images against marketplace rules
1) Given a selected marketplace, When the readiness engine evaluates the batch, Then it checks each image for: dimensions, background policy, file format, color profile, and max file size against that marketplace's rules. 2) Given any image violates a hard requirement classified as a blocker (e.g., exceeds max file size), Then the score < 100 and the Export button remains disabled until the issue is fixed. 3) Given images with warnings (e.g., recommended color profile mismatch), Then warnings are listed per image, reduce the score by their weights, but do not disable Export. 4) Given the failed checks list is queried via the service, Then each entry includes: check_id, severity (blocker|warning), scope (batch|imageId), message, and recommended_action. 5) Given only a subset of images is selected for export, Then the engine evaluates and scores only the selected images.
Weighted scoring configuration and deterministic calculation
1) Given a marketplace-specific weight config is loaded, Then the total score is computed as sum(weights of passed applicable checks) / sum(weights of applicable checks) × 100 and rounded to the nearest integer. 2) Given the following test fixture: mandatory_config weight=60, compliance_checks total weight=40, and 8/10 compliance checks pass while all mandatory are satisfied, Then the score equals 92. 3) Given a weight config change is applied, When the next recalculation occurs, Then the score reflects the new weights and the response includes the weight_config_version used. 4) Given checks are marked non-applicable by marketplace rules, Then their weights are excluded from the denominator. 5) Given warnings are present without blockers, Then the score may be < 100 but Export remains enabled.
Severity handling: blockers vs warnings and their UX/API impact
1) Given each check has a severity, Then blockers must be resolved before enabling Export, while warnings are advisory and do not block Export. 2) Given failed checks are returned by the service, Then each item includes severity, user_message, developer_code, and recommended_next_action. 3) Given the ReadyScore panel displays issues, Then blockers are visually distinguished from warnings and include one-click fix affordances. 4) Given all blockers are resolved and only warnings remain, Then a "Proceed with warnings" confirmation is shown on Export and the export can continue. 5) Given a previously warning-level check is escalated to blocker by config, Then the Export button becomes disabled on the next recalculation and the UI reflects the new severity.
Internal readiness service API and eventing
1) Given GET /internal/readiness?batchId={id} is called, Then it returns 200 with {score:int 0–100, recalculated_at:ISO8601, failed_checks:[{check_id, severity, scope, message, recommended_action, weight}], weight_config_version:string} within 300 ms p95 for batches ≤500 images. 2) Given a state change affecting readiness occurs, Then the service emits an internal event readiness.updated with batchId, score, deltas, and timestamp within 500 ms of the change. 3) Given successive reads during rapid changes, Then responses are consistent with the latest committed state (monotonic recalculated_at per batch). 4) Given an unknown batchId, Then the API returns 404 with no score and no failed_checks. 5) Given service authentication is required, Then unauthenticated calls receive 401 and no readiness data is leaked.
Export workflow integration driven by ReadyScore
1) Given blockers exist, Then the Export flow entry point is disabled and displays the top blocker reason with a link to its one-click fix. 2) Given score == 100 and blockers == 0, Then the Export flow can be initiated without interruptions, and the first export completes when no other system errors occur. 3) Given warnings exist but no blockers, Then starting Export prompts a confirmation summarizing warnings and allows proceed. 4) Given the batch selection changes (add/remove images), When the user returns to the Export step, Then the score and Export button state update within 300 ms. 5) Given an export attempt while a new blocker is introduced by a late processing event, Then the attempt is halted before submission and the UI surfaces the blocker with a retry option after fix.
Marketplace Compliance Rules Library
"As a user exporting to different marketplaces, I want the system to know each marketplace’s rules so that my images are validated correctly without me researching requirements."
Description

Provides a versioned, centralized repository of marketplace-specific rules (Amazon, Etsy, eBay, Shopify, regional variants) including image dimensions, aspect ratios, background requirements, allowed formats, file size caps, color profiles, naming conventions, and category-level overrides. Supplies a rules-evaluation API used by the scoring engine and export validators. Includes admin tooling for safe updates, automatic rule refreshes, and change logs to ensure ReadyScore reflects current marketplace requirements at all times.

Acceptance Criteria
Versioned Rules Repository per Marketplace and Region
- Given an existing Amazon US ruleset v1.3 and a new draft v1.4, when v1.4 is published, then the API returns v1.4 as latest for marketplace=Amazon, region=US with effectiveAt timestamp and semantic version. - Given a request for rules with query version=v1.3 and region=US, when GET /rules, then the API returns v1.3 and HTTP 200. - Given rulesets for Amazon US and Amazon GB exist, when GET /rules?marketplace=Amazon&region=GB, then fields differing by region are reflected and region=GB is indicated in the response. - Given a ruleset payload, when it is stored, then schema validates presence of dimensions, aspectRatio, background, formats, fileSizeCap, colorProfile, naming, and categoryOverrides; else 422 with machine-readable error list. - Then p95 latency for GET /rules (warm cache) <= 300 ms and monthly uptime for the endpoint >= 99.9%.
Rules Evaluation API returns deterministic compliance with reasons
- Given a valid evaluation request with marketplace, region, category, and image/file metadata, when POST /evaluate, then the response includes rulesVersion, overallCompliant boolean, and perRule results with code, pass/fail, and message. - Given the same request is sent twice within 24 hours, when responses are compared, then they are identical excluding responseId and server timestamps. - Given a batch of 500 items, when POST /evaluate/batch, then the API responds 200 with results for all items and p95 latency <= 2 seconds. - Given a request missing required fields, when POST /evaluate, then the API responds 400 with machine-readable error codes and does not evaluate partial data. - Given a request with an unsupported marketplace or region, when POST /evaluate, then the API responds 404 with error code RULES_NOT_FOUND.
Admin workflow for safe rule updates, approvals, and rollback
- Given role=RulesAdmin, when creating or editing a rules draft, then changes are saved as draft and visible in a change preview with diff. - Given a draft passes schema and unit validations, when role=Approver clicks Publish, then a new version is created with incremented semantic version and a change log entry (who, when, summary, diff) is recorded. - Given any validation fails, when Publish is attempted, then publishing is blocked with explicit errors and no new version is created. - Given a published version v1.4, when role=Approver clicks Rollback to v1.3, then v1.5 with content identical to v1.3 is published and logged; v1.4 remains in history. - Given category-level overrides are edited, when attempting to publish conflicting or unsatisfiable overrides, then validation blocks publication with conflict details.
Automatic rule refresh and ReadyScore consistency
- Given a new rules version is published, when the refresh service runs, then all caches are invalidated and ReadyScore/evaluators use the new version within 5 minutes. - Given an evaluation batch starts at time T, when a new version publishes at T+2 minutes, then the batch completes using the original version; the response includes rulesVersion pinned. - Given the last successful sync is older than 30 minutes, when a user opens ReadyScore, then a banner warns Rules may be out of date with next retry ETA. - Given a refresh attempt fails, when retries exceed 3 with exponential backoff, then an alert is sent to on-call and the health endpoint reflects OUT_OF_SYNC with lastError. - Given GET /health/rules, when called, then it returns per-marketplace currentVersion, lastSyncAt, and syncStatus.
Version pinning and historical reproducibility for exports
- Given an export job starts with rulesVersion=v1.3, when ReadyScore validates pre-export, then all validations use v1.3 until the job completes, regardless of later publishes. - Given an evaluation request includes header X-Rules-Version: v1.2, when POST /evaluate, then the API uses v1.2 and returns 200 with rulesVersion=v1.2 and a Warning header if v1.2 is deprecated. - Given a request pins a retired version, when POST /evaluate, then the API returns 410 with error code VERSION_RETIRED and suggests the nearest supported version. - Given a past export manifest references v1.1, when re-run with version pin v1.1, then results match the original pass/fail set exactly.
Category-level overrides and precedence resolution
- Given a marketplace base rule and a category override exist, when evaluating an item in that category, then the override supersedes the base and the response includes ruleSource=override and overrideId. - Given multiple category tags apply, when overrides conflict, then the system applies the most specific override based on configured precedence order; ties are resolved by highest priority weight. - Given an override is removed in a new version, when evaluating, then the response shows ruleSource=base and reports rulesVersion of the new publish. - Given admin attempts to create an override that renders constraints unsatisfiable (e.g., minWidth > maxWidth), when saving draft, then validation fails with error code CONSTRAINT_UNSATISFIABLE.
One-Click Fix Actions
"As a time-pressed seller, I want one-click buttons to fix each blocker so that I can reach 100% readiness quickly without manual edits."
Description

Adds actionable fixes next to each ReadyScore issue, enabling instant remediation such as applying a compliant preset, connecting a marketplace, generating and applying a naming template, normalizing color profiles, resizing/canvas adjustments, background removal, and compression to size limits. Supports batch operations, progress feedback, undo where feasible, idempotency, and safe fallbacks. Re-scores automatically upon completion and logs applied fixes for auditing and analytics.

Acceptance Criteria
One-Click Preset Application Resolves 'Missing Preset' Issue
Given a project with images flagged "Missing Preset" and a marketplace profile is selected When the user clicks Fix next to "Missing Preset" Then a compliant preset for the selected marketplace is applied to all selected images in the batch And progress feedback starts within 2 seconds and updates at least every 1 second until completion And ReadyScore for "Missing Preset" is 0 and overall ReadyScore is recalculated within 5 seconds of completion And the action is idempotent; repeating the fix produces no changes and returns "Already compliant" for 100% of the same images And an audit log entry is recorded with user ID, fix type=preset_apply, image count, image IDs, start/end timestamps, per-image outcomes, and before/after ReadyScore And an Undo option is available for 15 minutes that restores the previous image versions in bulk
One-Click Marketplace Connection Resolves 'Marketplace Link Missing'
Given the project has no linked marketplace and images are flagged "Marketplace Link Missing" When the user clicks Fix next to "Marketplace Link Missing" Then an OAuth/connect flow for the chosen marketplace is initiated and completes successfully And the secure token is stored, the marketplace link status is "Connected", and the ReadyScore sub-issue is set to 0 within 5 seconds And progress feedback starts within 2 seconds and updates at least every 1 second until completion And the action is idempotent; if already connected, the fix performs no duplicate linking and returns "Already connected" And failures or user cancellation leave the issue open and display a descriptive error without changing ReadyScore And an audit log entry is recorded with user ID, fix type=marketplace_connect, marketplace, timestamps, and outcome
One-Click Naming Template Generation and Application
Given images are flagged "File Naming Noncompliant" for the selected marketplace When the user clicks Fix next to "Naming Template" Then a compliant naming template is generated and applied to all selected images using allowed characters, required tokens, and extension rules for the marketplace And filename collisions are avoided by deterministic suffixing, and resulting names meet length limits and uniqueness in the project And storage paths and export manifests are updated to reference new filenames And progress feedback starts within 2 seconds and updates at least every 1 second until completion And ReadyScore for "File Naming" is 0 within 5 seconds of completion And the action is idempotent; rerunning results in zero renamed files and a summary "Already compliant" And an Undo option restores original filenames and references for 15 minutes And an audit log entry is recorded with before/after names (hashed or diff), counts, timestamps, and per-image outcomes
One-Click Color Profile Normalization to sRGB
Given images are flagged "Non-sRGB Color Profile" or "Missing Profile" When the user clicks Fix next to "Color Profile" Then all selected images are converted to sRGB IEC61966-2.1 with embedded profile and preserved pixel dimensions and bit depth And images already in sRGB are left unchanged and counted as "Already compliant" And the median visual similarity meets threshold (e.g., SSIM ≥ 0.99 versus pre-conversion) And failures on individual files are skipped with a clear reason and listed in the results And progress feedback starts within 2 seconds and updates at least every 1 second until completion And ReadyScore sub-issue count is reduced accordingly and recalculated within 5 seconds And an audit log entry is recorded with fix type=color_normalize and per-image outcome
One-Click Resize and Canvas Adjustment to Marketplace Specs
Given images are flagged "Size/Canvas Noncompliant" with target dimensions and background requirements for the selected marketplace When the user clicks Fix next to "Size/Canvas" Then each selected image is resized to meet target pixel dimensions, preserving aspect ratio and padding with the required background (transparent or #FFFFFF) to exact specs And DPI/metadata is normalized as required and no final dimension deviates by more than 1 pixel And images already within spec are left unchanged and reported as "Already compliant" And progress feedback starts within 2 seconds and updates at least every 1 second until completion And ReadyScore sub-issue is 0 for size/canvas within 5 seconds of completion And an audit log entry is recorded with old/new dimensions, padding applied, counts, timestamps, and per-image outcomes
One-Click Background Removal with Safe Fallbacks
Given images are flagged "Background Noncompliant" for the selected marketplace When the user clicks Fix next to "Background Removal" Then the background is removed to transparency or replaced with pure white according to marketplace rules, preserving product edges And if subject detection confidence is below threshold, a safe fallback is applied (non-destructive mask with soft white background) and the image is flagged for review And haloing and stray pixels are suppressed to within a 1-pixel tolerance along edges And progress feedback starts within 2 seconds and updates at least every 1 second until completion And ReadyScore sub-issue count is reduced accordingly and recalculated within 5 seconds And the action is idempotent; re-running does not further modify already compliant images And an audit log entry is recorded with mask stats (coverage %, confidence), counts, timestamps, and per-image outcomes And an Undo option restores the original background for 15 minutes
One-Click Compression to Marketplace File Size Limits
Given images are flagged "File Size Exceeds Limit" for the selected marketplace When the user clicks Fix next to "Compression" Then images are recompressed or converted (when allowed) to meet the max file size while preserving minimum dimensions and visual quality (SSIM ≥ 0.98) And formats are selected per marketplace rules (e.g., JPEG quality tuned; PNG preserved when transparency required) And images already under the limit are left unchanged and reported as "Already compliant" And progress feedback starts within 2 seconds and updates at least every 1 second until completion And ReadyScore sub-issue is 0 for file size within 5 seconds of completion And an audit log entry is recorded with before/after sizes, format changes, counts, timestamps, and per-image outcomes And the action is idempotent; rerunning on compliant images makes no changes
Readiness Meter UI
"As a user preparing my first export, I want a clear visual meter with actionable guidance so that I can see progress and what to fix at a glance."
Description

Delivers an accessible, responsive UI component embedded in the export flow that displays a numeric score, progress bar, and categorized list of blockers and warnings with concise explanations and tooltips. Provides deep links to relevant settings, hover details on rule rationale, and clear state indicators (calculating, up-to-date, stale). Designed for keyboard navigation and internationalization. Ensures minimal latency by subscribing to score updates and rendering incremental changes.

Acceptance Criteria
Score and Progress Bar Visibility in Export Flow
Given I am on the Export step with ReadyScore visible When the component loads Then a numeric score between 0 and 100 is displayed and labeled "Readiness Score" And a progress bar reflects the same value within ±1 point with role="progressbar" and correct aria-valuenow, aria-valuemin=0, aria-valuemax=100 And initial render of score and bar completes within 1000 ms on a reference device And the layout adapts to 375 px, 768 px, and 1280 px widths without horizontal scrolling and the progress bar height is at least 8 px Given a new score arrives from the subscription When the update is received Then the numeric score and progress bar update within 200 ms median and 500 ms p95 without full component reload And no content layout shift exceeds 0.02 CLS during the update
Categorized Issues List with Explanations and Tooltips
Given at least one blocker and/or warning exists When the list renders Then items are grouped under "Blockers" and "Warnings" with counts And each item shows a concise explanation of 120 characters or fewer And hovering or keyboard focusing an item reveals a tooltip with the rule rationale And tooltips are dismissible with Esc, close on focus loss, and remain within the viewport Given no items exist in a category When the list renders Then an empty state with a checkmark and localized text "No blockers" or "No warnings" is shown
Deep Links and One-Click Fixes from Flags
Given a flagged item has a one‑click fix When the user activates "Fix" via click, Enter, or Space Then the relevant settings view opens focused on the first required control and includes source=readyscore in the URL And on saving the change, the ReadyScore re-evaluates and updates within 500 ms p95 Given a flagged item has only a deep link When the link is activated Then navigation preserves the export flow context and returns via Back to the same scroll position And the linked control is scrolled into view and focused
State Indicators: Calculating, Up-to-Date, and Stale
Rule: While awaiting initial score, the component shows a visible "Calculating…" label with spinner, sets aria-busy="true", and transitions to "Up to date" within 2 seconds if data arrives Rule: After processing an update, "Up to date" is displayed and aria-busy="false" Rule: If no update has been received for 30 seconds or dependent settings changed without recompute, the state shows "Stale" with a "Refresh" action Given the user clicks "Refresh" When recomputation is triggered Then the latest score is requested and the state returns to "Up to date" on success or shows an inline error on failure Rule: A "Last updated <relative time>" timestamp is shown using the current locale
Keyboard Navigation and Screen Reader Support
Rule: All interactive elements are reachable by keyboard in a logical order and expose a visible focus indicator Rule: Tooltips open on focus and hover and close on Esc or focus loss without trapping focus Rule: Actions (Fix, links, refresh, expand/collapse) are operable with Enter and Space and have accessible names Rule: The score change is announced via an aria-live="polite" region as "Readiness score <value> percent" in the active locale Rule: Text and focus indicators meet WCAG 2.1 AA contrast and there are no keyboard traps
Internationalization: Locale, Formatting, and RTL
Rule: All user-facing strings are sourced from localization files and render correctly in en-US, es-ES, and de-DE Rule: Switching locale at runtime updates all texts, dates, and numbers without page reload Rule: Numeric values use locale formatting and dates use locale-specific formats Rule: RTL locales (e.g., ar) render correctly with mirrored layout where appropriate and correct reading order Rule: Pseudolocalization with 30% text expansion causes no clipping; overflowed labels are truncated with ellipsis and show full text on tooltip
Live Update Subscription and Incremental Rendering Performance
Given a subscribed event stream emits score updates at up to 5 events over 2 seconds When updates arrive Then the component re-renders only changed subtrees and completes each render within 120 ms median and 250 ms p95 on a reference device And CPU usage stays below 30% and additional memory stays below 5 MB during the burst And no visual flicker occurs; the progress bar animates smoothly at 60 fps on supported devices And if the subscription drops, an inline non-blocking warning is shown with a "Reconnect" action that restores updates within 2 seconds
Pre-Export Compliance Gate
"As a seller about to export, I want the system to stop me from exporting non-compliant images unless I explicitly override so that I avoid failed uploads and listing penalties."
Description

Integrates a validation gate into the export workflow that blocks submission when blockers remain, offering Fix All, resolve individually, or proceed with documented override for warnings. Generates a readiness report summarizing rules checked, fixes applied, and any overrides with timestamps for auditability. Ensures downstream exports have a higher success rate and reduces marketplace rejections.

Acceptance Criteria
Export Blocked When Blockers Remain
Given a batch contains at least one blocking issue identified by validation rules When the user clicks Export Then the export action is prevented and a compliance modal displays counts of blockers and warnings And the modal presents actions: Fix All (enabled if auto-fixable issues exist), Resolve individually, and Cancel And Proceed with Override is not displayed while any blocking issues remain And the Export button remains disabled until the blocker count equals 0
Fix All Applies Available Auto-Fixes
Given at least one auto-fixable issue exists in the current batch When the user clicks Fix All Then the system applies all available auto-fixes and shows a progress indicator updated at least every 2 seconds And validation re-runs automatically upon completion of fixes And ReadyScore, blocker count, and warning count update to reflect the new state And a summary displays total issues fixed, failures with reasons, and issues remaining by severity And no changes are made outside the scope of the selected fixes
Resolve Issues Individually Updates Readiness
Given the user chooses Resolve individually from the compliance modal When the user opens an issue from the list Then a detail pane shows the rule name, affected items, suggested fix, and an Apply Fix control When the user applies a fix on an item Then that item is revalidated immediately and its status updates within 1 second And ReadyScore updates to reflect the change And Next/Previous navigation allows moving through remaining issues And when the last blocker is resolved, the Export action becomes enabled
Override Warnings With Mandatory Justification
Given only warning-level issues remain (blockers = 0, warnings ≥ 1) When the user selects Proceed with Override Then a justification text field is required with a minimum of 10 characters And the dialog shows the exact rules that will be overridden and the count of affected items When the user confirms with a valid justification Then export proceeds And the override records user ID, UTC timestamp, rules overridden, affected item counts, and the entered justification
Readiness Report Generated and Stored
Given any export attempt occurs (blocked, successful, or overridden) When validation completes for the attempt Then a readiness report is generated containing: batch ID, user ID, UTC timestamp, target marketplace, rules checked with pass/fail per rule and counts, fixes applied (auto and manual) with counts, overrides with justification, ReadyScore before and after, and final disposition (Blocked, Exported, Exported with Overrides) And the report is stored server-side and accessible from the batch details view And the report can be downloaded as JSON and PDF And report generation completes within 3 seconds for batches up to 1,000 images
Marketplace Rule Pack Selection and Validation
Given a target marketplace link or selection exists for the batch When validation runs Then the corresponding marketplace rule pack is applied to all checks Given no marketplace is linked or selected When validation runs Then a blocking issue is created prompting the user to connect or select a marketplace with a one-click action When the user connects/selects a marketplace Then validation re-runs automatically and updates ReadyScore and issue counts
Export Proceeds at 100% Readiness
Given blockers = 0 and warnings = 0 and ReadyScore = 100% When the user clicks Export Then the export starts without additional compliance prompts And the readiness report records zero blockers, zero warnings, and zero overrides And the export event is logged with UTC timestamp and user ID for auditability
Readiness Telemetry & Analytics
"As a product manager, I want analytics on readiness and fixes so that we can improve defaults and reduce time-to-first-export for users."
Description

Captures event data around scoring changes, time-to-100%, most frequent blockers by marketplace, fix-action usage, and export success correlation. Feeds dashboards for product and support teams to optimize rules, presets, and default fixes. Implements privacy safeguards, opt-outs, and retention controls. Provides hooks for A/B testing improvements to ReadyScore and reporting impact on export success and CTR uplift.

Acceptance Criteria
Score Change Telemetry Event
Given a user’s ReadyScore changes for a project When the score updates from any value to a new value Then an event "readiness.score_changed" is emitted with fields: event_id (UUIDv4), workspace_id, session_id, project_id, marketplace, scoring_algo_version, client_version, timestamp (ISO-8601 UTC), old_score (0–100), new_score (0–100), delta, reason_codes (array) And the event is persisted within 2 seconds of emission and available in analytics within 1 minute And duplicate events for the same event_id are not stored more than once And overall duplicate rate is < 0.1% in a 24-hour window And events missing mandatory fields are rejected with a logged validation error
Time-to-100% Metric Capture
Given telemetry collection is enabled for a workspace When a project’s ReadyScore is first displayed in a session Then a timer starts for that project-session with start_time recorded And when the project first reaches 100% within 24 hours, end_time and duration_ms are recorded in a "readiness.time_to_100" event And idle periods >10 minutes between user interactions are excluded from duration_ms And the metric is aggregated daily per marketplace with p50, p90, and p99 surfaced in dashboards and exports And abandoned projects (no 100% within 24 hours) emit "readiness.time_to_100.abandoned" with last_known_score
Blockers by Marketplace Telemetry
Given ReadyScore identifies blockers for a marketplace When a blocker is detected or resolved Then events "readiness.blocker_detected" and "readiness.blocker_resolved" are emitted with fields: blocker_id, blocker_type (enum: missing_preset, missing_marketplace_link, invalid_file_naming, missing_required_attr, other), marketplace, project_id, session_id, timestamp And blocker instances are correlated via blocker_id until resolved And the Top 5 blockers per marketplace dashboard displays counts, unique projects, and median time_to_resolve over a selectable date range with filters for marketplace, date, and plan And data freshness for the dashboard is less than or equal to 5 minutes
Fix-Action Usage & Outcomes Tracking
Given one-click fixes are offered for a detected blocker When a user triggers a fix action Then an event "readiness.fix_action" is emitted with fields: fix_id, blocker_id, marketplace, project_id, timestamp, outcome (success|failed|no_op), latency_ms, pre_score, post_score And successful fixes automatically emit a linked "readiness.blocker_resolved" within 2 seconds And the Fix Usage dashboard shows conversion rate (success/attempt), average latency_ms, and median score delta per fix_id, filterable by marketplace and date
Export Success Correlation & CTR Uplift
Given exports are attempted from projects with a recorded ReadyScore When an export completes with outcome (success|failed) Then an event "export.outcome" is recorded with fields: project_id, marketplace, timestamp, outcome, error_code (if any), ready_score_at_export, time_to_100_ms (if available), variant (if in experiment) And a daily job computes and publishes export_success_rate_by_score_bucket (0–49, 50–74, 75–89, 90–99, 100) and time_to_export_after_100 distributions per marketplace And, when CTR data is available via marketplace link integration, ctr_uplift_by_ready_score_bucket and ctr_uplift_by_variant are computed with confidence intervals and exposed in analytics And all correlation datasets are filterable by marketplace, date range, and plan
Privacy, Consent, and Retention Controls
Given a workspace sets telemetry consent to opt-in or opt-out When consent is opt-out or the user enables Do Not Track Then no non-essential telemetry events are collected; only strictly necessary events for service operation are retained And all event payloads exclude raw PII; user_id is salted-hash per workspace; image content and filenames are never persisted in telemetry And data retention is configurable per workspace (default 90 days, min 7, max 365) and enforced by daily deletions with audit logs And administrators can export and delete telemetry associated to a user or project within 30 days of request And all telemetry is encrypted in transit (TLS 1.2+) and at rest (AES-256), with access restricted by role
A/B Testing Hooks for ReadyScore
Given an experiment is created targeting the ReadyScore algorithm or UI When eligible users or projects are assigned to variants Then exposure is logged via "exp.exposed" with fields: experiment_key, variant, unit_id (project_id or workspace_id), timestamp, traffic_allocation, hash_seed, scoring_algo_version And downstream telemetry includes experiment context (experiment_key, variant) on related events And experiment assignment is stable for the chosen unit and can be paused or ramped via config within 5 minutes And analysis datasets report primary (export_success, time_to_100) and guardrail metrics (error_rate, latency_ms) by variant with p-values or credible intervals

SafeRun Sandbox

Dry‑run your pipeline on sample images to preview results, compliance checks, and credit estimates before committing. See before/after diffs and a clear pass/fail list, then launch for real with a single click.

Requirements

Sample Set Selection & Management
"As a solo e-commerce seller, I want to pick a few representative product images for a dry run so that I can preview real outcomes without running the full paid batch."
Description

Enable users to select or upload a representative set of sample images (e.g., 3–20) from recent uploads, folders, or provided demo assets to dry‑run pipelines. Provide an image picker with thumbnails, basic metadata (dimensions, size), and validation for supported formats. Store samples in short‑lived, isolated storage for sandbox use, apply non‑destructive downscaling for faster previews, and allow saving named sample sets for reuse. Enforce per-workspace limits and show remaining quota. Integrates with PixelLift’s asset store and respects user permissions and marketplace profiles.

Acceptance Criteria
Image Picker From Multiple Sources
Given I am an Editor or Admin in a workspace with an active marketplace profile, When I open the SafeRun Sandbox image picker, Then I can switch between sources: Recent Uploads, Folders, and Demo Assets. Given I select images from one source and then switch to another source, When I return to the first source, Then my prior selections persist and the selection count reflects all currently selected images across sources. Given assets are displayed in any source, When I view them, Then each asset shows a selectable checkbox and unsupported formats are visibly disabled with a tooltip stating the reason. Given I have made a selection, When I click "Add to Samples", Then the selected images appear in the sandbox sample tray in the order selected.
Upload New Samples With Format Validation
Given I drag-and-drop or browse to upload files in the image picker, When files are queued, Then only supported formats (as defined by workspace settings) are accepted and queued for upload. Given unsupported files are included, When validation runs, Then each unsupported file is rejected with an inline error listing filename and reason, and no unsupported files are added to the selection. Given a supported file upload completes, When processing finishes, Then a thumbnail and basic metadata are shown and the image becomes available for selection. Given duplicate files (same content hash) are uploaded, When validation runs, Then duplicates are deduplicated and counted once in the selection.
Selection Count Rules (3–20) and Feedback
Given I have fewer than 3 images selected, When I view the selection controls, Then the Continue/Use Samples button is disabled and an inline message says "Select 3–20 images". Given I have between 3 and 20 images selected inclusive, When I view the selection controls, Then the Continue/Use Samples button is enabled and displays the current count. Given I attempt to proceed with more than 20 selected images, When I click Continue/Use Samples, Then the action is blocked with an inline error and no samples are added to the sandbox. Given an API client submits an invalid sample count, When the server validates the request, Then it responds with HTTP 422 and error code "invalid_sample_count".
Thumbnail Metadata Display in Picker
Given assets are listed in the picker, When I view any asset card, Then pixel dimensions (W×H) and file size (KB/MB) are displayed alongside the thumbnail. Given metadata retrieval is in progress, When thumbnails render, Then a skeleton/placeholder is shown for metadata until it loads, which completes within 2 seconds at the 95th percentile. Given an asset lacks readable metadata, When displayed, Then the UI shows "Unknown" for missing fields, and the asset remains selectable if its format is supported.
Isolated Short-Lived Sandbox Storage
Given I finalize my sample selection, When the sandbox session initializes, Then the selected images are copied or referenced into an isolated sandbox storage namespace not visible in the main asset library. Given a sandbox sample set exists, When 24 hours elapse without converting it to a live run, Then the sandbox copies are automatically purged and become inaccessible, while original assets remain unchanged. Given an unauthenticated or unauthorized user requests a sandbox asset URL, When the request is made, Then access is denied with HTTP 403 and no asset data is returned. Given I reopen the same sandbox within the TTL, When I view my samples, Then the same isolated copies are loaded without requiring re-selection.
Non-Destructive Downscaling for Previews
Given sandbox copies are created, When preview generation runs, Then images are downscaled non-destructively to the configured preview max dimension and compressed for faster rendering. Given previews are available, When I compare original and preview metadata, Then the original asset dimensions and file size are unchanged, and each preview's file size is less than or equal to its original. Given a preview fails to generate, When the error occurs, Then the UI surfaces a per-image error with a retry option and the failure does not block viewing of other previews.
Save & Reuse Named Sample Sets with Quota and Permissions
Given I have a valid selection (3–20), When I click Save Sample Set, Then I can enter a unique name (3–50 characters; letters, numbers, spaces, dashes, underscores) within the workspace. Given workspace limits are provided by the backend (e.g., max saved sets and storage quota), When saving would exceed any limit, Then the save is blocked and the UI shows remaining quota (e.g., "2 of 10 sets left" or "120 MB left") with guidance to free space. Given a named sample set exists, When I load it later, Then the system revalidates asset permissions and marketplace profile scope, rehydrates a fresh isolated sandbox copy, and lists any missing/unsupported assets with options to replace or remove. Given my role is Viewer, When I open the sample set manager, Then I can view and load named sets but cannot create, rename, or delete them; Editors and Admins can perform all management actions.
Before/After Diff Viewer
"As a user optimizing listings, I want an interactive before/after view so that I can quickly judge whether the edits meet my quality bar before spending credits."
Description

Provide an interactive preview that shows original vs. processed outputs via side‑by‑side, swipe slider, and grid modes. Support 100% zoom, pan, and pixel peeping, with overlays for background masks, crop boxes, and bounding guides. Display key metrics (dimensions, file size delta, background color, detected subject area) and quick toggles for pipeline steps (e.g., background removal on/off) to re‑preview fast. Generate watermarked, low‑res previews to prevent misuse. Ensure accessibility (keyboard shortcuts, ARIA labels) and responsive layout for desktop and tablet.

Acceptance Criteria
Mode Switching and Swipe Slider Interaction
Given a SafeRun sample image set is loaded in the Diff Viewer When the user selects side-by-side, swipe slider, or grid mode Then the viewer switches modes within 300 ms without a full page reload And the currently selected image pair (or sample set in grid) remains selected And the swipe handle is visible only in swipe mode, draggable/touchable from 0% to 100% of the viewer width with no dead zones And grid mode displays a two-column layout of Original and Processed thumbnails for all loaded samples, with consistent cell sizes and lazy-loading enabled
Synchronized 100% Zoom and Pan
Given side-by-side or swipe mode is active When the user sets zoom to 100% Then one image pixel maps to one screen pixel in both Original and Processed views without interpolation And panning (drag, touch, arrow keys) moves both views in lockstep with no misalignment greater than 1 px And zoom range supports 25%–800%; Ctrl/Cmd +/− and mouse wheel adjust zoom; double-click toggles 100%/Fit And zoom/pan state persists when switching between side-by-side and swipe modes
Overlay Toggles for Mask, Crop, and Guides
Given an image with generated mask and crop metadata is loaded When the user toggles Background Mask, Crop Box, or Bounding Guides overlays Then each overlay appears/disappears within 150 ms and can be independently toggled And overlay accuracy matches underlying data within ±2 px And overlay opacity is between 30% and 60% and does not block zoom/pan interactions And overlay toggles default to off and their state persists per session
Key Metrics Display and Refresh
Given a processed preview is available Then the viewer displays: Original and Processed dimensions (W×H), file size delta (KB/MB and %), background color (hex or transparent), and detected subject area (% or bounding box ratio) When a pipeline step toggle changes and the preview re-renders Then the metrics update to reflect the new result within 500 ms after image load And numeric values use consistent units and precision (integers for px; one decimal for MB; percentage rounded to whole %)
Quick Pipeline Toggles and Re-Preview Performance
Given background removal and other pipeline step toggles are visible When the user changes any toggle Then a new watermarked preview is requested without consuming credits And the previous render is canceled or superseded And the updated preview appears within 2 s for 80% of images and within 5 s worst-case And a progress indicator is shown during processing and disappears on completion
Watermarked Low-Resolution Preview Only
Given the Diff Viewer is displaying a processed result Then the loaded preview image long edge is capped at 1280 px and is overlaid with a repeating PixelLift watermark at 15%–25% opacity And any download, drag-save, or context-menu save yields only the low-res watermarked image And no full-resolution image URLs are requested over the network (verified via devtools) And zooming beyond 100% does not fetch higher-resolution assets
Accessibility and Responsive Behavior
Given the viewer is used on desktop (≥1024 px) and tablet (768–1023 px) in Chrome, Safari, and Firefox (latest two versions) Then all interactive controls have ARIA labels, visible focus indicators, and 44×44 px minimum hit area And keyboard shortcuts exist and are documented in a help tooltip: M (modes), +/− (zoom), 1 (100%), 0 (Fit), Arrow keys (pan), O (cycle overlays) And metrics updates are announced via an aria-live="polite" region And swipe slider, zoom, and pan are operable with keyboard and touch (pinch-to-zoom, drag to pan) And layout adapts without horizontal scroll; interaction framerate during zoom/pan/slider ≥ 30 FPS on test devices
Compliance Check Preview & Pass/Fail Report
"As a seller listing on multiple marketplaces, I want a pass/fail compliance preview so that I can fix issues before I launch and avoid listing rejections."
Description

Run marketplace-specific rules (e.g., Amazon: pure white background, minimum 1000px longest side, no text overlays) and brand safety checks during the sandbox run. Produce a clear per-image checklist with pass/fail/attention statuses, rule IDs, and remediation tips, including auto-fix suggestions where applicable. Allow switching marketplace profiles to compare outcomes. Surface severity levels and aggregate readiness status for the whole batch. Export the report as CSV/JSON for audit and share a link internally. Integrates with existing rules engine and marketplace profile settings.

Acceptance Criteria
Marketplace Rules Execution in Sandbox
Given I select a marketplace profile and upload sample images in SafeRun When I click "Run sandbox" Then the system invokes the existing rules engine with the selected marketplace profile settings And for each image and applicable rule, the result includes rule_id, rule_name, severity, and status in {pass, fail, attention} And per-image results render within 10 seconds for batches up to 50 images And no persistent changes are written to source images or listings
Per-Image Checklist with Remediation Tips
Given sandbox results are available When I open an image's compliance panel Then I see a checklist row for every evaluated rule with: rule_id, title, status, severity, and remediation_tip And failed and attention items are sorted above passed items And each remediation_tip is concise (<= 200 characters) and prescriptive (starts with an action verb)
Auto-Fix Suggestions and Preview
Given a rule is auto-fixable and the image fails that rule When I view the checklist Then the row shows an "Auto-fix available" indicator and a "Preview auto-fix" control And clicking "Preview auto-fix" shows a non-destructive visual preview and simulated status update for that rule And the UI displays an incremental credit estimate for applying the auto-fix And toggling the auto-fix preview off restores the original preview and status
Marketplace Profile Switching and Comparison
Given sandbox results for Marketplace A are displayed When I switch to Marketplace B Then the rules engine re-runs with Marketplace B's profile settings and refreshes results And I can view a side-by-side comparison of A vs B for the same image including differing rule outcomes and severities And each column is labeled with marketplace name and profile version And previously run results remain cached and accessible for 30 minutes
Severity Levels and Aggregate Readiness Status
Given sandbox results exist for a batch When outcomes are computed Then each rule outcome has severity in {critical, major, minor, info} And the batch header shows aggregate counts by status (pass/fail/attention) and severity And batch readiness is: Not Ready if any critical fail; Needs Attention if no critical fail but any major/minor attention or fail; Ready if all pass or info only And the banner displays percentages of images by readiness category
Export Report as CSV/JSON
Given sandbox results exist When I export as CSV Then a CSV downloads with columns: batch_id, image_id, file_name, marketplace_profile_id, rule_id, rule_name, severity, status, remediation_tip, auto_fix_available, timestamp And when I export as JSON Then a JSON file downloads with equivalent fields plus a top-level summary containing aggregate counts and batch readiness And exports reflect the currently selected marketplace profile and comparison mode And exports for batches up to 500 images complete within 15 seconds
Shareable Internal Link to Report
Given sandbox results exist When I click "Share internal link" Then a non-guessable link (token length >= 32) is generated and restricted to authenticated org users And the link opens a read-only view of the report with image-level and aggregate details and without auto-fix execution controls And I can set an expiry (default 7 days) and revoke the link; revoked or expired links return HTTP 410 on access And audit logs record link creation, access, and revocation with user_id and timestamp
Credit Cost Estimator & Time Forecast
"As a cost-conscious user, I want to see estimated credits and time before I commit so that I can control spend and schedule my launch."
Description

Estimate total credits and runtime for the full batch based on selected pipeline steps, sample complexity (subjects, masks, variations), and current queue load. Show a per-step cost breakdown, confidence range, and assumptions (e.g., model versions, resolution caps). Update estimates in real time when settings change and highlight optimizations (e.g., reducing variations cuts credits by X%). Display expected start/finish windows considering concurrency and tier limits. No credits are consumed by the sandbox itself.

Acceptance Criteria
Per-Step Breakdown and Totals
Given a selected pipeline, batch size, and sample-derived complexity metrics, when the estimator runs, then it displays for each step: step name, unit credits, unit time, and step subtotal credits/time. And the displayed total credits equal the sum of step subtotals within 0.01 credits, and total runtime equals the sum of step runtimes adjusted for configured concurrency. And all values include units with standardized rounding (credits to 2 decimals, time to nearest second), and steps with zero cost display 0.00 credits.
Confidence Range and Assumptions Panel
Given estimates are computed, when the user opens Assumptions, then min/likely/max credits and runtime are shown with a labeled 90% confidence band. And the panel lists exactly the parameters used: model version IDs, resolution caps, concurrency/tier limits, queue snapshot timestamp, and sample complexity metrics (e.g., subjects, masks, variations per image). And all listed assumption values match those applied in the calculation at the time of display.
Real-Time Recalculation on Setting Change
Given the estimator is visible, when the user changes pipeline steps, batch size, variations, resolution, model version, or the sample set, then credit and time estimates recalculate and update within 1 second of the change. And updated fields are visually highlighted and include deltas versus the previous values (e.g., −12.00 credits, −00:45). And the Last Updated timestamp reflects the recalculation time within 2 seconds accuracy.
Optimization Hints and One-Click Apply
Given a configuration where a single change yields at least 5% savings in credits or runtime without violating user-selected requirements, when detected, then an optimization hint appears quantifying the impact (e.g., “Reducing variations 5→3 saves 40.00 credits”). And clicking Apply on the hint modifies the configuration accordingly and triggers recalculation, with the new totals reflecting the predicted savings within 1 second. And if no qualifying optimizations exist, no hint is displayed.
Start/Finish Windows Respect Queue and Tier
Given the user’s tier concurrency limits and current queue load, when estimates are computed, then expected start and finish windows are displayed in the user’s timezone. And the windows honor max concurrent jobs and queue position; if the earliest start is more than 15 minutes from now, a delayed start notice is shown. And the forecast does not propose concurrency above the tier limit at any point in the timeline.
Sandbox Dry Run Does Not Consume Credits
Given the user runs a SafeRun Sandbox dry run, when the preview completes, then the account credit balance remains unchanged. And the estimator clearly labels the session as “Dry run — no credits consumed,” and audit logs record zero credit charge for the sandbox action. And launching the real run from the sandbox deducts credits only at real-run initiation, not during preview.
Queue Load Auto-Refresh Updates Forecast
Given the estimator remains open, when backend queue load changes by 10% or more, then start/finish windows and runtime estimates refresh within 10 seconds and display a “Queue updated” indicator with timestamp. And the user can manually refresh the snapshot; manual refreshes are rate-limited to one every 5 seconds. And the confidence band and assumptions timestamp update to reflect the new queue snapshot.
One-Click Promote to Live Run
"As a busy seller, I want to launch the real run from the preview in one click so that I can move from testing to production without reconfiguring anything."
Description

Offer a single action to convert the sandbox configuration into a live job targeting the chosen source set (folder, recent upload, or integration feed). Present a confirmation modal summarizing pipeline steps, expected cost/time, and compliance risks, then lock the snapshot to ensure identical settings. Create an idempotent job with a unique token to prevent double charges on repeated clicks. Route to the standard job tracker with progress, pausing, and cancellation controls. Supports rollback to sandbox if the user wants to tweak and re‑preview.

Acceptance Criteria
Promote to Live: Confirmation Modal Summary
Given a configured SafeRun sandbox and a selected source set (folder, recent upload, or integration feed) When the user clicks Promote to Live Then a confirmation modal appears showing: pipeline steps in order; selected source set name and item count; expected total credits and per-image credits; estimated duration; compliance risk summary (pass/fail counts); and a snapshot ID/hash And the modal presents primary action Confirm & Launch and secondary action Cancel And Confirm & Launch remains disabled until any required compliance acknowledgement is completed And on job creation failure, the modal displays an inline error without launching a job
Snapshot Lock of Sandbox Settings
Given the user confirms launch from the modal When the live job is created Then the system persists an immutable snapshot including pipeline steps, model versions, parameters, and asset references And the live job executes against that snapshot even if the sandbox configuration changes later And the job tracker and job API expose the snapshot ID/hash And attempts to modify the live job configuration are rejected with 409 Conflict and an explanatory message
Idempotent Job Creation with Unique Token
Given the client provides an idempotency token on confirm When duplicate create requests are received within 10 minutes (e.g., repeated clicks, page refresh, or network retry) Then exactly one live job is created And subsequent duplicate requests return 200 with the original jobId and no additional credit authorization And audit logs contain a single JobCreated event for that token And concurrent clicks from multiple tabs do not create extra jobs
Route to Job Tracker with Operational Controls
Given the job is created When the user confirms launch Then the user is routed to the standard job tracker for that job And the tracker displays queued/running/completed/failed counts and ETA And Pause, Resume, and Cancel controls are available according to job state And Pause halts new item processing within 30 seconds; Cancel stops remaining items and refunds unconsumed credits And page refresh preserves state and progress
Compliance Risk Acknowledgement and Details
Given the sandbox compliance checks have flagged risks When the user opens the confirmation modal Then the modal shows a risk list with severity badges and pass/fail counts And Confirm & Launch is disabled until the user explicitly acknowledges risks via a checkbox And a View details link opens the sandbox before/after diff and check results in a new panel or tab And if no risks are flagged, no acknowledgement is required
Rollback to Sandbox for Tweaks and Re‑Preview
Given the user is on the job tracker after promotion When the user selects Return to Sandbox Then the app navigates to SafeRun with the last sandbox configuration preloaded And the promoted job remains unchanged and continues (or retains its paused/cancelled state) And the user can modify settings and re-run a preview without consuming live-run credits And a deep link/URL parameter preserves the context
Correct Source Set Targeting and Validation
Given the user selected a specific source set (folder, recent upload batch, or integration feed) When the job is launched Then only eligible items from the selected set are processed And pre-run validation verifies access permissions and item counts; if limits are exceeded or access is missing, the modal shows an actionable error and disables Confirm And the tracker displays total eligible, skipped, and processed counts with reasons for any skipped items And on completion, processed count equals eligible item count; no items outside the selected set are processed
Pipeline Config Snapshot & Versioning
"As a repeat user, I want versioned snapshots of my preview configuration so that I can reproduce results and share exact settings with my team."
Description

Capture an immutable snapshot of all sandbox settings: pipeline graph, step parameters, model versions, marketplace profile, output formats, and random seeds for variations. Assign a version hash and allow comparing diffs between snapshots. Support naming, cloning, and sharing read-only links within the workspace. Enable restoring any snapshot as a template for future runs and record it in the audit log for traceability across previews and live jobs.

Acceptance Criteria
Create Immutable Snapshot from Sandbox
Given a configured SafeRun sandbox with a defined pipeline graph, step parameters, model versions, marketplace profile, output formats, and random seeds When the user clicks “Save Snapshot” Then the system persists an immutable snapshot that contains exactly: pipeline graph (nodes/edges), step parameters (names, types, values), model version IDs, marketplace profile ID, output formats (codec, dimensions, compression), and random seeds per variation And the snapshot is marked read-only; any direct edit attempt is blocked in UI and API with a 403 and “immutable snapshot” message And if no name is provided, a default name of “Sandbox Snapshot {yyyy-mm-dd hh:mm:ss}” is assigned And the save operation completes within 2 seconds for configurations up to 25 steps and 200 KB of metadata
Version Hash Generation and Uniqueness
Given a snapshot payload at save time When the snapshot is created Then a deterministic content-derived version hash (>=128-bit hex) is computed from the captured fields excluding non-functional metadata (name, description, timestamps, owner) And two snapshots with byte-identical captured content produce the same hash; any difference in captured content produces a different hash And the UI shows the full hash and an 8-char short hash; both are searchable; a “Copy” action copies the full hash to clipboard And in test with ≥1,000,000 synthetic variants, no hash collisions are observed
Diff Between Two Snapshots
Given two snapshot hashes in the same workspace When the user selects Compare Then a structured diff is displayed with sections for pipeline steps (added/removed/modified), step parameter deltas, model version changes, marketplace profile change, output format changes, and random seed changes And a summary indicates counts of adds/removes/modifies by section And non-functional fields (name, description, timestamps) are excluded by default with a toggle to include And the diff view supports filtering by section and exporting the diff to JSON And diffs render within 1 second for snapshots up to 25 steps and 200 KB each
Clone Snapshot and Edit as New Draft
Given a user with Editor or above permissions and a snapshot hash When the user selects Clone Then a new editable sandbox configuration is created with content copied from the snapshot and a new ID, while the original snapshot remains immutable And the cloned config name defaults to “Clone of {snapshot name}” and must be unique within the project And random seeds are preserved by default with an option to regenerate seeds before first run And Viewers cannot clone; Editors and Owners can And the clone action is recorded in the audit log with source and destination IDs
Restore Snapshot as Template for Live Run
Given a valid snapshot hash When the user clicks “Restore as Template” Then a template is created and set as the current working configuration for both SafeRun and Live launchers without modifying the original snapshot And launching a SafeRun or Live job from this template records the snapshot hash on the job and in the audit log And if any referenced model version, marketplace profile, or output preset is missing or deprecated, a blocking validation list is shown with actionable resolutions before launch And when resources and app versions match, running the same inputs with the same seeds produces identical outputs across SafeRun and Live (pixel-identical or within configured deterministic tolerance) And template creation completes within 1 second
Share Read-only Snapshot Link Within Workspace
Given a snapshot and a workspace with authenticated members When the owner or editor generates a share link Then a URL is created that is accessible only to workspace members; unauthenticated or external users are denied And the snapshot opens in a read-only view with edit controls disabled; Editors may clone from the view; Viewers cannot clone And the link supports optional expiration and can be revoked; revoked or expired links return 410 and are unusable And share, open, revoke, and clone-from-link events are recorded in the audit log
Audit Log Traceability Across Previews and Live Jobs
Given snapshot lifecycle events (create, clone, compare, share, restore, run) When any such event occurs Then an append-only audit entry is written including actor ID, timestamp, action, snapshot hash, related object IDs (sandbox ID, template ID, run/job IDs), and workspace ID And the snapshot detail view lists all associated SafeRun previews and Live jobs with timestamps and statuses And the audit log is searchable by snapshot hash and job/run ID and exportable to CSV and JSON And audit records are retained for at least 12 months
Sandbox Isolation, Limits, and Safety Guards
"As a workspace admin, I want the sandbox to be resource‑isolated and capped so that previews are safe, fast, and never impact live processing or billing."
Description

Execute dry runs in isolated, throttled workers with defined concurrency, CPU/GPU caps, and timeouts to protect production capacity. Cache intermediate results to accelerate re‑previews while ensuring no billable artifacts are exported. Enforce maximum sample count, resolution ceilings, and daily sandbox budget per workspace. Provide clear error states and a kill switch for runaway previews. All telemetry and logs are tagged as nonbillable and stored with short retention.

Acceptance Criteria
Isolated Sandbox Execution
- Given a workspace triggers a SafeRun dry-run, When the job is scheduled, Then it runs only on the configured sandbox worker pool and cannot be placed on production workers. - Given a sandbox worker, When the job attempts to access production queues, buckets, or databases, Then the requests are denied and audited without production side effects. - Given job completion or cancellation, When the worker finalizes the job, Then all job-scoped temp storage, credentials, and mounts are destroyed and are not accessible to subsequent jobs. - Given cross-tenant operations, When multiple workspaces run sandbox jobs concurrently, Then no data or artifacts from one workspace are visible to another.
Configurable Resource Caps and Throttling
- Given configured caps per workspace (concurrency=N) and per job (CPU=C vCPU, GPU memory=M, timeout=T), When K sandbox jobs are launched, Then at most N run concurrently and the remainder are queued. - Given a running sandbox job, When its CPU/GPU demand exceeds configured caps, Then usage is throttled to the caps and the event is recorded in metrics. - Given a sandbox job exceeding the configured timeout T, When T elapses, Then the job is terminated with status "Timeout" and no partial exports occur. - Given a global sandbox admission threshold Q, When new sandbox jobs arrive and the threshold is hit, Then jobs are queued or rejected with "Throttled" without impacting production job SLAs.
Sample Count and Resolution Limits Enforcement
- Given workspace limits configured: maxSamples=S and maxResolution=W×H, When the user selects exactly S images at resolution ≤ W×H, Then validation passes and processing starts. - Given a selection with S+1 images, When validation runs, Then the run is blocked with error code "LimitExceeded:SampleCount" and no processing begins. - Given one or more images exceeding W×H, When validation runs, Then the run fails fast with error code "LimitExceeded:Resolution" listing offending filenames. - Given the user adjusts the selection to comply with limits, When validation is re-run, Then the request passes without staff intervention.
Daily Sandbox Budget Enforcement
- Given a workspace daily sandbox budget B and remaining budget R, When the estimator computes E for a configured dry-run, Then if E ≤ R the run is allowed; if E > R the run is blocked pre-execution with status "BudgetExceeded". - Given a successful sandbox run consuming C credits within the sandbox ledger, When the next estimate is computed, Then remaining budget equals R−C and is shown consistently in UI and API. - Given the daily window boundary (configurable timezone), When the window rolls over, Then the sandbox budget resets and the previous day’s ledger is archived. - Given production billing systems, When sandbox runs occur, Then no billable line items are created or exported to invoicing.
No Export of Billable Artifacts in Sandbox
- Given a sandbox run produces previews and A/B variants, When the pipeline reaches export stages, Then export actions are disabled or redirected to ephemeral, nonbillable storage isolated from production endpoints. - Given any artifact produced in sandbox, When metadata is inspected, Then previewOnly=true and billingCategory=nonbillable are set. - Given webhook/integration connectors configured for production, When a sandbox run executes, Then no outbound calls are made and audit logs show zero deliveries for the run. - Given a user attempts manual export from the sandbox UI, When they click export, Then they are prompted to launch a real run instead and no artifacts are exported.
Caching and Re-preview Acceleration Without Side Effects
- Given identical inputs and pipeline graph between Run1 and Run2 within configured cache TTL, When Run2 executes, Then cached intermediates are reused and Run2 total duration is measurably lower than Run1 (as recorded in metrics). - Given any step parameters change that affect outputs, When a subsequent run starts, Then cache is invalidated for affected steps and only impacted stages recompute. - Given cached artifacts in sandbox storage, When they are persisted, Then they are tagged nonbillable and excluded from production export paths. - Given cache TTL expires, When a new run starts, Then expired cached intermediates are not used and are eligible for purge.
Error States, Kill Switch, and Short-Retention Observability
- Given a running sandbox job, When a user triggers the kill switch via UI or API, Then the job transitions to status "Canceled" within the configured SLA S and releases all resources. - Given an error condition (Timeout, BudgetExceeded, LimitExceeded, ResourceCapExceeded, Throttled), When it occurs, Then the run ends with a distinct terminal status and a user-facing error message with remediation hints. - Given logs, metrics, and traces emitted by sandbox runs, When stored, Then they are tagged sandbox=true and nonbillable and retained only for a configurable short retention window; after the window elapses they are automatically purged. - Given audit trails are queried, When filtering by job ID, Then entries include workspace ID, sandbox=true, resource usage summary, and zero links to billable artifacts or production endpoints.

CSV AutoMap

Automatically parses filenames and folder patterns to map SKUs, variants, and image roles. Generates the right CSV template per marketplace and pre‑fills rows—eliminating tedious setup and format mistakes on day one.

Requirements

Smart Filename Pattern Parsing
"As a solo e‑commerce seller, I want filenames to be auto‑parsed into SKUs, variants, and image roles so that I don’t have to create manual mapping rules or rename files before exporting CSVs."
Description

Automatically detect and extract SKU, variant attributes (e.g., color, size), and image roles (e.g., main, alternate, swatch, lifestyle, infographic) from diverse filename conventions at scale. Support token-based patterns, delimiters, regex, camel/kebab/snake case, and Unicode. Provide marketplace-aware role normalization, confidence scoring, and interactive suggestions to refine rules. Handle edge cases such as duplicated tokens, missing attributes, conflicting roles, and noisy prefixes/suffixes. Performantly parse thousands of files per batch and expose a structured mapping for downstream CSV generation.

Acceptance Criteria
Mixed-convention batch parsing accuracy
Given a labeled validation set of 5,000 product image filenames covering token-based patterns, mixed delimiters (hyphen, underscore, dot, space), camel/kebab/snake case, and Unicode characters When Smart Filename Pattern Parsing runs with default settings and no manual rules Then a structured mapping is produced per file with fields: sku, variant attributes (e.g., color, size), and image role And SKU extraction achieves F1 >= 0.98 And variant attribute extraction achieves micro-F1 >= 0.96 And image role detection achieves accuracy >= 0.96 And unparsed files are <= 0.5% and flagged as needs_review And zero crashes or encoding errors are logged
Marketplace-aware role normalization
Given a marketplace context is provided (e.g., Amazon, eBay, Shopify) And filenames contain role tokens and synonyms (e.g., main, hero, primary, alt1, alternate-02, swatch, lifestyle, infographic) When parsing is executed Then roles are normalized to the marketplace’s canonical role set and position indices as defined in configuration And >= 99% of cases in a labeled set of 500 filenames per marketplace match the expected canonical role and position And unrecognized roles are mapped to UNKNOWN with confidence < 0.50 and flagged needs_review And a normalization audit log lists original token, canonical role, position, and confidence for each file
Confidence scoring and review flagging
Given per-field confidence scoring in [0.00, 1.00] is enabled And a batch of parsed results is available When the confidence threshold is set to 0.70 Then any file with sku, any required attribute, or role confidence < 0.70 is marked needs_review And per-file confidence values are emitted for sku, each attribute, and role with two-decimal precision And a batch summary reports counts by review status and average confidence per field And repeated runs on identical input and configuration yield identical confidence scores (deterministic)
Interactive rule suggestions
Given a user selects a sample of at least 100 filenames from a batch When the user opens AutoMap Suggestions Then the system proposes up to 3 ranked parsing rules (token, delimiter, and/or regex) within 2 seconds (P95) And each suggestion displays estimated coverage (%) and precision (%) computed on the sample And accepting the top suggestion and re-parsing reduces the needs_review rate by at least 50% on the full batch or to <= 1.0%, whichever results in fewer needs_review items And the user can preview at least 10 before/after mappings before applying the rule to the batch
Edge-case resilience
Given filenames include duplicated tokens, missing attributes, conflicting role tokens, and noisy prefixes/suffixes (e.g., IMG_, copy, final, v2, date stamps) When parsing runs Then noise terms from a configurable list are ignored during tokenization And duplicated tokens do not overwrite previously extracted values unless the new occurrence has higher confidence And missing attributes are set to null and flagged incomplete without blocking extraction of other fields And conflicting role tokens result in role=UNKNOWN, error code=ROLE_CONFLICT, and needs_review flag And 0 unhandled exceptions occur across a labeled set of 1,000 such edge-case filenames
High-volume performance at scale
Given a batch of 50,000 filenames (each <= 255 bytes) on the reference environment (8 vCPU, 8 GB RAM) When parsing executes with default concurrency Then average throughput >= 2,000 files/second and total wall time <= 30 seconds And P95 per-file parsing latency <= 30 ms And peak process memory (RSS) <= 1.5 GB And 0 tasks time out and 0 files are dropped And results are streamed in chunks of <= 5,000 records with backpressure support
Structured mapping for CSV generation
Given parsing completes for a batch When exporting the structured mapping Then each record conforms to the published JSON Schema (v1.x) with required fields: file_path, sku, attributes (object), role (canonical), marketplace_role (canonical/slot where applicable), confidence (object per field), flags (array), errors (array of {code, message}) And schema validation passes for 100% of records And the downstream CSV generator consumes the mapping in a dry run to produce marketplace CSVs for at least two marketplaces with >= 99% fill rate on required columns and 0 schema validation errors
Hierarchical Folder Mapping
"As a catalog manager, I want folder structures to inform product and variant grouping so that my existing organization habits reduce setup time and improve mapping accuracy."
Description

Leverage folder hierarchy to infer product groupings, variant families, and default image roles, then merge these signals with filename parsing using configurable precedence rules. Support directives at the folder level (e.g., _main, _ignore) and optional per-folder config files to declare SKU roots or attribute defaults. Work with local uploads, zip archives, and cloud imports while skipping system folders. Provide a visual tree preview and reconciliation of folder-derived mappings with filename-derived mappings.

Acceptance Criteria
Infer product groups and variants from folder hierarchy
Given a root folder "products/SKU-1001" with subfolders "color=red" (2 images) and "color=blue" (3 images) When the user imports the folder with default hierarchy rules Then one product group "SKU-1001" is created And two variants are created with attributes Color=Red and Color=Blue And all 5 images are mapped to their respective variants And if no role is inferred by directives or filenames, the first alphanumeric image in each variant is assigned role=primary and the rest default to role=secondary
Merge mappings using precedence rules
Given precedence setting "folder_over_filename" and an image at "SKU-1002/color=red/IMG_blue_01.jpg" where the folder infers Color=Red and the filename infers Color=Blue When mappings are reconciled Then the resulting variant attribute is Color=Red And a conflict entry is logged noting source=filename and the overridden value And the preview marks the filename-derived value as overridden Given precedence setting "filename_over_folder" for the same path When mappings are reconciled Then the resulting variant attribute is Color=Blue And a conflict entry is logged noting source=folder Given a role conflict where folder directive sets role=primary and filename tag sets role=alt When mappings are reconciled Then the resolution follows the active precedence setting and is reflected in the preview
Process folder-level directives _main and _ignore
Given a folder path "SKU-200/_main" containing images When the folder is imported Then the first alphanumeric image in "_main" is assigned role=primary for SKU-200 And remaining images for that SKU default to role=secondary unless otherwise specified by filename tags or configs Given any folder named "_ignore" at any depth with images When imported Then files within "_ignore" are excluded from mapping and CSV export And the import summary lists the count of ignored files and their paths Given both "_main" and "_ignore" exist under the same SKU When imported Then only contents outside "_ignore" are considered for mapping and role assignment
Apply per-folder config files for SKU roots and attribute defaults
Given a per-folder config file ".pixellift.json" with {"skuRoot":"SKU-LINEA","defaults":{"Color":"Green","Material":"Canvas"}} placed at "Catalog/Sneakers/" When importing "Catalog/Sneakers/Retro/IMG_1.jpg" Then the resulting SKU begins with "SKU-LINEA" And the variant defaults include Color=Green and Material=Canvas unless overridden by deeper folder names or filenames Given a child folder "Catalog/Sneakers/Retro/color=blue" under the same config When importing Then Color=Blue overrides the default while Material=Canvas is retained Given a malformed ".pixellift.json" When scanning Then the config is ignored for that folder And a validation error with file path and reason is surfaced in the preview and logs
Support local, zip, and cloud imports; skip system folders
Given a local folder upload containing ".DS_Store", "__MACOSX", ".git", or "Thumbs.db" When scanning Then these files/folders are skipped And they do not appear in the preview or mapping counts Given a zip archive with the same contents When uploaded Then the archive is unpacked without persisting files to disk And the same skipping rules are applied consistently And path normalization preserves SKU folder boundaries Given a cloud import from S3 or Google Drive with a selected prefix When scanning Then pseudo-folders are derived from keys And prefixes starting with "." or "__" are ignored And mapping results match those from an equivalent local folder structure
Visual tree preview and reconciliation
Given a completed scan When the preview renders Then a collapsible tree of folders/files is shown with per-node badges for inferred product, variant attributes, and roles And conflict nodes are flagged with a warning icon and count Given the user toggles the precedence setting in the preview When applied Then the tree updates within 1 second for up to 2,000 nodes And conflict counts update accordingly Given unreconciled mandatory conflicts remain (e.g., missing primary image per product) When the user clicks "Apply Mapping" Then the action is blocked with an inline message listing the affected SKUs Else the mapping is saved and CSV generation is enabled using the reconciled mapping
Marketplace CSV Template Generation
"As a marketplace seller, I want the correct CSV template generated for my channel so that I avoid format errors and rework caused by outdated or incorrect columns."
Description

Generate and maintain up-to-date CSV templates for supported marketplaces (e.g., Amazon, eBay, Etsy, Walmart, Shopify), including required/optional columns, variant-linking fields, and locale-specific nuances. Map PixelLift’s internal fields and image role taxonomy to each marketplace’s schema with version pinning and automatic updates when schemas change. Provide inline documentation/tooltips, downloadable blank templates, and a schema diff view when versions update.

Acceptance Criteria
Marketplace-Locale Template Generation
- Given a user selects a supported marketplace and locale, when they generate a template, then the CSV contains exactly the required and optional columns defined by the pinned schema for that marketplace-locale. - The template includes all variant-linking fields defined by the schema. - Column order matches the marketplace’s specification (if defined) or a stable default order otherwise. - The generated file name includes marketplace, locale, and schema version (e.g., marketplace-locale-v{schemaVersion}-template.csv). - The file is UTF-8 encoded and contains a single header row with zero data rows. - Template generation completes in ≤ 2 seconds for schemas with up to 500 columns (95th percentile).
Schema Version Pinning and Auto-Update
- The system ingests and makes available new marketplace schema versions within 24 hours of detection. - Existing projects remain pinned to their current schema version until the user explicitly upgrades. - Users see an in-app notification when a newer schema exists for their pinned version. - Initiating an upgrade requires viewing a schema diff and explicit confirmation. - After upgrade, newly generated templates use the new version; templates generated prior to upgrade remain unchanged. - All schema version changes (detect, notify, upgrade) are audit-logged with timestamps and user IDs.
Internal Field and Image Role Mapping Completeness
- 100% of PixelLift internal fields required by any supported marketplace-locale have a defined mapping for each schema version or are explicitly marked Not Applicable. - Image roles (e.g., main, alternate, swatch, 360/spin) map to the correct marketplace columns with defined ordering/cardinality rules. - Where value transformation is required (e.g., boolean to Y/N, enum remap), transformation rules are defined and covered by automated unit tests. - Build-time validation fails if any required field lacks a mapping for a released marketplace-locale schema version. - Mappings are versioned and tagged to the marketplace schema version to which they apply.
Inline Documentation and Tooltips
- Every column displayed in the template UI has a tooltip with: definition, required/optional status, data type/format, allowed values or max length, locale-specific notes, last updated date, and link to official marketplace docs. - Tooltip content is sourced from schema metadata matching the pinned version. - Documentation coverage is ≥ 95% of columns per marketplace-locale; gaps are surfaced in an admin report. - Tooltips are accessible: keyboard focusable, aria-describedby set, and readable by screen readers. - Clicking the docs link opens the official documentation in a new tab/window.
Downloadable Blank CSV Templates
- Users can download a blank CSV template for any supported marketplace-locale and pinned schema version from the UI. - The file name follows {marketplace}-{locale}-template-v{schemaVersion}.csv. - The file is UTF-8 encoded, comma-delimited, RFC 4180 compliant, with a single header row and no data rows. - Download initialization occurs in ≤ 1 second; file delivery completes in ≤ 2 seconds for 95th percentile users. - Generated templates pass a CSV validator with zero errors.
Schema Diff View Between Versions
- The diff view lists all changes between current and target versions: added/removed/renamed columns, required⇄optional toggles, data type/format changes, enum additions/removals, and description changes. - Changes are categorized and filterable; counts per change type are displayed. - For detected renames, suggested migration mappings are shown when heuristics or metadata provide confident matches. - The diff view renders in ≤ 1 second for schemas up to 1,000 columns. - Users can export the diff as CSV and JSON.
Auto Prefill & Row Assembly
"As a time-pressed seller, I want CSV rows to be pre‑filled from my files so that I can export a ready-to-upload file with minimal manual edits."
Description

Pre-fill CSV rows using parsed SKUs, variant attributes, and image roles, assembling parent/child variant rows and attaching the correct image references per marketplace requirements. Support batch A/B variation groupings, deduplicate repeated assets, and preserve stable identifiers across re-exports. Provide an editable grid to review, override, and bulk-edit values before export, with change tracking and undo. Ensure output respects column order, encoding, and size limits imposed by each marketplace.

Acceptance Criteria
Auto-Parse SKUs, Variants, and Image Roles from Filenames and Folders
Given a batch of images named and/or nested using documented SKU-Variant-Role patterns When the user selects a marketplace template and runs CSV AutoMap Then 100% of files conforming to the pattern are parsed into SKU, variant attributes, and image role fields And nonconforming files are listed in an Unmapped panel with a parse-failure reason per file And the grid is prefilled with one row per discovered SKU/variant with parsed values populated And marketplace-specific attribute columns are added only when required by the selected template And the parsing and prefill complete in under 30 seconds for a batch of 1,000 images on baseline hardware
Assemble Parent/Child Variant Rows per Marketplace Template
Given products with color and size variants detected from filenames/folders When CSV rows are generated for the selected marketplace Then one parent row is created per product where the marketplace requires parent/child relationships And child rows reference the parent via the marketplace-specific parent key (e.g., parent_sku) and relationship type And all required columns for parent and child rows are populated according to the template rules And parent rows appear before their children where the marketplace mandates ordering And any row missing a required field is flagged with a blocking validation error and cannot be exported until resolved
Attach Correct Image References and Roles per Marketplace Limits
Given images mapped to roles (e.g., main, swatch, additional) for each SKU/variant When prefilled rows are generated for the selected marketplace Then the first valid main-role image is assigned to the marketplace's primary image field; if none exists, the row is flagged with a blocking validation error And additional images are assigned in deterministic order to the correct columns up to the marketplace's maximum allowed image count And images beyond the limit are excluded from the row and listed in a non-blocking warning with counts And role names are translated to the marketplace-specific column headers And duplicate references to the same underlying asset within a SKU/variant row are deduplicated so each image appears at most once per role
Batch A/B Variation Grouping and Prefill
Given two or more image sets per SKU distinguished with A/B group indicators in filenames or folders When CSV AutoMap runs Then the system creates exactly two groups (A and B) per eligible SKU And for marketplaces that support custom columns, an ab_group column is populated with A or B; otherwise two export files are generated with -A and -B suffixes And A and B rows are identical except for image reference columns And every SKU in group A has a corresponding SKU in group B with a one-to-one match count And any SKU missing a counterpart is flagged with a non-blocking warning identifying the missing group
Preserve Stable Identifiers Across Re-exports
Given an initial prefill/export producing stable row and asset identifiers When the same source set is re-exported with no changes to SKU/variant composition Then the stable identifiers remain identical across exports and are visible in the grid for comparison And sort order of rows remains deterministic between exports And newly added SKUs/assets receive new identifiers without reusing any previously assigned identifiers And removed SKUs/assets do not cause identifier reassignment for remaining rows
Editable Grid: Review, Bulk-Edit, Change Tracking, and Undo
Given a prefilled editable grid When a user edits a single cell and saves Then the edited value persists and is used in the exported CSV When a user selects multiple rows/columns and applies a bulk edit Then all targeted cells are updated in one operation with a single change-log entry And every change is recorded with user, timestamp, field, old value, and new value And the user can undo and redo at least the last 20 changes, restoring exact previous values And inline validation runs on edit, blocking export for errors and allowing export for warnings And a Reset to Prefill action restores prefilled values for the selected cells
Export Formatting Compliance per Marketplace
Given a selected marketplace template with specified header names, column order, encoding, quoting, and file-size limits When the user exports the CSV Then the file's header names and column order exactly match the template And the file is encoded per template requirements (e.g., UTF-8 or UTF-8 with BOM) with correct line endings And values are quoted and escaped per the marketplace rules (e.g., RFC 4180) And if the generated file would exceed the marketplace's maximum file size, the system splits the export into sequentially numbered parts that each meet the limit and includes a manifest listing the parts And an export is blocked with a clear error message if any required column is missing or any row has a blocking validation error
Validation & Confidence Scoring
"As a seller concerned about rejection errors, I want the system to flag and help fix mapping issues before export so that my uploads succeed on the first try."
Description

Validate mappings against marketplace rules (e.g., mandatory main image, max image count, variant link integrity, allowed role sets) and surface errors, warnings, and low-confidence inferences with actionable fixes. Provide a validation summary, per-row issue markers, and one-click auto-fix suggestions where safe. Block export on critical failures while allowing overrides for warnings. Expose confidence scores and require confirmation for low-confidence mappings.

Acceptance Criteria
Export is blocked on critical validation failures
Given a target marketplace is selected and a mapped dataset exists And at least one row violates a critical rule (e.g., missing MAIN image, images per SKU exceed marketplace max, disallowed role) When the user clicks Export CSV Then the Export action is blocked and the Export button is disabled And an error modal displays the total number of critical failures with links to affected rows And the Validation Summary shows Critical count > 0 And export becomes enabled only when Critical count = 0
Per-row issue markers and validation summary filters
Given validation has been run on the current dataset When the user views the grid Then any row with issues displays a severity icon per row (Critical, Warning, Info) And hovering the icon reveals rule ID, message, and suggested fix (if available) And clicking a severity pill in the Validation Summary filters the grid to matching rows And the counts by severity in the Validation Summary equal the number of rows/issues shown after filtering
Marketplace rule compliance checks for images and roles
Given a marketplace profile is selected (e.g., Amazon, eBay) with configured rules When validation runs Then each SKU must have exactly one MAIN image (Critical if missing or >1) And total images per SKU must be <= profile.max_images (Critical if exceeded) And image roles used must be within profile.allowed_roles (Critical if disallowed) And each violation records rule code, severity, affected row/field, and fixability flag
Confidence scoring and required confirmation for low-confidence mappings
Given AutoMap has inferred mappings with confidence scores from 0–100 And the low-confidence threshold is set to 70 by default (configurable) When at least one mapping has confidence < threshold Then those fields are flagged as Low Confidence in the grid and summary And Export is disabled until each low-confidence mapping is either corrected or explicitly confirmed And confirming a low-confidence mapping records user, timestamp, and prior value
One-click safe auto-fix suggestions with preview and undo
Given validation detects issues eligible for safe auto-fix (e.g., assign first image as MAIN when MAIN missing and images exist; normalize role casing; re-order to meet max_images; fill variant link from deterministic filename pattern) When the user clicks Auto-Fix Safe and confirms the preview Then the system applies only fixes marked fix_safe = true and shows a diff summary (rows affected, rules resolved) And validation is re-run automatically after fixes And an Undo action reverts all changes from the last auto-fix batch within the session
Warning-level overrides permitted with audit trail
Given the dataset contains Warning-level validation issues and no Critical issues remain When the user chooses Override Warnings and confirms with a reason Then Export proceeds And an audit record is stored with user, timestamp, rule IDs, row counts, and provided reason And the Validation Summary reflects that warnings were overridden for this export session
Variant link integrity validation and guided fixes
Given SKUs contain variant groups with parent-child relationships When validation runs Then every child SKU must reference an existing parent and share a consistent variant group key (Critical if orphaned or mismatched) And cycles or duplicate parents within a group are flagged as Critical And missing or inconsistent variation attributes across a group are flagged as Warning And the system offers guided fixes: create/link parent or reassign group; mark as safe only if confidence >= 85, otherwise require user confirmation
Mapping Profiles & Rule Library
"As a repeat user, I want to save my mapping rules as reusable profiles so that future batches auto-map correctly without reconfiguration."
Description

Allow users to save, version, and reuse mapping profiles that capture filename patterns, folder precedence, and marketplace selections. Provide default starter profiles per marketplace and the ability to import/export profiles for team sharing. Support rollback to prior versions and environment-scoped profiles (workspace, brand). Include conflict resolution and rule testing against sample files before applying to a full batch.

Acceptance Criteria
Create, Save, and Reuse Mapping Profile
Given I have defined filename patterns (regex or wildcard), folder precedence, and a marketplace selection When I click Save with a unique profile name within a chosen scope Then the profile is saved as version 1 with metadata (name, scope, marketplace, creator, timestamp) And it appears in the profile library and profile picker And applying the profile to a new batch restores all saved rules identically And invalid patterns are blocked with inline errors and Save is disabled until all errors are resolved And profile names must be 3–64 characters and unique within the selected scope
Profile Versioning and Rollback
Given an existing profile with version vN When I modify rules and save Then a new version vN+1 is created with a required change summary And I can view version history and a diff of rule changes between versions When I select Rollback to version vK and confirm Then vK becomes the active version without deleting newer versions And an audit log records actor, timestamp, and from→to versions
Default Starter Profiles per Marketplace
Given a workspace or brand with no user-created profiles When I open the profile library Then default read-only starter profiles are available for supported marketplaces (e.g., Amazon, eBay, Shopify) And each default can be cloned into an editable user profile And defaults include preconfigured role mappings (main, alt, swatch), typical filename patterns, and folder precedence And defaults carry a system-maintained version tag and cannot be edited in place
Import/Export Profiles for Team Sharing
Given I select one or more profiles When I export profiles Then a downloadable JSON file is generated containing rules, versions, scope, marketplace, and checksum And the export includes only permitted profiles for my access level When I import a profile file Then the system validates schema and checksum and shows a preview of profiles to be imported And if a name conflict exists in the target scope, I must choose: Keep both (auto-suffix), Replace existing, or Merge rules with preview And after import, the new/updated profiles appear in the library with preserved scopes and versions (or incremented if replaced)
Environment-Scoped Profiles (Workspace and Brand)
Given workspace-level and brand-level scopes are enabled When I save a profile Then I must select a scope (workspace or brand) And brand-scoped profiles are only visible within that brand; workspace-scoped profiles are visible across brands in the workspace And when both scopes contain a profile with the same name, the brand-scoped profile takes precedence in selection and execution And the library allows filtering and bulk actions by scope and brand
Rule Conflict Detection and Resolution
Given a profile contains rules that can target the same asset When I validate or attempt to run the profile Then overlapping patterns or duplicate role assignments are detected and listed as conflicts And a deterministic precedence applies: explicit rule priority > folder precedence > filename specificity > creation order And I can resolve by reordering rules, editing patterns, or accepting the proposed precedence with a confirmation And batch execution is blocked until conflicts are resolved or explicitly acknowledged
Rule Testing on Sample Files Before Full Batch
Given I have selected a profile and at least 20 sample files/folders When I run Test Profile Then the system displays a preview mapping for each sample (SKU, variant, image role, destination CSV columns) and summary counts (matched, unmatched, conflicts) And unmatched items are highlighted with reasons and suggested fixes And I can export the test report as CSV or JSON And the test completes within 10 seconds for up to 200 samples And Apply to Batch remains disabled until a test has zero critical errors

Smart Sampler

If you drop a whole folder, Smart Sampler selects a representative 10—covering main angles, backgrounds, and sizes—so detection and presets calibrate correctly. You get faster, more accurate first‑run results without manual curation.

Requirements

Representative Sampling Engine
"As a solo e‑commerce seller, I want Smart Sampler to automatically pick a representative 10 from my folder so that calibration and first‑run edits are accurate without manual curation."
Description

Implements an ML-driven selection pipeline that chooses a representative set of 10 images from a dropped folder, maximizing coverage across camera angles, background types, sizes/aspect ratios, and visual diversity. Extracts embeddings and metadata (EXIF, resolution, aspect) to cluster and score images, then selects medoids with constraints to ensure balanced coverage. Enforces deduplication via perceptual hashing and quality gates (sharpness, exposure) while remaining deterministic with a seed for reproducibility. Integrates with PixelLift’s processing graph so the selected set becomes the canonical input for first-run detection and preset calibration.

Acceptance Criteria
Balanced Coverage Selection of 10
Given a folder F with at least 50 images spanning at least 5 distinct angle clusters, at least 2 background types, and 3 aspect-ratio buckets (portrait < 0.9, square 0.9–1.1, landscape > 1.1) When the Representative Sampling Engine runs with default configuration and seed S Then it outputs exactly 10 unique image IDs And the selected set covers at least 5 distinct angle clusters And includes at least 2 distinct background types And includes all 3 aspect-ratio buckets And no more than 2 images from any single angle cluster are selected And each selected image’s shortest side is at least 512 pixels
Perceptual Deduplication and Quality-Based Tie-Break
Given a folder containing groups of near-duplicates where any pair in a group has 64-bit perceptual hash Hamming distance <= 6 And each image has a computed quality score Q in [0, 1] derived from sharpness and exposure metrics When the engine selects the representative set Then at most one image from each near-duplicate group appears in the selected 10 And within each group, the image with the highest Q is selected; ties are broken by higher resolution, then earlier file modified time And a de-duplication reason code is recorded for each excluded duplicate
Quality Gates for Sharpness and Exposure
Given a folder with mixed-quality images When the engine evaluates candidates Then no selected image has variance of Laplacian < 50 (blur threshold) And no selected image has normalized mean luminance < 0.1 or > 0.9 (under/overexposure thresholds after gamma correction) And no selected image has JPEG quality factor < 60 or WebP quality < 60 when available or reliably estimated And if at least 10 images meet all thresholds, the selected 10 all meet them
Deterministic Selection With Seed and Stable Ordering
Given the same input folder F and seed S When the engine is run three times on the same hardware and once on a different OS Then the selected 10 image IDs and their order are identical across all runs And given a different seed S2 != S, at least 3 of the 10 selected IDs differ from the set selected with S
Metadata and Embedding Extraction Completeness and Fallback
Given supported image formats (JPEG, PNG, WebP, HEIC) in folder F When the engine processes F Then for 100% of supported, non-corrupt files it extracts EXIF (when present), resolution, aspect ratio, and a vision embedding vector And files missing EXIF are still processed using pixel-derived metadata And any file for which embedding computation fails is excluded from selection and logged with reason code "embedding_error"
Processing Graph Integration as Canonical Input
Given the PixelLift processing graph is initialized When the sampling engine completes selection Then a node "sampling.selected" is created with exactly 10 image IDs And nodes "detection.first_run" and "calibration.presets" consume only from "sampling.selected" And non-selected images do not flow into these nodes until after calibration completes And the run record stores the selected IDs and seed used for reproducibility
Handling Folders With Insufficient Eligible Images
Given a folder that after deduplication and quality gates contains fewer than 10 eligible unique images When the engine runs Then it returns all eligible unique images (count < 10) without padding with ineligible images And it emits a warning with reason "insufficient_eligible_images" including counts: total, dedup_removed, quality_filtered, selected And the processing graph uses the selected subset as canonical input for first-run detection and preset calibration
Folder Ingestion & File Hygiene
"As a user, I want to drop a folder and have PixelLift safely scan and prep images so that the sampler works reliably even with duplicates or mixed formats."
Description

Provides robust folder import with drag‑and‑drop, recursive scanning, and support for common formats (JPEG, PNG, WebP, HEIC). Performs file validation, corruption checks, and perceptual de‑duplication, and extracts metadata needed for sampling (dimensions, EXIF orientation). Handles large folders asynchronously with progress feedback and temporary sandboxed storage under PixelLift’s data retention policies, ensuring the sampler receives a clean, consistent candidate set.

Acceptance Criteria
Drag-and-Drop Folder Import (Recursive & Supported Formats)
- Given a user drags and drops a top-level folder, When ingestion starts, Then the system recursively scans all subfolders and discovers image files with extensions .jpg/.jpeg, .png, .webp, .heic. - Given non-image or unsupported files are present, When scanning, Then they are skipped, not counted as candidates, and per-file reasons are captured. - Given scanning completes, When results are shown, Then the total candidate count equals the number of valid, supported images discovered. - Given platform differences in case sensitivity, When scanning, Then extension matching is case-insensitive. - Given identical path names across subfolders, When listing candidates, Then a deterministic ordering is applied (path + filename ascending).
File Integrity Validation & Skipping Corrupt Files
- Given a discovered image, When validation runs, Then the decoder opens the image and verifies non-zero dimensions; failure marks the file corrupt. - Given a corrupt or truncated image, When validation fails, Then the file is excluded from candidates and logged with reason=CORRUPT. - Given a valid image, When validation passes, Then it proceeds to metadata extraction with a recorded validation status=OK. - Given unreadable EXIF blocks but decodable pixel data, When validation runs, Then the file is not marked corrupt; the EXIF read error is logged separately.
Perceptual De-duplication and Canonical Selection
- Given two images whose perceptual hash Hamming distance <= 5, When deduplication runs, Then they are grouped as near-duplicates. - Given a duplicate group, When selecting canonical, Then the highest resolution image is retained; if tied, retain the largest file size; if still tied, retain the most recent modified timestamp; selection is deterministic. - Given byte-identical files (same checksum), When deduplication runs, Then only one is retained and others marked reason=DUPLICATE with reference to canonical file ID. - Given deduplication completes, When the summary is generated, Then the number of duplicates removed equals the sum of non-canonical items across groups.
Metadata Extraction and Orientation Normalization
- Given a valid image, When metadata extraction runs, Then width, height, format, color profile (if present), EXIF orientation (if present), and file path are captured. - Given EXIF Orientation indicates rotation/mirroring, When normalization runs, Then a normalized orientation is applied so downstream previews and dimensions reflect the displayed orientation. - Given EXIF metadata is missing or unreadable, When extraction runs, Then processing continues with defaults and no crash occurs; the file remains a candidate if decodable. - Given metadata is captured, When the candidate set is delivered, Then each item includes normalized width/height and orientation=top-left.
Asynchronous Processing with Progress & Cancellation
- Given a folder with 5,000 images, When ingestion runs, Then the UI remains responsive and displays progress with processed/total counts, percent complete, and ETA updated at least once per second. - Given ingestion is in progress, When the user clicks Cancel, Then processing stops within 3 seconds, temporary artifacts for unprocessed files are cleaned, and a partial summary is shown. - Given a file-level transient read error occurs, When retry policy applies, Then up to 2 retries with exponential backoff are attempted before marking the file as FAILED with reason code.
Sandboxed Temporary Storage & Retention Compliance
- Given ingestion starts, When files are staged, Then they are stored under a per-job, tenant-isolated sandbox path not accessible by other tenants or jobs. - Given a job completes successfully, When cleanup runs, Then all temporary files and derived artifacts are deleted within 60 seconds of completion. - Given 24 hours elapse after job creation, When retention policy runs, Then any remaining temporary artifacts are deleted automatically. - Given cleanup completes, When storage usage is checked, Then usage returns to within ±5% of pre-job baseline.
Ingestion Summary Report & Clean Candidate Delivery to Sampler
- Given ingestion finishes or is canceled, When the summary is shown, Then it includes counts for: total discovered, supported, unsupported skipped, corrupt skipped, duplicates removed, final candidates. - Given per-file issues occurred, When the user exports the log, Then a CSV is generated containing file path, action taken (ingested/skipped), and reason code (UNSUPPORTED/CORRUPT/DUPLICATE/FAILED). - Given final candidates are prepared, When handing off to Smart Sampler, Then only validated, deduplicated, and orientation-normalized images with metadata are passed in deterministic order.
Coverage Rules Configuration
"As a user, I want to tune how the sampler balances angles, backgrounds, and sizes so that the selected set aligns with my marketplace requirements."
Description

Adds a settings panel and API options to control sample size (default 10) and weighting for coverage dimensions such as angles, backgrounds, sizes/aspect ratios, and variants. Supports min/max constraints per dimension, include/exclude patterns, hero image preference, and similarity thresholds. Persists workspace defaults and preset-linked profiles so teams can align selection behavior with marketplace guidelines while retaining sane guardrails to prevent over‑fitting.

Acceptance Criteria
Adjustable Sample Size with Default Fallback
- Given no explicit configuration at workspace, preset, or API levels When Smart Sampler runs Then the sample size is 10 images - Given a user sets sampleSize = N in Coverage Rules and saves successfully When Smart Sampler runs without API override Then exactly N images are selected - Given an API request includes sampleSize = M When Smart Sampler runs Then exactly M images are selected and the API value overrides workspace and preset defaults for that run only - Given sampleSize < 1 or sampleSize > 100 When saving configuration or submitting an API request Then validation fails with HTTP 400/inline error and no changes are applied - Given sampleSize > total eligible images after filters When Smart Sampler runs Then all eligible images are returned and a warning notes the shortfall
Weighting and Min/Max Constraints per Coverage Dimension
- Given weights are set for angles, backgrounds, sizesAspectRatios, and variants summing to 100% When Smart Sampler runs Then the selected sample distribution matches each weight within ±1 image rounding tolerance - Given minCount and maxCount are configured per dimension value When Smart Sampler runs Then counts per value respect minCount and do not exceed maxCount - Given weights and min/max constraints conflict or are unsatisfiable for the configured sampleSize When Smart Sampler runs Then selection fails with HTTP 422/inline error listing the conflicting rules and no sample is produced - Given no weights are specified When Smart Sampler runs Then an even distribution across observed values is attempted subject to min/max constraints
Include/Exclude Patterns and Hero Image Preference
- Given includePatterns and excludePatterns are configured using glob or regex syntax When Smart Sampler runs Then files matching excludePatterns are removed from eligibility before other rules are applied - Given includePatterns select at least K images and K ≤ sampleSize When Smart Sampler runs Then at least K matching images are included unless explicitly excluded - Given one or more images are marked hero and not excluded When Smart Sampler runs Then at least one hero image is included in the sample and counted toward relevant dimension quotas - Given includePatterns and min/max constraints cannot be satisfied simultaneously When Smart Sampler runs Then the run fails with HTTP 422/inline error enumerating the unmet constraints
Similarity Thresholds and Diversity Guardrails
- Given similarityThreshold ∈ [0.0, 1.0] is configured When Smart Sampler runs Then any two images in the selected sample have similarity ≤ threshold per the system similarity metric - Given enforcing similarityThreshold would reduce the sample below sampleSize or violate any configured minCount Then the system relaxes the similarity constraint minimally to satisfy hard constraints and emits a warning in the response/UI - Given similarityThreshold is missing or null When Smart Sampler runs Then no similarity-based exclusions are applied
Persistence of Workspace Defaults and Preset-Linked Profiles
- Given a workspace default Coverage Rules profile is saved When a new preset is created Then it inherits the workspace default values - Given a preset has a linked Coverage Rules profile When that preset is used in a run Then the profile values are applied regardless of later changes to workspace defaults - Given a user updates a preset-linked profile and saves When Smart Sampler runs with that preset Then the latest saved profile values are used - Given a user discards changes in the settings panel When leaving the page Then no changes are persisted
API Parity, Validation, and Response Telemetry
- Given POST /coverage-rules/preview and POST /coverage-rules/apply accept the same schema as the UI When invoked with a valid payload Then the service returns 200 with the resolved configuration and target counts per dimension - Given an invalid payload (unknown fields, wrong types, out-of-range values) When submitted Then the service returns 400 with machine-readable error codes and field paths - Given a successful run When the response is returned Then it includes achieved vs target counts per dimension, applied overrides, and any warnings emitted during constraint resolution
Selection Preview & Control UI
"As a user, I want to preview and tweak the selected samples so that I have confidence before processing the full batch."
Description

Introduces a preview grid displaying the chosen 10 with coverage badges (angle, background, size), a coverage summary, and quick actions: approve all, regenerate with same/different seed, swap from alternates, lock/unlock items, and reorder. Provides keyboard shortcuts, accessible labels, and a difference view between runs. On approval, the selection is frozen and passed to calibration, improving user confidence and reducing miscalibration risk.

Acceptance Criteria
Preview Grid with Coverage Badges and Summary
Given Smart Sampler has produced a selection of 10 images When the Selection Preview & Control UI loads Then a grid displays exactly 10 image cards in the current selection order And each card shows persistent badges for Angle, Background, and Size derived from detection/metadata And focusing or hovering a badge reveals a tooltip with the full attribute label And a coverage summary shows counts and unique values aggregated for Angle, Background, and Size And the coverage summary values exactly match the aggregation of the 10 cards And items with missing attributes display "Unknown" and are counted under that label And initial render of grid and summary completes within 1000 ms on a baseline test device
Approve All Freezes Selection and Initiates Calibration Handoff
Given 10 images are visible in the preview grid and at least one quick action is available When the user activates Approve All Then the selection state is set to Frozen and all items are marked locked And regenerate, swap, and alternates actions are disabled until the selection is unfrozen And the system sends a Calibration payload containing: ordered image IDs, lock flags, run seed, and coverage snapshot And a confirmation toast appears stating the selection was sent to Calibration And the UI indicates the next step (e.g., progress state or navigation) without losing the frozen selection context
Regenerate with Same/Different Seed with Lock Respect
Given a current selection with a recorded run seed and some items optionally locked When the user triggers Regenerate (same seed) Then all unlocked items remain the same image IDs and order as the prior run And all locked items remain unchanged And the coverage summary remains identical to the prior run When the user triggers Regenerate (new seed) Then at least one unlocked item is replaced with a different image ID when alternates exist And all locked items remain unchanged And the displayed run seed updates to the new value And the coverage summary recalculates to reflect the new unlocked items And a non-blocking notice explains if no alternates were available and nothing changed
Swap from Alternates Updates Selection and Coverage
Given a card in the grid has alternates available When the user opens the alternates panel for that card Then a list of alternate thumbnails is shown with the current selection highlighted When the user chooses a different alternate Then the card updates to the chosen image ID without changing other cards And the card’s badges update to reflect the new image’s Angle, Background, and Size And the coverage summary recalculates and matches the new grid state within 500 ms And an Undo action is available to revert the last swap And if the card is locked, the swap action is blocked with an explanatory tooltip And if no alternates exist, a "No alternates available" message is shown and no changes occur
Reorder via Drag-and-Drop and Keyboard Persists
Given 10 images are shown in the grid When the user reorders items via drag-and-drop Then the grid updates to the new order and shows updated position indices And the new order is included in the Calibration payload on approval When the focused card is reordered using keyboard shortcuts Then the card moves to the intended position without requiring a pointer And locked status does not prevent reordering (it prevents replacement only) And the new order is announced to assistive technologies And reordering never duplicates or drops items; the set of 10 image IDs remains unchanged
Keyboard Shortcuts and Accessibility Compliance
Given the Selection Preview & Control UI is focused When the user presses keyboard shortcuts Then the following actions are performed: A=Approve All; G=Regenerate (same seed); Shift+G=Regenerate (new seed); L=Toggle lock on focused card; S=Open/close alternates for focused card; D=Toggle Difference View; Arrow keys=move focus; Ctrl/Cmd+Arrow=reorder focused card And every action available by mouse is operable by keyboard alone And focus order follows grid reading order and is preserved after dynamic updates And all actionable elements have visible focus indicators And screen readers announce for a focused card: index, filename/ID, and badges (Angle/Background/Size) And live regions announce completion of regenerate operations and approval confirmation And color contrast for text and badges is at least 4.5:1 And UI controls and images have accessible names/labels and roles
Difference View Between Runs Highlights Changes
Given at least two runs exist for the current folder When the user toggles Difference View Then cards that changed since the prior run are visually flagged and announced to assistive tech And a summary bar shows counts of Changed, Unchanged, Locked, and Unavailable items And selecting a changed card reveals a side-by-side mini-compare of Previous vs Current And Difference View is disabled for the first run with an explanatory message And turning off Difference View restores the normal grid without altering the underlying selection
Calibration Pipeline Integration
"As a user, I want the selected samples to automatically calibrate edits so that the rest of my images process accurately from the start."
Description

Connects Smart Sampler’s output to PixelLift’s detection and preset calibration stages. Generates a calibration profile from the selected images, caches it for the batch, versions the profile with metadata (embedding model, rules used), and applies it to the remaining images. Supports rollback, re‑calibration on reselection, and emits metrics (precision, background removal confidence) to verify improved first‑run quality.

Acceptance Criteria
Batch Calibration Profile Generation and Caching
Given Smart Sampler has produced 10 selected images for a new batch (batchId) When the calibration stage starts Then a calibration profile is generated from the 10 images within 60 seconds for batches up to 2,000 images And the profile is stored and cached keyed by batchId with status "ready" And the profileId is returned to the pipeline
Versioned Calibration Metadata
Given a calibration profile is created for a batch Then the profile metadata includes versionId, createdAt (ISO 8601), embeddingModel.name, embeddingModel.hash, ruleset.name, ruleset.hash, sampleImageIds, calibrationChecksum And the metadata is retrievable and immutable for that version Given a subsequent calibration is performed for the same batch When recalibration completes Then versionId increments by 1 and all prior versions remain queryable by versionId
Applying Calibration to Remaining Images in Batch
Given a calibration profile with profileId is ready for batchId When processing non-sample images in the batch Then 100% of non-sample images use detection and preset parameters from profileId And each processed image record stores profileId And additional latency due to profile retrieval is <= 100 ms at p95
Re-calibration on Reselection
Given the user updates the selected images for the batch When recalibration is requested Then a new calibration profile version is generated within 60 seconds And all pending images switch to the new version for subsequent processing And already processed images are marked eligible-for-reprocess and can be re-queued in a single action And a version history entry is recorded with reason "reselection"
Rollback to Prior Calibration Version
Given multiple calibration profile versions exist for a batch When a rollback to a prior version is triggered Then pending and queued images begin using the target version within 10 seconds And no more than one in-flight image is completed with the superseded version And a rollback event is recorded with fromVersion, toVersion, actor, timestamp And the previous latest version remains available for future selection
Calibration Quality Metrics Emission
Given a calibration profile has been applied to the batch When first-run processing completes for the first 50 non-sample images or the entire batch if fewer than 50 Then metrics are emitted and stored with profileId and versionId: detection precision at IoU 0.5, background removal confidence mean and p90, false-positive rate, calibration coverage percentage And metrics are visible via API and dashboard within 30 seconds of computation And the batch is flagged "attention" if background removal confidence mean < 0.85 or detection precision < 0.90
Performance, Determinism, and Auditability
"As a user, I want fast, consistent sampling with a clear rationale so that I can trust and reproduce results across runs."
Description

Sets clear SLAs and engineering controls: sample selection for 1k images completes under 10 seconds on a standard tier; streaming feature extraction and bounded-memory clustering support up to 20k images. Ensures deterministic results via seedable selection, with an exportable JSON report that explains inclusion rationale per image and the coverage achieved. Adds health checks, fallbacks to heuristic mode on embedding service degradation, and telemetry for latency and selection stability.

Acceptance Criteria
1k Batch SLA on Standard Tier
Given a Standard Tier environment and a folder with exactly 1,000 supported images When Smart Sampler is invoked Then the selection job completes within 10 seconds wall-clock And the output contains exactly 10 selected image IDs with associated metadata And no retry or fallback mode is triggered during the run And the job status is success and latency is emitted to telemetry
20k Streaming Scalability and Bounded Memory
Given a folder with 20,000 supported images and the memory cap configured to 2 GiB When Smart Sampler runs with streaming feature extraction and bounded-memory clustering Then the job completes without out-of-memory or crash And peak resident memory remains below the configured cap throughout the run And exactly 10 representative images are selected and returned
Deterministic Seeded Selection
Given a fixed input set of 5,000 images with fixed order, configuration constant, and seed=42 When Smart Sampler is executed twice Then the selected image IDs, their order, and selection scores are identical across both runs And the audit report fields selection_ids and coverage are identical Given the same inputs but seed=43 When Smart Sampler is executed Then at least one selected image or its order differs from the seed=42 run And coverage metrics for angles, backgrounds, and sizes remain within ±2 percentage points of the seed=42 run
JSON Audit Report and Coverage Metrics
Given a completed Smart Sampler run When the JSON audit report is exported Then the file validates against schema version "sampler_audit_v1" And it contains per selected image: image_id, cluster_id, selection_reason, similarity_score, tags {angle, background, size} And it contains run-level fields: run_id, seed, algorithm_version, mode (ml|heuristic), input_count, selected_ids[10], coverage {angles, backgrounds, sizes}, started_at, completed_at, duration_ms, degraded (boolean) And the report is downloadable via UI and retrievable via API within 5 seconds of job completion
Health Checks and Degradation Detection
Given the embedding service is monitored with configured thresholds (error_rate > 5% over 30s or p95_latency > 2x baseline) When a Smart Sampler run starts Then a pre-run health check executes and its result is logged And if thresholds are exceeded during the run, a degradation event is recorded within 2 seconds and surfaced in the job log and telemetry And the run does not surface unhandled errors to the user
Auto Fallback to Heuristic Mode
Given an embedding service degradation is detected or requests time out per threshold When Smart Sampler is running on a 1,000-image batch Then it switches to heuristic mode within 1 second of detection And the run completes successfully with exactly 10 selected images And the audit report flags mode=heuristic and includes fallback_reason And total runtime remains ≤ 10 seconds
Telemetry for Latency and Selection Stability
Given telemetry collection is enabled When 30 Smart Sampler jobs run, including at least 5 repeated runs on the same dataset and seed Then latency percentiles (p50, p95) and selection stability metrics are recorded and exportable via the telemetry API And repeated runs with the same dataset and seed report stability = 1.0 And telemetry includes job_count, error_rate, fallback_rate, and average coverage for angles, backgrounds, and sizes over 24h
Edge-case Handling & Fallbacks
"As a user, I want the sampler to handle messy or edge‑case folders gracefully so that I’m not blocked and still get meaningful results."
Description

Gracefully manages atypical inputs: folders with fewer than 10 images select all; extreme homogeneity relaxes coverage constraints to prioritize quality and slight diversity; mixed‑SKU detection suggests optional auto‑segmenting by product; low‑resolution or corrupted files are flagged and excluded with user notification. Provides clear warnings and safe defaults to avoid blocking the workflow while keeping calibration meaningful.

Acceptance Criteria
Folders With Fewer Than 10 Images
Given a dropped folder contains N valid images where 0 < N < 10 and each image is decodable and >= 800x800 px When Smart Sampler runs Then it selects all N images And then shows an info banner indicating all N images were selected due to small set And then proceeds to calibration without prompts And then the API result includes selected_count = N and reason = "SMALL_SET"
Relaxing Coverage Under Extreme Homogeneity
Given a folder with at least 10 valid images and a near-duplicate ratio >= 0.8 based on perceptual hash similarity (Hamming distance <= 5) When Smart Sampler runs Then it relaxes angle/background coverage constraints And then selects up to 10 images prioritizing quality metrics (sharpness, exposure, resolution) and slight diversity if present And then displays a non-blocking warning that coverage constraints were relaxed due to homogeneity And then the API metadata includes relaxed_coverage = true and homogeneity_ratio >= 0.8
Mixed-SKU Detection Suggests Segmentation
Given unsupervised product embedding clustering yields K >= 2 clusters with silhouette score >= 0.5 and each cluster has size >= 3 When Smart Sampler runs Then it shows a non-blocking prompt recommending auto-segmentation by product with per-cluster counts And then if the user accepts, the system splits into K sub-batches and samples up to 10 per cluster And then if the user declines/dismisses, the system continues sampling across the combined set And then the user choice is logged and exposed in batch metadata without blocking the workflow
Excluding Low-Resolution and Corrupted Files
Given a folder contains some low-resolution images (min dimension < 800 px) and/or corrupted images (decode failure) When Smart Sampler runs Then it excludes those files from the candidate pool and does not count them toward the selection of up to 10 And then it surfaces a warning listing counts by reason (LOW_RES, CORRUPTED) And then the API returns per-file statuses with reason in {LOW_RES, CORRUPTED} And then remaining valid images proceed to sampling and calibration
Non-Blocking Warnings and Safe Defaults
Given edge-case conditions are triggered (e.g., homogeneity relaxation, mixed-SKU suggestion, low-res/corrupted exclusions) When Smart Sampler proceeds Then the workflow continues without a hard block and returns min(10, valid_count) selected images And then if valid_count == 0, it halts sampling gracefully with an actionable error and guidance to re-upload, without app crash And then if 1 <= valid_count <= 2, it runs calibration using a Safe Default preset and marks calibration_mode = "SAFE_DEFAULT" and confidence = "low" And then all warnings persist in the notification center and batch summary
Auditability and Machine-Readable Outcomes
Given any edge-case rule was applied during sampling When the batch completes Then a structured diagnostics record is saved containing: rules_triggered, thresholds_used, counts_by_reason, valid_count, selected_count, and user_decisions And then batch details in the API include a diagnostics object and per-file statuses And then the UI batch summary surfaces the same diagnostics for verification

Product Ideas

Innovative concepts that could enhance this product's value proposition.

RuleLock Presets

Auto-validate images against Amazon/eBay rules and fix issues in batch—backgrounds, margins, DPI. Prevent rejections, with per‑marketplace checklists and red‑flag previews.

Idea

TurboSnap Mobile

Shoot, auto-clean, and export marketplace crops from your phone in seconds. Offline queues sync later; one-tap vertical and square sets for TikTok Shop and Etsy.

Idea

Split-Test Bundles

Generate labeled A/B/C image sets with matching CSVs and folder names for instant experiments. Exports include traffic-split counts and change logs for clean tracking.

Idea

SceneSwap Studio

Swap bland backdrops with photoreal scenes and true shadows tuned to item lighting. One slider controls reflection strength; keep a 'natural' variant for craft/vintage sellers.

Idea

CreditSafe Billing

Prepaid credits, spend caps, and auto-topup rules per workspace. Client-billable invoices and alerts prevent surprise overages while keeping batch jobs flowing.

Idea

Workspace Roles & Locks

Role-based access, client workspaces, and approval gates. Lock presets, require reviewer sign-off, and capture audit trails for agencies and regulated sellers.

Idea

Onboard Autopilot

Drag in 10 images; we detect marketplace, suggest presets, and produce your first compliant export in five minutes. Interactive checklist teaches core flows as you ship.

Idea

Press Coverage

Imagined press coverage for this groundbreaking product concept.

P

PixelLift Launches to Automate Marketplace‑Ready Product Photos, Cutting Edit Time 80% for Solo Sellers and Teams

Imagined Press Article

San Francisco, CA — September 26, 2025 — PixelLift, the automated photo‑enhancement SaaS for e‑commerce, today announced general availability of its platform that batch‑optimizes product images for marketplaces in minutes. Built for solo sellers and lean teams juggling dozens or thousands of listings, PixelLift removes backgrounds, applies pro‑grade edits, and generates A/B‑ready variations for bulk export—cutting editing time by up to 80%, shrinking per‑image costs to cents, and lifting click‑through rates (CTR) by 12–25%. PixelLift’s launch brings enterprise‑level speed and consistency to everyday sellers on Amazon, eBay, Etsy, TikTok Shop, Shopify, and more. Instead of manual retouching or stitching together point tools, users define a rules‑driven pipeline once and run it at scale across SKUs, categories, and marketplaces. “At a time when marketplace rules change weekly and attention spans shrink by the hour, sellers need a reliable way to ship compliant, conversion‑ready images without becoming Photoshop experts,” said Ava Nguyen, CEO and Co‑founder of PixelLift. “We built PixelLift so a solo operator can process a catalog like a seasoned studio—fast, accurate, and with clear control at every step.” At the core of PixelLift is the Auto‑Fix Pipeline, a customizable sequence of background enforcement, subject centering, margin correction, DPI/size normalization, and color profile sync. Each run produces visual before/after diffs, step‑by‑step logs, and one‑click rollback, giving speed without sacrificing oversight. Category IQ automatically detects or ingests product category and applies category‑specific image rules—so apparel, jewelry, and collectibles each get the exact margins, backgrounds, angles, and size constraints required. To maximize product fill and preserve natural composition, Smart Margins uses AI subject detection to set compliant, composition‑aware margins per marketplace and aspect ratio. Text Guard scans for disallowed overlays, watermarks, badges, or promo stickers and can auto‑remove them to prevent instant rejections on strict channels. Compliance Gate provides a hard stop that blocks export until all RuleLock checks pass, with a clear fail list, guided fixes, and a bulk “Fix All” option to keep teams moving. “PixelLift eliminates rework loops that burn time and morale,” said Jordan Patel, Head of Product at PixelLift. “With Policy Pulse monitoring rule changes and simulating ‘what‑if’ updates, sellers can update presets with confidence, avoid surprise rejections, and stay focused on growth, not policy debugging.” Early users report faster launches and cleaner workflows. “We went from three days of back‑and‑forth edits to same‑day approvals,” said Casey Lin, Catalog Manager at a multi‑marketplace home goods seller. “Category IQ got our margins and sizes right on the first pass, and Compliance Gate caught text we didn’t even realize would trigger a rejection.” For conversion‑focused teams, PixelLift generates A/B‑ready image sets on demand. QuickSets builds vertical, square, and main‑image variants per channel with consistent naming and folders. Auto Split labels files and CSVs with precise traffic weights, while Sample Guard and Results Sync guide test design and pull downstream metrics into clean comparisons. When a winner emerges, Winner Auto‑Promote prepares the winning package and pushes it live where integrations allow, with a one‑click rollback for safety. PixelLift also accelerates on‑ramp time for new users. Preset Genie builds a compliant, marketplace‑ready preset from the first 10 images, explaining each choice in plain language for quick trust and tweaks. SafeRun Sandbox lets teams dry‑run pipelines on sample images to preview edits and compliance checks, including clear credit estimates before committing. CSV AutoMap interprets filenames and folder patterns to generate the right marketplace CSV templates automatically, eliminating early spreadsheet pain. Sellers who care about authentic, pro‑grade visuals benefit from PixelLift’s realism stack. LightLock Match automatically matches scene lighting to the product photo for native‑feeling shadows and highlights; Perspective Anchor aligns ground planes and vanishing points for believable compositions; TrueScale ensures props and shadow lengths stay realistic; and EdgeBlend Pro refines hair, glass, and translucent edges for crisp results that withstand zoom on Amazon and Shopify. PixelLift is purpose‑built for the real operating constraints of e‑commerce. Role Blueprints and Preset Lockbox keep teams on a least‑privilege, version‑controlled footing; Approval Matrix routes high‑risk changes through the right sign‑offs; and Audit Ledger records a tamper‑evident history of who changed what, when, and why. Agencies can keep client data air‑gapped with Workspace Walls while enabling scoped collaboration through Review Queue and Safe Share links. “Whether you’re a Solo Store Sprinter racing to list new finds or a Multi‑Marketplace Operator enforcing zero rejects across regions, PixelLift turns image production into a measurable, controlled pipeline,” added Nguyen. “Our mission is to let small teams ship like big ones.” Availability and getting started: PixelLift is available today globally. New users can authenticate marketplaces via Draft Connect and push their first export as safe drafts/unlisted items for a fully reversible “hello world.” Pricing uses prepaid credits with controls that keep per‑image costs to cents at scale. A guided Checklist Coach and ReadyScore meter help most users reach a compliant first export in about five minutes. About PixelLift PixelLift is an automated photo‑enhancement SaaS that batch‑optimizes product images for marketplaces. The platform helps e‑commerce sellers remove backgrounds, apply professional edits, and generate A/B‑ready variations for bulk export—cutting editing time up to 80%, shrinking per‑image costs to cents, and lifting CTR 12–25%. Media contact press@pixellift.com +1‑415‑555‑0137 www.pixellift.com/press

P

PixelLift Debuts Mobile Capture‑to‑Publish Suite for TikTok Shop, Amazon, and Etsy with Live Compliance Overlays

Imagined Press Article

San Francisco, CA — September 26, 2025 — PixelLift today introduced a mobile capture‑to‑publish suite that lets sellers shoot, clean, and export marketplace‑ready images straight from their phones—no desktop handoff required. The new mobile workflow combines Live Clean background removal, SafeZone Overlay compliance guides, SKU Scan organization, Angle Coach capture prompts, Smart Sync offline resilience, Direct Push publishing, and QuickSets one‑tap variant packs to keep listings flowing from warehouse aisle to storefront in minutes. “Mobile is the new studio,” said Ava Nguyen, CEO and Co‑founder of PixelLift. “Sellers are sourcing, staging, and listing on the move. Our mobile suite lets them compose once, see compliance in real time, and publish to TikTok Shop, Amazon, eBay, and Etsy in a single tap. That’s less rework, fewer reshoots, and faster time to live.” Live Clean shows background removal and key fixes as the shot is framed, so creators can compose for pure‑white or brand‑safe backgrounds and avoid post‑production surprises. SafeZone Overlay adds marketplace‑specific grids, aspect ratios, and margin rings with a live fill score, helping users maximize product size while staying compliant with rules that differ across Amazon, Etsy, and newer social marketplaces like TikTok Shop. To tame on‑the‑go chaos, SKU Scan ties each capture to the right preset and naming convention by scanning a barcode/QR or entering a SKU. Files, folders, and export tags are auto‑applied, keeping batches organized, traceable, and ready for push. Angle Coach brings category‑tuned AR outlines and distance prompts—think apparel front/side/back, shoes at 45°, and jewelry macro—so every hero, detail, and lifestyle image is consistent and conversion‑ready. “Mobile sellers shouldn’t lose quality just because they’re fast,” said Jordan Patel, Head of Product at PixelLift. “Our Angle Coach and SafeZone Overlay features borrow from studio best practices, but they run in your pocket. You get consistent angles, compliant crops, and crisp subject isolation without sacrificing momentum.” When connectivity is spotty, Smart Sync keeps progress intact with a resilient offline queue. Users can auto‑retry on Wi‑Fi, respect cellular data caps, pause/resume, and safely reprocess conflicts—ideal for sellers moving between storage units, showrooms, and events. Once images are ready, Direct Push publishes finished sets from the phone to Amazon, eBay, Etsy, TikTok Shop, or cloud storage in one tap, with export logs for easy tracking. QuickSets generates vertical, square, and main‑image variants per channel with consistent naming and foldering. For social commerce, one‑tap vertical sets suit TikTok Shop’s feed and PDP needs, while square and 4:5 variants cover Instagram and Etsy. Paired with Smart Margins and Text Guard running server‑side, sellers can have confidence that mobile exports are both visually optimized and policy‑safe. Early adopters highlight speed gains and lower rejection rates. “We list dozens of items right from the loading dock,” said Max Hernandez, an on‑the‑go reseller who manages daily SKU additions. “Live Clean shows me exactly how the main image will look on Amazon, and SKU Scan keeps our batches labeled correctly. The days of reshooting because of a missed margin are over.” For teams, Role Blueprints and Workspace Walls extend to mobile, keeping client assets, presets, and logs air‑gapped by default and access‑controlled by least privilege. Reviewers can annotate images and clear policy flags from a central Review Queue, tied directly to Compliance Gate and Text Guard outcomes, accelerating approvals without email ping‑pong. Mobile onboarding is streamlined, too. New users can authenticate marketplaces in one step via Draft Connect and push their first export as safe drafts/unlisted items. Preset Genie auto‑builds a starter preset from ten sample images captured on the phone, explaining the rationale behind background, margins, and file types. The ReadyScore meter shows progress in real time, while SafeRun Sandbox offers a dry‑run to preview edits and compliance checks before consuming credits. “From TikTok Shop sellers measuring watch time and clicks to Etsy artisans who want non‑destructive, natural‑shadow results, our mobile suite respects each audience’s needs,” added Nguyen. “We’re bringing pro‑grade guardrails to the places where listings actually begin—phones, not studios.” Availability: The PixelLift mobile suite is available today on iOS and Android in supported regions. Features like Live Clean, SafeZone Overlay, Angle Coach, and SKU Scan are included with standard PixelLift credits, and Direct Push supports channel‑specific draft publishing where integrations allow. About PixelLift PixelLift is an automated photo‑enhancement SaaS that batch‑optimizes product images for marketplaces. The platform helps sellers remove backgrounds, apply professional edits, and generate A/B‑ready variations for bulk export—cutting editing time up to 80%, reducing per‑image costs to cents, and lifting CTR 12–25%. Media contact press@pixellift.com +1‑415‑555‑0137 www.pixellift.com/press

P

PixelLift Unveils Built‑In Image A/B Testing Stack with Auto‑Promote to Boost CTR 12–25% Across Marketplaces

Imagined Press Article

San Francisco, CA — September 26, 2025 — PixelLift today announced the general release of its conversion optimization stack for product imagery, giving sellers an end‑to‑end way to plan, run, and act on A/B and multivariate image tests across Amazon, eBay, Etsy, TikTok Shop, and D2C storefronts. The new capabilities—Auto Split, Winner Auto‑Promote, Sample Guard, Results Sync, Variant Tags, Clean Compare, and CSV AutoMap—eliminate spreadsheet wrangling and manual changeovers so teams can lift click‑through rates (CTR) by 12–25% with repeatable, low‑friction experiments. “Main images drive discovery and conversion, but most sellers still A/B test by guesswork,” said Ava Nguyen, CEO and Co‑founder of PixelLift. “We designed a stack that makes testing scientific and fast: generate variants in a click, label and route them cleanly, track performance without drama, and auto‑promote winners when the data clears a threshold.” Auto Split lets users set precise traffic weights—such as 50/30/20 for A/B/C—and auto‑labels files, folders, and CSVs to match, with built‑in guardrails that prevent misconfigured splits. Sample Guard offers instant sample‑size and duration estimates per SKU based on recent traffic and chosen metrics, while live progress bars flag underpowered tests and recommend extensions or split tweaks. To keep results clean and comparable, Variant Tags attach structured descriptors to each image—angle, crop tightness, background, shadow style—and roll them up across tests and SKUs. Results Sync pulls performance data from supported marketplaces and ad dashboards or ingests CSVs, auto‑mapping metrics to variants via labels. Teams can view apples‑to‑apples comparisons without manual VLOOKUPs, and experiment logs stay portable and auditable. “PixelLift turns image testing into a productized workflow,” said Jordan Patel, Head of Product at PixelLift. “Clean Compare gives side‑by‑side reviews with instant zoom, flicker toggles, and overlays of impressions, CTR, and conversions. When a winner emerges, Winner Auto‑Promote packages it for the relevant channel and, where integrations allow, pushes it live automatically with a one‑click rollback.” The testing stack integrates tightly with PixelLift’s production features. QuickSets generates marketplace‑ready variants—vertical, square, and main image formats—per channel with consistent naming and folders. Smart Margins and Category IQ ensure each variant remains compliant, while Text Guard and SceneSafe Filter catch disallowed overlays and props before export. Compliance Gate blocks publishing until RuleLock checks pass, protecting test integrity by preventing silent rejections. Early users credit the system with faster learning cycles and durable gains. “We had a hunch that tighter crops would perform better in Home & Kitchen,” said Bailey Rivera, a D2C brand lead who manages cross‑channel campaigns. “Variant Tags let us quantify that pattern across eight SKUs, and Winner Auto‑Promote rolled the winning look out to the whole category in two clicks.” The stack respects governance and audit needs for larger organizations and agencies. Preset Lockbox ensures only approved, versioned presets can be used to generate test variants, and Approval Matrix routes high‑impact changes—like Amazon main image updates—through the required sign‑off steps. Audit Ledger captures who changed what, when, and why, including test design parameters, allowing clean reporting for clients and compliance reviews. Getting started is straightforward. CSV AutoMap parses existing filenames and folder structures to pre‑fill marketplace CSV templates; SafeRun Sandbox lets teams dry‑run their first tests on sample images to preview edits, ensure compliance, and estimate credits; and ReadyScore shows exactly how close a user is to a compliant first export, with one‑click fixes to close gaps. For catalog managers overseeing thousands of SKUs, Review Queue centralizes approvals and ties directly to Compliance Gate and Text Guard flags, consolidating what used to be sprawling email threads. “Testing should never be an afterthought,” added Nguyen. “With PixelLift, it becomes the default mode: shoot a set, label intelligently, let the math run, and promote winners safely. Sellers can learn what truly moves CTR and conversion for their category in weeks, not quarters.” Availability: The PixelLift optimization stack is available today to all customers. Auto‑Promote and Results Sync support direct push and data pulls on a growing list of channels; users can also export labeled files and CSVs to run experiments with their existing ad and marketplace workflows. About PixelLift PixelLift is an automated photo‑enhancement SaaS that batch‑optimizes product images for marketplaces. The platform helps e‑commerce sellers remove backgrounds, apply professional edits, and generate A/B‑ready variations for bulk export—cutting editing time up to 80%, shrinking per‑image costs to cents, and lifting CTR 12–25%. Media contact press@pixellift.com +1‑415‑555‑0137 www.pixellift.com/press

P

PixelLift Introduces Enterprise‑Grade Compliance and Governance for Zero‑Reject Listings at Scale

Imagined Press Article

San Francisco, CA — September 26, 2025 — PixelLift today launched a comprehensive compliance and governance suite designed to keep listings policy‑safe while preserving speed for multi‑marketplace operators, agencies, and regulated sellers. The new capabilities—Policy Pulse, Category IQ, Compliance Gate, SceneSafe Filter, Role Blueprints, Preset Lockbox, Approval Matrix, Audit Ledger, Workspace Walls, Review Queue, and Freeze Windows—help teams ship fast with confidence and maintain zero‑reject standards even as rules evolve. “Policy compliance has become a moving target across Amazon, Etsy, and social marketplaces,” said Ava Nguyen, CEO and Co‑founder of PixelLift. “Sellers deserve a system that watches the rules for them, flags what changed, and offers a one‑click path to stay compliant—without grinding production to a halt. That’s what our governance stack delivers.” Policy Pulse continuously monitors marketplace rule updates and automatically suggests preset adjustments. It flags what changed, shows which presets and SKUs are impacted, and offers a one‑click update or sandbox ‘what‑if’ run to simulate failures before they happen—preventing surprise rejections and rework. Category IQ merges marketplace and category nuances into every run, so batches receive the correct margins, backgrounds, angles, and size constraints without manual checklists. Compliance Gate serves as a protective barrier that blocks export until all RuleLock checks pass. The system presents a clear, actionable fail list with guided fixes and a bulk “Fix All” option. SceneSafe Filter performs pre‑flight scene checks, flagging disallowed elements for main images—people, logos, or text props—while preserving lifestyle flexibility for secondary images. “Governance is about speed with safeguards,” said Jordan Patel, Head of Product at PixelLift. “Role Blueprints bring least‑privilege access in minutes; Preset Lockbox puts critical presets behind versioned approvals; and Approval Matrix routes work through the right sign‑off path automatically. Together, they enable fast, consistent outputs without permission sprawl.” For agencies and multi‑brand teams, Workspace Walls keep client data air‑gapped by default, with scoped Safe Share links for time‑boxed collaboration. Review Queue centralizes approvals with checklist‑driven tasks, annotations, and bulk‑approval when checks pass. Freeze Windows allow admins to schedule change freezes around launches or audits—blocking risky exports or preset edits unless a break‑glass approval with justification is provided. Audit Ledger records a tamper‑evident timeline of who changed what, when, and why—roles granted, presets edited, batches run, exports pushed, and overrides approved. It captures before/after diffs, attached justifications, and policy context at the time of change, making compliance reviews and client reporting straightforward. The suite integrates natively with PixelLift’s production features. Auto‑Fix Pipeline enforces background, centering, and DPI/size normalization in a rules‑driven sequence; Smart Margins maximizes product fill while respecting safe zones; Text Guard uses OCR and watermark detection to catch disallowed text and stickers; and SafeRun Sandbox provides dry‑runs for high‑stake batches with clear pass/fail summaries and credit estimates. Early enterprise users report fewer rejections and faster approvals. “We operate on Amazon, eBay, and a regional marketplace with distinct image policies,” said Priya Kapoor, Senior Operations Lead at a global electronics distributor. “Policy Pulse told us exactly which presets were affected by a background update, and the one‑click update kept our queues moving. Audit Ledger gave us the evidence we needed during a quarterly review.” PixelLift’s governance tools are designed for all seller profiles, from Compliance‑First Amazonians who prioritize policy acceptance, to Agency Production Desks that manage multiple client catalogs, to Craft & Vintage Curators who need subtle, non‑destructive edits without losing a natural look. By combining role‑based controls, preset versioning, review flow, and immutable audit trails, teams can scale output without sacrificing brand or compliance. Availability: The compliance and governance suite is available today to all PixelLift customers. Administrators can enable Role Blueprints, Preset Lockbox, and Approval Matrix from workspace settings, and connect Policy Pulse to their target marketplaces to receive real‑time rule updates and impact assessments. About PixelLift PixelLift is an automated photo‑enhancement SaaS that batch‑optimizes product images for marketplaces. The platform helps e‑commerce sellers remove backgrounds, apply professional edits, and generate A/B‑ready variations for bulk export—cutting editing time up to 80%, shrinking per‑image costs to cents, and lifting CTR 12–25%. Media contact press@pixellift.com +1‑415‑555‑0137 www.pixellift.com/press

P

PixelLift Rolls Out CreditSafe Billing and Spend Controls to Keep Image Pipelines Moving Without Surprise Costs

Imagined Press Article

San Francisco, CA — September 26, 2025 — PixelLift today announced CreditSafe Billing, a comprehensive set of spend controls and client billing tools that make it easy for agencies, brands, and multi‑team organizations to keep batch jobs flowing without overspending or surprise invoices. The release includes CapGuard spend caps, Smart Top‑Ups, Client Buckets, Overrun Shield, Invoice Split, and Forecast Alerts—features designed to bring transparency, predictability, and automation to high‑volume image production. “Credits shouldn’t be a stressor,” said Ava Nguyen, CEO and Co‑founder of PixelLift. “With CreditSafe Billing, teams can set the rules once—how much to spend, when to refill, how to allocate—and PixelLift makes sure work continues within those guardrails. Finance gets clarity, production gets continuity, and clients get clean, itemized billing.” CapGuard introduces precise spend caps per workspace, client, or user with soft warnings and hard stops. Teams can choose daily, weekly, or monthly resets and route over‑cap requests for approval. Smart Top‑Ups automate credit refills with rule‑based triggers—such as a balance threshold, time window, or forecasted depletion—and allow priority selection of payment methods, approvals above set amounts, and one‑tap pause/resume. For agencies and multi‑brand operations, Client Buckets allocate prepaid credits to clients, brands, or projects and lock queues/presets to the right bucket. Burn rate tracking, expiry dates, PO numbers, and exportable usage summaries make pass‑through billing effortless and accurate. Overrun Shield protects in‑flight batches from stalling when a cap is hit, with options to finish with a small grace allowance, auto‑split the batch at the cap, or pause and notify the approver—keeping SLAs intact and avoiding half‑processed deliverables. “Finance teams no longer need to babysit credits or chase usage reports,” said Neha Soni, Director of Operations at PixelLift. “Invoice Split creates itemized, client‑billable invoices from one shared pool, supporting tax IDs, currencies, and memo fields, and can auto‑send on schedule. Forecast Alerts provide predictive visibility into days‑to‑empty and projected overages based on recent usage velocity, with smart recommendations for top‑up sizes.” The billing suite integrates with PixelLift’s production and governance layers. Approval Matrix can require finance or senior approver sign‑off when thresholded top‑ups or cap changes are requested. Audit Ledger records all credit movements, cap adjustments, approvals, and invoices in a tamper‑evident history. Role Blueprints ensure only the right people can change billing settings, and Workspace Walls keep client data—including credit buckets and invoices—air‑gapped and secure. For sellers who need to move fast, CreditSafe Billing pairs with onboarding accelerators like Preset Genie, Draft Connect, Checklist Coach, and ReadyScore—making it easier to reach a compliant first export without worrying about mid‑run credit surprises. SafeRun Sandbox provides credit estimates for sample runs before committing a large batch, giving teams cost certainty ahead of go‑live. Early adopters cite fewer delays and cleaner accounting. “We manage nine client catalogs with weekly launches,” said Whitney Moore, a B2B catalog coordinator who oversees distributor uploads. “Client Buckets and Invoice Split turned our month‑end from a two‑day scramble into a one‑hour export. Overrun Shield saved us twice when a cap hit during a rush job—we finished on time without blowing the budget.” CreditSafe Billing supports a range of user profiles: Solo Store Sprinters who want simple auto‑top‑ups; Agency Production Desks that need PO‑tied allocations and pass‑through invoices; and Multi‑Marketplace Operators that benefit from Forecast Alerts and role‑based approvals when usage spikes ahead of a seasonal push. Availability: CreditSafe Billing is available today to all PixelLift customers. CapGuard, Smart Top‑Ups, Client Buckets, Overrun Shield, Invoice Split, and Forecast Alerts can be enabled from the Billing & Credits panel. Admins can configure approval thresholds and notifications to match existing finance policies. About PixelLift PixelLift is an automated photo‑enhancement SaaS that batch‑optimizes product images for marketplaces. The platform helps e‑commerce sellers remove backgrounds, apply professional edits, and generate A/B‑ready variations for bulk export—cutting editing time up to 80%, shrinking per‑image costs to cents, and lifting CTR 12–25%. Media contact press@pixellift.com +1‑415‑555‑0137 www.pixellift.com/press

Want More Amazing Product Ideas?

Subscribe to receive a fresh, AI-generated product idea in your inbox every day. It's completely free, and you might just discover your next big thing!

Product team collaborating

Transform ideas into products

Full.CX effortlessly brings product visions to life.

This product was entirely generated using our AI and advanced algorithms. When you upgrade, you'll gain access to detailed product requirements, user personas, and feature specifications just like what you see below.