Commit Graph
18 Commits
Author SHA1 Message Date
Ajay SableandClaude Opus 4.8 9f03ce3874 feat(tna): P1 authoring flow — Create TNA from a no-TNA order → edit → approve → push
Adds the merchant authoring flow on top of the existing recommender:
- POST /tna/draft: propose a TNA for a no-TNA order, with optional template
  override + fabric-lead-time override (recommender extended for both).
- PUT /tna/recommend/{key}/schedule: persist planner date/owner edits (frozen
  once accepted); sets edited flag.
- POST /tna/push/{key}: push an approved plan's template to Stitch, gated on
  Template-Readiness — returns blocked + missing tasks when the tenant isn't
  seeded (no silent failure). Distinct path avoids the /{decision} route clash.
- TnaRecommendation gains pcd_date/edited/stitch_template_id/push_status/
  push_detail (idempotent migration + boot self-heal in services/tna_authoring).
- Frontend: TnaCreate page (proposal + template picker + fabric-lead-time input +
  editable milestone table + approve→push with readiness gate), wired from the
  "Create TNA" action on the no-TNA cards.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:01:06 +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 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 3be60a301b feat(analytics): filters, library-growth trend, and expanded breakdowns
Extend the design-analytics page (all real data, no mock):
- /api/analytics now accepts filter params (fabric_family, design_dna,
  design_team_city, season, segment, status, category, collection) and
  scopes every distribution, colour palette, growth series and review
  health to the filtered garment set; returns filter_options + growth.
- New tag breakdowns surfaced (neckline, sleeve, length, hem, closure,
  fabric_texture, embellishment, weave, group) plus design-team-city.
- Library-growth trend chart from garment created_at (added + cumulative)
  — the team-adoption curve.
- Frontend filter bar (only shows dimensions with >1 value), recharts
  growth chart, new sections, and mobile-friendly labels/padding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 12:14:22 +05:30
Ajay SableandClaude Opus 4.8 742adf872d fix(startup): don't block app readiness on the initial integration syncs
On every restart the frontend sat empty until a flood of outbound GETs
finished. Cause: the lifespan AWAITED the initial Stitch pull (50+ pages of
style-plan-master) — plus the TNA-template and WFX syncs — BEFORE yielding.
Uvicorn doesn't accept requests until the lifespan reaches `yield`, so the
API was unavailable for the whole initial-sync duration.

Move all three off the pre-yield path:
  - Stitch initial sync + 2-min loop → background task (loop already sleeps
    first, so no double sync).
  - TNA template master sync → its own background task.
  - WFX mirror → background loop via asyncio.to_thread (the sync is blocking
    urllib; running it on the loop also stalled every request each cycle).
    First sync now fires immediately instead of after a 30s wait.

Startup now reaches `yield` at once; data fills in as syncs land.
py_compile clean; SQLite engine already check_same_thread=False.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 11:36:15 +05:30
Ajay SableandClaude Opus 4.8 fa8a1d40fc fix(fabrics): dedupe swatch uploads by image hash
Uploading the same card twice created two rows — there was no duplicate
check. design_no can't be the key: the AI label-read varies between runs
(saw "SAPL-LOT NO-8658" + brand RAICHALLO vs "...8658 RAICHALLO" + no brand
for the same card). The reliable identity is the image bytes.

- new Fabric.source_hash (sha256 of the source card image), indexed;
  ensure_fabric_schema() ALTERs it onto the existing prod table at startup,
  and backfill_source_hashes() hashes the already-seeded rows so they dedupe
  too. Wired into the lifespan.
- POST /fabrics/upload hashes the bytes BEFORE extraction: a known hash
  returns the existing fabric with {duplicate:true} + HTTP 200 (and skips the
  wasted Gemini call); new cards store the hash and return {duplicate:false}.
- frontend: bulk upload counts duplicates as "skipped" (not re-added, not
  failed), shown live ("· N dup") and in the summary.

py_compile + tsc -b + build clean; schema self-heal + dedupe lookup unit-tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 18:19:00 +05:30
Ajay SableandClaude Opus 4.8 5d044925c9 feat(fabrics): seed the Fabric Library on fresh deploys
Production runs a separate SQLite file on a persistent volume that was
never seeded, so /fabrics showed 0 swatches even though 59 were ingested
locally. Ship a committed snapshot (seed/fabrics_seed.json + 118 card/
swatch images) and an idempotent startup seeder:

  - inserts the 59 rows only when the fabrics table is empty (never
    overwrites edits / human-verified data on a populated prod DB)
  - copies swatch images into the images volume only when missing
  - coerces ISO datetime strings back to datetime for SQLite

Wired into the lifespan startup right after RBAC seeding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 16:04:31 +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 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 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 66fa5c953f feat(tna): "Sync now" button + 30s WFX poll for near-instant lot pickup
Add a "Sync now" button on the Production Line that pulls WFX on demand and
refreshes the board, so a freshly-created lot appears in seconds instead of
waiting for the timer. Drop the WFX mirror poll from 2 min to 30s as the
no-click safety net. Lower the /wfx-sync gate from tna:manage to tna:view —
it's a benign read-refresh, so any TNA user (e.g. a merchant who just made the
lot) can trigger it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 11:51:38 +05:30
Ajay SableandClaude Opus 4.8 eb14ac6810 chore(security): warn loudly at startup if SECRET_KEY is the dev default
The token-signing secret_key must be overridden in production; running on the
built-in default makes auth tokens forgeable. Log a CRITICAL on boot so this
can't happen silently.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 11:26:08 +05:30
Ajay SableandClaude Opus 4.8 7f67ffe7cc feat(tna): AI recommendation engine + critical-dates board & calendar
Recommendation engine: mirror Stitch's 17 task templates, fuzzy-match the
best template per order (buyer + lead-time fit), and back-schedule every
milestone date from the delivery anchor (CPM). Proposed plans stored in SAPL
(no Stitch write-back); planners review/accept on the order lifecycle page,
and "Waiting for a TNA" cards surface a "Suggested TNA ready" hint.

Critical milestones: drive the key/critical flag from Stitch's own
critical_task flag (no duplicate definition); flag them in PERT + order
detail, add a clickable milestone detail card, a key-dates strip and a
key-only focus toggle in the timeline.

Critical Dates board (/tna/key-dates): cross-order table of every order's
critical milestones, plus a month-grid calendar view of what's due when.
Added a consistent Line/All/Critical/Dashboard nav across the TNA pages.

Also: map the spaced "PCD ( Plan Cut Date )" task variant to D2; restore +
extend the TNA smoke test (auth + recommendation assertions), 48/48 passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 11:20:14 +05:30
Ajay SableandClaude Opus 4.8 b46c313e2f fix(rbac): self-heal RBAC columns on startup for pre-existing DBs
Base.metadata.create_all can't ALTER existing tables, so a prod DB that
predates the RBAC columns would break on boot. ensure_rbac_schema() adds the
missing department_id/is_active columns idempotently on every startup, run
right after create_all and before seed_rbac.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:11:36 +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 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 f86b941cab test: auto-deploy pipeline - frontend title + backend v2.0.0 2026-06-21 21:03:21 +05:30
shridhar.sabat c5a11f6adc Initial commit: SAPL backend + frontend + docs 2026-06-21 18:37:36 +05:30