Commit Graph
47 Commits
Author SHA1 Message Date
Ajay SableandClaude Opus 4.8 fbc9796de0 feat(tna): P2 — recommend by value-add × fabric using synced Stitch Category templates
Replaces the buyer-name fuzzy match with Sharavana's real model. The plan is chosen
by value-add category × fabric type; lead time is deterministic from the grid; the
engine selects the real synced Stitch template ("Category N - X Days") and back-
schedules its authoritative milestones (no hardcoding — Sharavana maintains them in
Stitch, we re-sync). services/tna_catalog.py holds only what's not in Stitch: the
(category × fabric) → tier grid + the WFX→category/fabric pre-fill guessers.

- recommend(): select "Category {N} - {gridLead} Days" mirror template, schedule_backward
  its items; encoded catalog kept only as a fallback if a template hasn't synced.
- _resolve_order_context: returns garment/category/fabric pre-fill guesses from WfxOrder.
- POST /tna/draft: takes category/fabric_type/garment (defaulting to the guesses);
  push builds from the selected mirror template. TnaRecommendation gains
  category/fabric_type/garment (self-healed).
- Frontend TnaCreate: two dropdowns (value-add, fabric) + auto-derived lead, replacing
  the template picker + free-text lead box.
- docs/plans/STATUS.md: P1/P2 progress + infra lessons.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 11:24:17 +05:30
Ajay SableandClaude Opus 4.8 3b0230969c feat(tna): reconcile Stitch TNA write-back integration + Template-Readiness
Foundation for the TNA authoring→approve→push flow. Reconciles our dormant
scaffold to Stitch's delivered contract (write-back proven live 8 Jul):

- config: split integration host/key (stitch_integration_host/stitch_key),
  correct write path /api/integration/tna-template/
- stitch_tna_api: 3 real endpoints (task-master, task-category-master,
  tna-template POST), STITCH-KEY-only auth
- tna_export: real Create schema — category-by-name, username owners,
  dependency-relationship type + depends_on chain, topological ordering,
  validate_payload enforcing the server rules (exactly 4 critical /
  ≥1 sewing / ≥1 shipment) discovered via live POST
- stitch_master: cached task dictionary + template reconciliation
  (naming-variant detection) + master-aware validation gate
- routers/tna: /tna/stitch/{task-dictionary,reconciliation,validate/{id}}
- frontend: Template-Readiness page (/tna/stitch-sync) + "Stitch" tab
- scripts/dryrun_stitch_template.py: build+validate payload (no POST)
- docs/plans: full end-to-end TNA platform plan + STATUS update

Verified on branch: py_compile + tsc -b + build clean; read-only dry-run
against live UAT Stitch builds + validates as expected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 11:45:01 +05:30
root e70a537baa chore: remove CI deploy test marker 2026-07-08 17:19:47 +05:30
root 7ed20534aa test: verify Gitea Actions CI deploy 2026-07-08 17:18:09 +05:30
root 2d347080de chore: remove webhook test marker 2026-07-08 16:35:28 +05:30
root 9ad943a638 test: verify Dokploy webhook auto-deploy 2026-07-08 16:34:54 +05:30
root d370984423 chore: remove dokploy test marker 2026-07-08 16:28:33 +05:30
root ee503fe635 test: verify Dokploy auto-deploy on push to uat 2026-07-08 16:24:02 +05:30
root e6032703f1 chore: remove test marker — UAT auto-deploy verified successfully 2026-07-08 15:02:46 +05:30
root 6ce9f1d0da test(uat): verify auto-deploy triggers on push to uat branch 2026-07-08 14:54:02 +05:30
root 19c2d66a5c docs(uat): add team user guide for UAT workflow 2026-07-08 14:18:04 +05:30
Ajay SableandClaude Opus 4.8 c6bf6efee5 feat(tna): Phase 0 + 2b — milestone source-map, reconciler, approval capture
Buildable-now foundation for auto-completing TNA milestones (no live-integration
dependency), per docs/plans/tna-milestone-autocomplete.md and Pratik's 3 Jul data
reality:

- services/tna/milestone_map.py — classify each milestone by source (WFX order/PO/
  GRN · Stitch production · SAPL approval · manual) + coverage (AUTO_NOW/NEEDS_QR/
  MANUAL).
- services/tna/reconcile.py — idempotent, monotonic completion + delay + order-risk
  (never un-ticks a done milestone; measures delay vs the frozen plan).
- Phase 2b: models/tna_approval.py (append-only approval log) + services/tna/
  approvals.py; decide_recommendation now records each planner decision, feeding the
  Approvals milestone auto-tick with zero ERP dependency.
- scripts/smoke_test_tna_phase0.py — 24/24 passing.

Not wired into any live sync yet; ready for Phase 1 when Stitch's TNA Task API lands.
NOT deployed (prod deploys are on hold after the 7 Jul incident).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:53:53 +05:30
Ajay SableandClaude Opus 4.8 147429379c feat(kaarigar): add OKF domain-knowledge layer to the assistant
Adopt Google's Open Knowledge Format (curated markdown + YAML frontmatter)
as KAARIGAR's domain-knowledge layer — not a RAG replacement (KAARIGAR is a
tool-calling agent), but the stable, curated knowledge it needs every turn to
reason correctly about SAPL.

- New OKF bundle backend/knowledge/ (glossary, wfx-oc + tna-milestones schemas,
  tna-approval rule, stitch-ids join, when-to-use tool guide), interlinked.
- Loader services/hermes/knowledge.py: frontmatter-stripped, lru_cache, fail-soft.
- agent.py injects it into system_instruction (~2.5K tokens/turn).
- Tested live (real Gemini, local DB): tool routing + delay/owner + Library
  facet behavior all match the curated rules.

Also: STATUS.md updated; TNA milestone auto-complete plan added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:11:54 +05:30
Ajay SableandClaude Opus 4.8 6751de114d feat(hermes): drive the Library's native facet filter, not a static list
"show me beige dresses" now applies the real Refine facets (chips +
filtered grid + count) instead of listing one item and pulsing a card.

- Library.tsx reads URL facet params (?colour=&category=&fabric=&dna=
  &season=&q=) -> native facetSel selections (substring match, _->space)
- search_library emits a "Show in Library" action with those facets; one
  call both answers and filters the UI. Colour matching made lenient to
  match the Library's Colour facet (counts agree: beige=8).
- prompt: browse intents -> filter UI (one-line reply, no long lists,
  single call); factual -> text; TNA "take me to" -> navigate+highlight.

Verified: beige dresses -> /?colour=beige&category=dress; beige pieces
-> 8; "how many late" -> text only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 12:55:43 +05:30
Ajay SableandClaude Opus 4.8 6241ca45ee fix(hermes): precise library search via structured facet matching
search_library was semantic-only, so 'black dresses' returned fuzzy,
polluted results (8 sources for 3 real matches). Now the model extracts
attributes into structured params (category/colour/fabric_family/
design_dna/season) matched exactly against tags; colour is role-aware
(dominant Hero colour). Citations == matched set.

Verified: black dresses -> 3 (all black-dominant); navy blouses -> 2;
knit tops -> 1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 12:42:45 +05:30
Ajay SableandClaude Opus 4.8 540e71b60d feat(hermes): UI-driving — navigate + highlight the thing, RPA-style
HERMES no longer just answers; it can drive the app. New `navigate` tool
lets the agent take the user to a page and highlight a specific item; the
backend returns actions[] with focus-aware paths (/tna?focus=order:...).
A global HermesFocusRunner scrolls to + pulses the matching
[data-hermes-focus] card once the page loads. AskHermes auto-runs the
action (cancellable). Tagged TNA order cards + Library garment cards.

Verified: "take me to the late Primark order" -> query_orders+navigate
-> /tna?focus=order:8863-1264674; broad info queries don't navigate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 12:32:40 +05:30
Ajay SableandClaude Opus 4.8 31665d64d7 feat(hermes): Ask HERMES command bar — read-only Q&A over Library + TNA
Phase 1 of the master-brain unifier (scope-hermes-for-pratik.md §9-12).
An LLM agent whose tools are our own backend functions: the user asks in
natural language, the agent picks tool(s), they run server-side AS the
logged-in user (RBAC applies), and it answers over the real results with
clickable citations. Reuses the Market Scraper's Gemini function-calling
loop, generalised and made synchronous.

- services/hermes/tools.py: search_library + query_orders (portable,
  {summary, rows, citations})
- services/hermes/agent.py: ask_hermes sync loop (never raises)
- routers/hermes.py: POST /api/hermes/ask (auth as current user)
- frontend AskHermes.tsx: floating Ask bar (⌘J) + grounded answer +
  citation cards; mounted globally

Tools verified against real data locally; live Gemini answer verified on
prod (local key is 403 project-denied, same as Scraper/Concept Studio).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 12:09:01 +05:30
Ajay SableandClaude Opus 4.8 288556047c feat(privacy): scope Trends/Moodboard/Boards to their creator
History in Trends and Moodboard (and Boards) leaked across users
because those sections were scoped by department, not by user.
Rework them to per-user ownership:

- rbac: add can_see_all(), owner_scope_query(), can_access_owned()
- add created_by to TrendSearch + MoodboardDecode (boot-time self-heal);
  Boards reuse existing owner_id
- scraper/moodboard/collections: stamp owner on create, scope every
  list, and 404-guard every detail-GET + mutation by owner
- by-garment saved-state only reflects the caller's own boards

Library + Fabric stay public; TNA/Inbox/Review/Analytics stay
team-shared; Admins and 'all'-scope roles still see everyone's.

Verified: scripts/smoke_test_user_isolation.py 14/14.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 11:05:28 +05:30
Ajay SableandClaude Opus 4.8 9fb2571965 fix(auth): solid logout backdrop for text contrast; docs: STATUS update
- Logout overlay backdrop bg-paper/92 → solid bg-paper so the busy page
  (e.g. the colourful fabric grid) no longer bleeds through behind the dark
  "Signing out…" text.
- STATUS.md: add the 2026-06-30 session entry (fabric prod-seed + bulk/folder
  upload + 503/timeout retry + image-hash dedupe, concept fabric-picker +
  enhance, non-blocking startup, Lottie loaders, mobile bottom-nav, animated
  logout, WFX response, dormant Stitch TNA client).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 14:41:12 +05:30
Ajay SableandClaude Opus 4.8 11be606f8d feat(fabric-library): smart AI ingestion + premium page; library/nav polish
Fabric Library (new):
- Separate Fabric model/table; Gemini vision extractor reads the printed
  swatch-card spec label verbatim + analyses the cloth; Pillow swatch crop
- Bulk seeder (idempotent, timeout+retry+streaming); 59/214 seeded so far
- API: GET/facets/{id}, POST /upload, PATCH /{id} (verify)
- /fabrics page: swatch grid + Refine (reuses FacetSidebar) + digital-twin
  detail modal (edit/verify, Use-in-Studio, prev/next, download)

Garment Library detail modal: inline Edit (patchable columns + sample),
Use-in-Studio (as shape), prev/next; gated off the public scan view.

Nav: top tabs -> left hover-expand glass SideRail (shared lib/nav), slim
top bar, glassmorphism; Refine defaults closed; studios mobile-friendly.

Animation: Fabrics uniform row-major grid for a clean cascade; Library
keeps Pinterest masonry, fixed only the reveal (anim-fade-in, opacity-only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 13:42:51 +05:30
Ajay SableandClaude Opus 4.8 35d2fc0283 docs(hermes): addendum — elevate HERMES to the platform-wide master brain
Append a 2026-06-26 section to the HERMES scope note: promote HERMES from the TNA
WhatsApp coach to the atelier's brain across the whole app (Library/Studio/Trends/
Inbox/TNA). One persona, two surfaces (proactive push + an "Ask HERMES" bar); the
TNA coaching becomes its flagship skill. Notes the agent-with-tools design (reuses
our Gemini function-calling engine), grounding + RBAC, carried-over privacy
guardrails, and a 3-phase rollout (in-app Ask + morning brief first; person-level
WhatsApp + scorecards last, HR-gated).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 12:55:43 +05:30
Ajay SableandClaude Opus 4.8 557e2672c3 security(login): remove "Use a demo account" link (+ hard-coded creds)
The demo link's fillDemo helper hard-coded real demo credentials into the frontend
bundle, so anyone with the login URL or viewing source could sign in. Remove the
link, the fillDemo helper, and its CSS. Verified the creds are no longer present in
the built bundle.

Follow-up: the demo account still exists server-side (and the creds are now in git
history) — deactivate it or rotate its password via /admin for full closure.

tsc -b + build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:59:58 +05:30
Ajay SableandClaude Opus 4.8 caea2c66d3 fix(studio): Concept Studio layout fits the page (parity with Product Studio)
Same fit-to-page treatment: h-screen flex layout, compact custom header (drop
PageHeader), height-cap the two input slots (aspect-[3/4] -> h-36), tighten the
description/dimensions/aspect controls with Generate pinned at the bottom, and let
the result panel fill the column height. The old two aspect-[3/4] boxes pushed the
controls below the fold.

Fit smoke 3/3 PASS (scrollHeight==innerHeight at 900px, Generate visible, both
slots present). tsc -b + build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:48:03 +05:30
Ajay SableandClaude Opus 4.8 340bd41a43 feat(studio): AI prompt enhancer for Product Studio
Our-side value-add on the "Describe the shoot" field: ✦ Enhance prompt reads the
uploaded garment image + the user's draft and rewrites it into a precise shoot
direction tuned to how Product Studio Pro is directed (subject/model, pose & angle,
background, lighting, framing, and always "keep the garment's exact colour/pattern/
texture/details sharp").

- backend POST /api/product-studio/enhance-prompt (gated moodboard:edit): Gemini
  vision (gemini-3-flash-preview, JSON out) with a product-photography art-director
  system prompt; designs a default e-commerce shoot if the draft is empty; 503 if
  no Gemini key.
- frontend: client.ts productStudioEnhance() + an Enhance button by the textarea
  (disabled until a garment is uploaded; replaces the draft with the enhanced prompt).

Frontend-flow smoke 3/3 PASS (disabled -> enabled-on-upload -> textarea filled).
tsc -b + build + py_compile clean. Real Gemini call verified on prod (key lives there).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:41:30 +05:30
Ajay SableandClaude Opus 4.8 e60eb0d186 fix(studio): Product Studio output quality — add HD/2K/4K (default 4K)
Output was hard-coded output_resolution:"HD" (896x1152), so texture looked soft
vs Swatch->Garment. The tool also supports 2K/4K — probed 4K = 3712x4608 (~16x
the pixels). Add a Quality selector (HD/2K/4K) defaulting to 4K; bump the backend
default HD->2K; raise the poll ceiling 120->180s and client timeout 180->300s for
the heavier 4K render. Layout still fits the page (4/4 fit smoke).

tsc -b + build + py_compile clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:33:57 +05:30
Ajay SableandClaude Opus 4.8 79a92000b4 fix(studio): Product Studio layout fits the page
The first native cut used two oversized aspect-[3/4] boxes (~700px tall each),
pushing the prompt/aspect/generate controls below the fold. Rework to fit-to-page:
h-screen flex layout, a compact height-capped upload preview (object-contain), a
left control column with Generate pinned at the bottom, and a result panel that
fills the column height. Drop the now-unused PageHeader import.

Fit smoke: scrollHeight==innerHeight at 900px viewport, Generate visible without
scroll. tsc -b + build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:26:36 +05:30
Ajay SableandClaude Opus 4.8 93edc7f66f feat(studio): Product Studio Pro as a native proxy (our UI, their backend)
Replaces the earlier iframe embed: per Ajay, it should be our SAPL frontend over
the vendor's backend, exactly like Concept Studio — not the vendor's branded page.

Live-probed the backend and confirmed the contract (and corrected the trial-quota
worry): /api/tenant-info shows isTrial:false (paid tenant, no quota); /api/edit-image
works anonymously with the image as a base64 data-URL -> requestId; poll
/api/edit-status until COMPLETED -> images[].

- backend/routers/product_studio.py: POST /api/product-studio/generate
  (gated moodboard:edit) base64s the upload, submits, polls to completion,
  downloads the result, persists to data/concepts (served /concepts/).
  config.product_studio_base; registered in main.py.
- frontend/pages/ProductStudio.tsx: native SAPL page (upload, prompt + seed chips,
  aspect ratio, generate, result, download). client.ts productStudioGenerate().

Smokes: end-to-end backend via the real vendor with a synthetic image 4/4 PASS
(851 KB PNG persisted); native-UI render 5/5 PASS (no iframe). tsc -b + build +
py_compile clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:22:52 +05:30
Ajay SableandClaude Opus 4.8 372a7ac873 feat(studio): integrate Product Studio Pro (Phase 1 — gated embed)
AIBrandSolutions' Product Studio Pro (flat garment + prompt -> on-model shots)
is a full async chat/canvas app with a trial session + per-browser-id quota —
not the no-auth one-shot endpoint Swatch->Garment was. A server-side proxy would
share one trial quota across all users; embedding gives each user their own
browser session/quota, needs no creds (works anonymously from the link), and the
tool sets no frame-bust headers.

- New "Product Studio" nav tab -> /product-studio (pages/ProductStudio.tsx),
  gated on the moodboard module like Concept Studio: full-height iframe to the
  tool + an "Open in new tab" fallback.
- docs/plans/product-studio-integration.md: reverse-mapped async contract
  (edit-image -> edit-status, X-Browser-ID trial, R2 storage) for the Phase-2
  native persist-to-library proxy (deferred until SAPL has a vendor tenant key).

Embed smoke 5/5 PASS (gated route, iframe->tool, nav tab, fallback link,
0 errors). tsc -b + build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:13:29 +05:30
Ajay SableandClaude Opus 4.8 7be125f25c fix(qr): public QR scan no longer bounces to the login page
Scanning a hangtag QR (/g/:style, the public no-login landing) redirected to
/login. Regression from the Tier-2 "More like this" work: GarmentDetailModal now
fetches getSimilar on mount, and GarmentScan reuses that modal — but
GET /garments/{id}/similar is RBAC-gated, so the logged-out scan 401'd and the
global axios interceptor hard-redirected to /login (the component .catch can't
stop the interceptor).

- GarmentDetailModal: add showSimilar prop (default true); GarmentScan passes
  false so the gated call never fires on the public page.
- 401 interceptor: skip the /login redirect when on a /g/ path (defense-in-depth
  so a public page handles its own failures).

Logged-out scan smoke 5/5 PASS (renders garment, stays on /g/, no /login bounce,
/similar never called, 0 errors). tsc -b + build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 18:40:30 +05:30
Ajay SableandClaude Opus 4.8 784c03c799 feat(tna): sortable "Waiting for a TNA" lane
The no-TNA lane was hard-sorted by delivery urgency (most overdue first). Add a
sort toggle with three modes:
- Most overdue   (delivery date — existing default, unchanged)
- Longest waiting (most days since OC created with no TNA)
- Newest          (newest-created first, true newest->oldest)

Backend orders_without_tna(sort=) + /api/tna/no-tna?sort=; undated rows always
sort last; unknown sort falls back to delivery. Frontend TNALine segmented
toggle reloads the lane on change + on Sync-now, resets the show-all fold.

Sort smoke 4/4 PASS; tsc -b + build + py_compile clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 18:18:00 +05:30
Ajay SableandClaude Opus 4.8 584020c7ef perf(tna): WFX mirror sync writes deltas only (drop no-op churn)
The 30s WFX poll re-upserted all ~77 mirror rows every cycle — it bumped
last_synced on every row, so all rows went dirty -> 77 UPDATEs/30s
(~220k no-op writes/day) and the log said "upserted 77" regardless of change.

sync_wfx_orders now compares each incoming order to the stored row and writes
(and bumps last_synced) only for rows that actually changed; commit is skipped
when nothing differs, so steady-state cycles do 0 DB writes. Log is now honest
("N new, M changed of 77 seen", debug when none).

Safe: WfxOrder.last_synced has no onupdate and is read by nobody (the last_synced
reads in tna.py are on StitchOrder). `synced` key kept for the "Sync now" UI.

Isolated in-memory delta smoke 5/5 PASS (fresh->2 added; identical re-sync->0
writes, last_synced stable; one field change->exactly 1 changed). py_compile clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 18:07:22 +05:30
Ajay SableandClaude Opus 4.8 af01366bc7 feat(tna): Stitch Task Template API — digest, exporter, sample payload
The 2026-06-25 Stitch call unblocks TNA write-back: Stitch will expose a Task
Template API so our recommender can push templates back (closes the "no write-back"
gap open since 16 Jun).

- docs/plans/stitch-task-template-api.md: digest, payload contract, mapping to our
  mirrored templates, action items, and the open-questions list to send Stitch.
- services/tna_export.py: map a mirrored/recommended template -> the (inferred)
  push payload + validate per-task mandatories and the mandatory 4-critical /
  1-sewing / 1-shipment composition. ("swing" == sewing, confirmed by the
  sewing_task field already in task-template-master.)
- scripts/gen_stitch_template_sample.py -> docs/plans/stitch-task-template-sample.json:
  a contract-valid sample to send back (action item #2). Sample: backward template,
  23 tasks, lead 90d, validation PASS.
- STATUS: log the digest; flip the write-back blocker from red to amber.

Not wired to any live endpoint yet (awaiting Stitch's URLs + masters).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 15:14:05 +05:30
Ajay SableandClaude Opus 4.8 f489dfd448 feat(boards): board page = native-ratio masonry of pins (Tier 3)
The Boards landing + mosaic covers already existed; the board page itself still
used the old uniform aspect-[3/4] crop. Rebuild it as a native-ratio masonry
consistent with the Library:

- CSS multi-column masonry + dominant-colour placeholders via the shared
  GarmentImage; keeps drag-to-reorder + hover-remove.
- Clicking a pin opens the shared closeup (GarmentDetailModal) with its
  "More like this" strip; onSave omitted (no Save CTA on a board), onOpenGarment
  fetches+maps so More-like-this still navigates.

Board smoke 7/7 PASS (14 pins masonry, varied heights, 5 cols, all decoded,
closeup w/ 6 similar thumbs, no Save CTA, 0 errors); Library smoke re-run green.
tsc -b + build clean. Library Pinterest redesign (Tiers 1-3) complete.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:23:06 +05:30
Ajay SableandClaude Opus 4.8 fd1f49788b feat(library): Pinterest interactions (Tier 2) — hover-Save, infinite scroll, closeup
- Hover-Save hero: prominent gold "Save" pill + secondary find-similar appear
  top-right on card hover (replaces the old bottom-overlay +).
- Progressive infinite scroll: render a growing 24-card window of the filtered
  set; an IntersectionObserver sentinel appends the next batch near the viewport;
  window resets on filter/search change. Faceting stays fully client-side, so the
  live facet counts keep working (backend limit/offset would break them at this
  scale — revisit at thousands of items).
- Closeup: the 2-pane detail modal gains a "Save to board" CTA and a "More like
  this" mini-masonry (existing getSimilar endpoint); clicking a similar thumb
  swaps the closeup to that garment.

Runtime smoke extended -> 8/8 PASS (59/59 decoded, 16 heights, 6 cols, windowing
gates initial render then scroll->59, hero Save on every card, closeup
Save+More-like-this with 6 thumbs, 0 errors). tsc -b + prod build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:14:59 +05:30
Ajay SableandClaude Opus 4.8 5bd39eecc6 feat(library): Pinterest-style masonry feed (Tier 1)
Replace the uniform aspect-[3/4] grid (which cropped every garment to an
identical tile) with a CSS multi-column masonry. Cards now render at their
native aspect ratio — the library's images span ratios 0.54–1.39, variety
that was being thrown away — each over a dominant-colour placeholder that
reuses the garment's first tagged colour and reserves a 3:4 box until decode.

- GarmentImage: native-ratio masonry mode + a `cover` prop so fixed-size
  contexts (detail modal, similar-intent modal, selection tray) still crop.
- Fix a real lazy-load deadlock: hiding the un-decoded image with display:none
  prevents `loading=lazy` from ever loading it (a hidden image is never in
  viewport) -> 0 images would render. Float it at opacity-0 instead.

Runtime smoke (Playwright on the built app, API mocked to the real 59-garment
payload): 59/59 images decoded, 16 distinct heights, 6-col masonry, 0 errors.
tsc -b + prod build clean. Tier 2 (hover-Save, infinite scroll, closeup) next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:06:20 +05:30
Ajay SableandClaude Opus 4.8 b862ce1d29 docs: mark Concept Studio deployed & live in STATUS.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:36:10 +05:30
Ajay SableandClaude Opus 4.8 678e0764a5 feat(studio): Concept Studio — Swatch→Garment via AIBrandSolutions proxy
New "Studio" tab (/concept): drop a garment shape + a fabric swatch, describe
the item, and generate a concept preview. Proxies SAPL's commissioned
AIBrandSolutions Swatch→Garment backend server-side (no vendor creds needed),
persists the result to data/concepts/, and serves it same-origin behind our
login/RBAC.

- backend/routers/concept.py: POST /api/concept/swatch-to-garment (moodboard:edit)
- config: swatch_to_garment_base
- main.py: mount /concepts (fresh-checkout-safe mkdir) + register router
- frontend: ConceptStudio page, "Studio" nav tab, swatchToGarment()/conceptImageUrl()
- docs: 24-Jun design-library meeting notes + STATUS entry

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:31:23 +05:30
Ajay SableandClaude Opus 4.8 9ba1712d60 docs: log 2026-06-24 progress in STATUS.md
AI TNA recommender + Critical-Dates board/calendar + Google SSO + the
prod-readiness sweep (demo-order hiding, secret-key guard, blank-home fix,
nginx cache fix, milestone mapping) and the WFX export gap finding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 15:35:38 +05:30
Ajay SableandClaude Opus 4.8 c124317970 docs: simple TNA workflow overview + 2026-06-23 meeting notes
Plain-language one-pager of the TNA automation flow (for sharing with Pratik)
and the raw meeting transcript/notes from the 23 Jun TNA session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 11:20:27 +05:30
Ajay SableandClaude Opus 4.8 43d950d26c docs: split STATUS.md changelog — archive entries ≤2026-06-16
The running "Last updated" changelog had grown to ~44K chars on a single
line (~135 dated entries). Moved entries from 2026-06-16 and earlier into a
new docs/plans/STATUS-ARCHIVE.md (newest-first), leaving the recent ~2 weeks
in STATUS.md with a pointer link. No entries altered, just relocated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:17:44 +05:30
Ajay SableandClaude Opus 4.8 eac05046a9 feat(rbac): granular user & access management (roles + departments)
Real role-based access control over the previously-stub auth:
- models/rbac.py (Department, Role w/ module×level matrix + all|own scope);
  User gains department_id + is_active; nullable department_id (NULL=global)
  on garments/collections/trend_searches/moodboard_decodes/inbox_mails/
  stitch_orders. Idempotent migrate_rbac.py seeds 5 roles + 5 departments
  and maps existing users; seed_rbac() also re-runs on boot.
- services/rbac.py: effective_permissions / has_permission / user_scope /
  scope_query / user_payload (Admin hardcoded all-manage).
- require_permission(module, level) dependency; applied as router-level view
  guards + per-endpoint edit/manage across garments, collections, moodboard,
  scraper, review, ingest, mail, tna, search, analytics. Data explorer now
  gates on the `data` permission. QR scan stays public.
- Department scoping on library/boards/trends/moodboard lists; dept set to
  the creator's department on create.
- routers/users.py (/api/admin/users|roles|departments|meta, users:manage):
  user/role/department CRUD with last-admin, self-deactivation, system-role
  and in-use guards. login/me return permissions+scope+department.
- Frontend: lib/auth.ts can() helper, client.ts admin API, TopNav permission
  filtering + Admin link, App.tsx RequirePerm route guard, new pages/Admin.tsx
  (Users/Roles/Departments tabs + permission-matrix editor).

Verified via TestClient + manual click-through; tsc/build/py_compile clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:07:53 +05:30
Ajay SableandClaude Opus 4.8 30026ba831 fix(mobile): sweep remaining pages for responsiveness
- TNAAllOrders + TNALifecycle: add pt-20 so headers clear the fixed nav
- TNAAllOrders + Review: tables were in overflow-hidden wrappers that
  clipped on mobile -> overflow-x-auto + min-w so they scroll
- ScraperReport: trend-report sidebar was hidden lg:flex (mobile lost
  the report text) -> flex-col lg:flex-row, stacks above the grid
- Upload: form-field rows grid-cols-2 -> grid-cols-1 sm:grid-cols-2
- TNAOrderPERT: fixed 340px AI panel crushed the lanes on mobile -> now
  a full-screen overlay below the nav, default-closed <768px; header wraps

Analytics/Moodboard/Boards/OrderInbox/Upload-main/AdminData were already
responsive. tsc -b + build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 17:08:18 +05:30
Ajay SableandClaude Opus 4.8 a052c8caf2 fix(mobile): responsive TNA dashboard + Production-Line zone cards
On-device testing flagged two broken TNA views:
- TNA.tsx dashboard: filter sidebar + charts were a non-collapsing
  2-col flex -> content squeezed to half width, PiecesStrip numbers
  overlapping. Now flex-col lg:flex-row, sidebar w-full lg:w-52 stacks
  on mobile; PiecesStrip header stacks flex-col sm:flex-row.
- TNALine.tsx zone cards: the horizontal OC->pre->PCD->prod->Dispatch
  strip overflowed and overlapped labels on mobile. Strip is now
  overflow-x-auto with shrink-0 sm:flex-1 ZoneBoxes (scrolls on narrow
  screens, desktop unchanged). Added pt-20 so the header clears the nav.

tsc -b + VITE_API_URL=/api build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 16:57:01 +05:30
Ajay SableandClaude Opus 4.8 6306eed0b7 feat(mobile+qr): responsive nav/headers + cloud-scannable QR codes
Mobile:
- TopNav: collapse the 9-tab bar into a hamburger drawer below lg
  (links + New garment + user/sign-out + scrim); desktop unchanged
- PageHeader: stack flex-col on mobile so title + right-side
  filters/stats/tabs no longer overlap

QR (cloud):
- build_qr_payload URL-encodes the style segment so spaced style
  numbers (e.g. "SA W675 A") scan cleanly
- qr.png/hangtag.png auto-heal payloads whose host != current
  QR_BASE_URL (old frozen localhost/LAN QRs) -> rewritten once to the
  cloud host and persisted, so existing QRs resolve from a phone

tsc -b + VITE_API_URL=/api build clean; backend py_compile OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 16:38:10 +05:30
Ajay SableandClaude Opus 4.8 0db1e5e527 perf(tna): cache Stitch integration reference + gate owner enrichment
The 2-min sync_from_stitch refetched the slow STITCH-KEY integration
reference (style-plan-master ~51 pages/1010 plans + client-master) and
made one style-tna-data owner call per order every cycle, even when
nothing changed (0 new rows, identical enrichment).

- _get_integration_reference(): TTL-cache plan_map+buyer_map for 30 min,
  returns a `fresh` flag
- _enrich_from_integration(): per-order owner fetch only when the
  reference was just refreshed (every 30 min, catches reassignments) or
  the order has no owner yet -> ~0 integration calls in steady state
- add owner_api_calls to the sync summary for visibility
- stitch_sync_loop sleeps first (lifespan already runs the initial sync;
  was double-syncing at startup)

Near-real-time milestone fetch, last_synced stamping and buyer/qty
mapping still run every cycle. Buyer count 36 (client-master, full
catalogue) vs 34 (tna-dashboard-client-data, clients with TNA) is an
expected superset/subset, not drift.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 16:25:01 +05:30
Ajay SableandClaude Opus 4.8 437a68e93e feat: migrate Market Scraper from Groq+Mistral to Gemini + dev fixes
Closes #1

Scraper now runs entirely on Gemini (gemini-3-flash-preview):
- scraper_agent.py: agent loop rewritten to Gemini native function-calling
  (Tool/FunctionDeclaration from the existing schema, tool_config AUTO,
  function_response parts fed back, _to_jsonable arg coercion, 8192 out-tokens
  + 1024 thinking so the report arg never truncates, fail-fast if no key)
- scraper_tools._analyze_image: Pixtral -> Gemini vision (JSON, thinking_budget=0)
- same 4 tools, max_steps=10, 429-retry, 2s/5s pacing preserved
- Groq/Mistral still power garment tagging + PERT narrator (untouched)
- requires only GEMINI_API_KEY; verified live end-to-end (Zudio SS26, 14 items)

Also fixes found while running locally on a fresh clone:
- client.ts: imageUrl/scrapedImageUrl derive ASSET_ORIGIN from API BASE so
  images resolve to the backend in dev; prod output byte-identical (/images,
  /scraped relative, proxied by nginx)
- main.py: create data/images + data/scraped before StaticFiles mounts (mount
  runs at import, before the lifespan hook) so a fresh checkout boots
- Scraper.tsx: prominent loading state (spinner + current action + elapsed
  timer + "Starting agent..." panel)

Docs: STATUS.md log entry + stack/scraper references updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 16:03:51 +05:30
shridhar.sabat c5a11f6adc Initial commit: SAPL backend + frontend + docs 2026-06-21 18:37:36 +05:30