74 Commits
Author SHA1 Message Date
Hermes Agent 37f386eeba ci(azure): repoint prod CI to Azure VM + PyPI mirror for uv
CI/CD Pipeline / lint-and-test (pull_request) Successful in 2m46s
CI/CD Pipeline / backend-unit-tests (pull_request) Successful in 34m42s
CI/CD Pipeline / deploy (pull_request) Skipped
CI/CD Pipeline / Publish release notes + notify users (pull_request) Skipped
Semgrep SAST / Static Security Scan (pull_request) Successful in 38s
- ci.yml deploy: run on the Azure VM host (/opt/sapl/prod, hermes
  git auth, apk mirror loop, BusyBox-safe ownership restore)
- backend/Dockerfile: UV_DEFAULT_INDEX -> Tsinghua mirror (pypi.org
  flaps from Azure, broke backend builds twice)
- docs: Gitea URL -> git.*
2026-11-03 23:05:50 +05:30
sapl-admin c9f9dd109b docs(boards): smoke test + phase-A plan follow the is_deleted trash contract (refs #1667) 2026-10-15 19:11:34 +05:30
Hermes Agent 68aa64e8f1 feat(scraper): enforce house taxonomy at ingest — alias resolver + colour collapse (refs #1522)
Categories resolve through the §1.3 machine tree (backend/taxonomy_data/,
generated from docs/plans/garment-taxonomy.md by generate_taxonomy_tree.py):
tee/T-shirt/T-shirts → T-Shirt, sub-types fold to parents, anything else →
empty + taxonomy_review flag. Colours collapse to bare CANONICAL_COLOURS by
measured hex (blue-gray → whichever it is more of), unknown → Unknown +
review. taxonomy_review is ingest bookkeeping: excluded from the item
sidecar all_tags and from Save-to-Library GarmentTag rows. 28 new tests +
2 adapter regression tests; test file registered in ci.yml + deploy-dev.yml.
2026-10-12 17:43:25 +05:30
sapl-admin 34b73ef0cd fix(auth): move the browser session to an HttpOnly cookie; drop script-src unsafe-inline (refs #1346)
The bearer token no longer lands in any script-readable storage. Login and
Google sign-in set an HttpOnly + Secure + SameSite=Lax `sapl_session` cookie,
and the frontend stores only the non-secret user snapshot (any legacy
`sapl_token` key is removed on sight). The Bearer path is unchanged for
machine clients — tests, scripts and the QA harnesses keep using the body
token / Authorization header — so nothing that cannot hold a cookie breaks.

Backend
- routers/auth.py: `_set_session_cookie` / `_clear_session_cookie` helpers;
  `current_user` now resolves header OR cookie; new idempotent POST /logout.
- config.py: AUTH_COOKIE_NAME (sapl_session) + AUTH_COOKIE_SECURE (default
  true; set false only for a plain-http localhost dev run).

Frontend
- axios uses `withCredentials` and no longer attaches an Authorization
  header; direct fetches (techpack previews/downloads, seasons, gallery,
  org chart) send `credentials: 'include'`.
- sign-out controls call `logout()` (clears the server cookie, then the
  local snapshot) before navigating — a local-only clear would leave the
  session alive server-side.
- `isAuthed()` reads the user snapshot; the 401 interceptor still heals a
  stale snapshot.

CSP
- script-src loses 'unsafe-inline': the built bundle carries ZERO inline
  scripts (only /assets/*.js and /registerSW.js, both 'self') and Google's
  documented GSI policy needs none. style-src keeps it (React style
  attributes, the login <style> block, GSI's injected styles).

Tests
- backend/tests/test_auth_session_cookie.py (offline; wired into the CI
  router-test step): cookie attributes, cookie/header/anonymous resolution,
  logout expiry, and no cookie on a failed login.
- frontend/src/lib/auth.test.ts: token never persisted; legacy key cleanup.
- seasons.test.ts signIn() now seeds the user snapshot.

Note: sessions from pre-#1346 builds end at the next request after deploy —
everyone signs in once more.
2026-10-09 12:06:08 +05:30
sapl-admin 7eae9482c6 feat(library): pattern number optional, style number mandatory
The garment's identity is its style number (QR scan URL, hangtag, every TNA
join key). Pattern number is a pattern-master reference that routinely lands
after the sample, so it must not gate an approval.

- contract: MANDATORY_FIELDS = (style_number, fabric_quality, counters) in
  services/qr_fields.py, mirrored by frontend/src/lib/qrFields.ts; the
  QR_MANDATORY_FIELDS / missing_qr_fields names stay as aliases so the gates,
  the QR endpoint and the CI test list keep working.
- approval gates + POST /api/plm/{id}/qr now require the style number; it is
  listed exactly once (no double-listing in the refusal payload).
- closed the identity hole in PUT /api/garments/{id}: blank, whitespace-only
  and explicit JSON null are all 422 STYLE_NUMBER_REQUIRED and the row is left
  untouched. The edit form used to send null when the field was emptied, which
  silently erased the number.
- form: Style number carries the "· required" marker and refuses to save;
  Pattern no. loses its "· QR" marker and is marked optional.
- capture drafts are safe: garment_capture_save_service reserves a style number
  at save time, so no draft is blocked by the new rule.
- tests: inverted the null-clears-the-number case, added pattern-less-approval
  and per-field blocking coverage.

Existing style-less rows are deliberately left un-approvable until someone
fills the number in (no backfill, no data writes).
2026-09-28 12:24:13 +05:30
sapl-admin f18c961e3e fix(a11y): reduced motion drops the animation, not just its duration
A 0.01ms keyframe still only STARTS on the frame after the element is inserted,
so the panel's first painted frame was the keyframe's `from` state (opacity 0)
even with prefers-reduced-motion: reduce. Verified live on dev by the harness's
8a check ('panel fully opaque on the first frame' -> measured 0).
`animation: none` paints the element's natural style immediately and removes
the row stagger's delays with it.

Also decides the panel's open/close from an imperative `phase` ref instead of
the render closure, so a close+re-open landing in ONE task re-opens the panel
instead of being swallowed as a second close (harness 6b).

And fixes the harness itself: it sampled the panel synchronously right after a
programmatic .click(), i.e. before React's commit, which reported 'panel never
mounted' for a working panel (checks 1/2/3a/3b/8a); the sample now starts on the
first animation frame, and the gates scan every sample. It also fell back from
channel='chrome' (not installed on the verifying VPS) to bundled chromium.
2026-09-23 05:19:58 +05:30
sapl-admin cb64c4aac6 test(ui): playwright harness for notification panel motion 2026-09-22 19:18:07 +05:30
shridhar.sabat 9bfa806318 fix(security): SSL, Docker, /tmp, hardcoded password false positives
Security fixes:
- trigger_mail.py: add certifi-backed SSL context for SMTP
- Dockerfile: add non-root sapl user (docker:S6471)
- .dockerignore at repo root (docker:S6470)
- people_endpoint_probe.py: tempfile.mkdtemp() (S5443)
- render_mail_local.py: tempfile.mkdtemp() (S5443)

False positive suppression:
- 7 test scripts: # NOSONAR on hardcoded password lines (S2068)
2026-08-28 10:27:09 +05:30
shridhar.sabat f13c6469ba Merge pull request 'fix(sonarqube): extract duplicated string literals into constants (Batch 3)' (#363) from fix/sonarqube-string-dup into dev 2026-08-28 00:57:13 +05:30
shridhar.sabat 859822bf69 fix(sonarqube): extract duplicated string literals into constants (Batch 3)
Resolves 89 python:S1192 string duplication issues across 40 files by
extracting repeated literals into module-level UPPER_SNAKE_CASE constants.

Files: generate_tna_phase1.py, mail_format_probe.py, escalation_matrix.py,
migrate_sqlite_to_postgres.py, concept.py, tna.py, llm_routes.py, auth.py,
garments.py, fabrics.py, moodboard.py, plm.py, product_studio.py, mail.py,
scraper.py, route_store.py, rule_templates.py, enhance.py, scraper_agent.py,
scraper_tools.py, tagging_service.py, fabric_extraction_service.py,
stitch_service.py, dispatch.py, review_service.py, trigger_mail.py, main.py,
migrate.py, migrate_moodboard_storytelling.py, verify_auth_flow.py,
smoke_test_auth.py, smoke_test_boards.py, playwright_check_feed.py
2026-08-28 00:56:25 +05:30
shridhar.sabat f3cdf1f3f8 fix(sonarqube): resolve BLOCKER + security vulns + sort bugs (Batch 1)
BLOCKER fixes:
- smoke_test_pert_narrate.py: add return ok to check() (S3516)
- plm_service.py: type-ignore for Pillow >=10 size param (S930)

Security fixes:
- wfx_service.py: remove SSL CERT_NONE bypass (S5527/S4830)
- wfx_webapi_client.py: remove SSL CERT_NONE bypass (S5527/S4830)
- wfx_login_probe.py: remove SSL CERT_NONE bypass (S5527/S4830)
- tna_recommender_prototype.py: remove SSL CERT_NONE bypass (S5527)
- migrate_sqlite_to_postgres.py: replace MD5 with SHA256 (S4790)
- backend/.dockerignore: exclude sensitive files from COPY (docker:S6470)

Bug fixes:
- Library.tsx: add localeCompare to sort (S2871)
- TNAAllOrders.tsx: add localeCompare to sort (S2871)
- TNALine.tsx: add localeCompare to 2 sort calls (S2871)
2026-08-27 23:52:31 +05:30
shridhar.sabat 5bb283e939 docs(tna): add milestone #15 tester manual (frontend UI test guide)
Step-by-step manual test document covering issues #82-#92 (TNA creation,
plan engine, approval workflow, email triggers, escalation matrix, colour
coding, admin rule editor). Includes seed credentials, email dry-run
safety warning, 19-milestone verification table with exact task IDs from
planner.py, mobile PWA checks, and regression smoke test.
2026-08-10 10:48:40 +05:30
shridhar.sabat f02be094b2 test(uat): add live UAT API test suite + UAT test case document
- backend/tests/test_uat_live_api.py: 55 read-only pytest tests against the
  deployed UAT stack (uat.ai-automation-hub.tech) covering meta endpoints,
  auth/RBAC, library/search, TNA (stats/orders/filters/lifecycle/watchdog),
  boards, moodboard, scraper, fabrics, feed, notifications, and PWA artifacts
  (sw.js, manifest, icons, offline media SW routes). Structural assertions
  only — safe on the prod-synced UAT environment.
- backend/tests/conftest.py: add --base-url option to target any deployment.
- backend/pytest.ini: register the 'uat' marker.
- docs/test-cases/uat-test-cases.md: TC-001..TC-153 matrix (55 automated +
  23 manual mutation/LLM/browser cases) with sign-off checklist.

Verified: 94 passed (39 existing unit + 55 new live UAT) against commit 2e390bf.
2026-08-05 22:52:42 +05:30
root 014778812d feat: migrate UAT database from SQLite to PostgreSQL 2026-08-04 00:02:48 +05:30
Ajay SableandClaude Opus 4.8 167a036607 fix(moodboard): assign each deck page to one category (no repeated strips)
Pratik: the "From the deck" strips showed the same ~8 pages under every
category. The filter included any page that merely mentioned a category, and
most deck pages are full looks (colour+fabric+silhouette) → qualified for all.

Replace with assignDeckPages: each page goes to a single category — its
page_type enum (colour_direction/fabric/print/silhouette) wins, else its
dominant content array. No page repeats across sections; each strip is
genuinely relevant. Verified on a real 14-page decode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 11:34:24 +05:30
Ajay SableandClaude Opus 4.8 26760be264 feat(moodboard): show the buyer deck's own images under each category
Pratik feedback — the decode read as a wall of text. The deck pages were
already rendered + tagged during analysis but never displayed. Add a
"From the deck" thumbnail strip (deckPagesFor + DeckStrip) to the Colour /
Fabric / Print / Silhouette sections, sourced from page_results by category
(ranked by relevance, click → lightbox). Zero extra AI cost.

Also render the Fabric/Print/Silhouette sections even when no AI swatches
were generated (previously hidden) — deck strip + brief text — so a
text-only brief still surfaces the buyer's imagery.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 11:01:02 +05:30
Ajay SableandClaude Opus 4.8 9948888241 docs(status): record prod promotion of the OpenCode/moodboard/concept stack
main was 17 commits behind + diverged from uat; promoted via cherry-pick of
the 7 session commits onto main (261b0e6), Dokploy deploy verified live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 17:02:43 +05:30
Ajay SableandClaude Opus 4.8 ae5bb90cf0 feat(library): concept as an origin-tagged library entity
Concept/Product Studio + moodboard outputs were one-shot generators whose
images never entered the library. Persist them as first-class, origin-tagged
entries so the public library stays clean.

- Garment.origin (made|ai_concept) + origin_source; rbac self-heal
  (existing rows default to 'made').
- filter_search excludes ai_concept by default (concepts never leak into the
  main library, studio pickers, similar, or boards); origin=ai_concept|all
  param threaded through /garments + /search/filter; origin in serializer.
- POST /api/garments/concept: copies a generator image into the library store
  and creates an origin=ai_concept garment (no style number / QR / production).
- Frontend: Library Made/Concepts tab toggle; amber CONCEPT badge on cards;
  concept cards titled by description; saveConcept client fn; "Save to Library"
  on Concept Studio + Product Studio results + moodboard starter-kit cards.

Validated live: concept created → excluded from Made grid, shown in Concepts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 16:30:34 +05:30
Ajay SableandClaude Opus 4.8 2a5b95e37e fix(moodboard): gate starter kits + flats on the garment-visual opt-in
Technical flat sketches generated unconditionally and the Design Starter
Kits section was assembled regardless of what the designer chose — so
picking only "print & fabric swatches" still produced garment flats + a
full starter-kit block they never asked for (wasted image spend on a
garment design they won't use).

Starter kits are a garment-design deliverable, so tie them to the garment
opt-in: wants_garments = generate_visuals AND "concepts" in visual_types
now gates construction-spec drafting, flat-sketch generation, and the
starter-kit assembly. Swatches-only runs skip all of it (frontend already
hides an empty section); "garment concepts"/"everything" get the full kit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 11:32:25 +05:30
Ajay SableandClaude Opus 4.8 a51bf2b3f4 fix(moodboard): accurate library matching — DB-grounded fabric availability + real threshold + embed_texts batch bug
The "Fabric direction" cards showed in-house garment matches for fabrics
SAPL doesn't stock. Calibrating against the live gemini-embedding-2 index
proved cosine similarity can't judge availability (a suede query scores
0.69 > cotton poplin 0.66; nonsense 0.51), so availability must be a DB
fact.

- _fabric_status: vocabulary-driven, matches STRUCTURED fabric fields only
  (composition/weave/texture/pattern), never free-text description — kills
  the blend/soft/coloured false-positives; replaces loose check_fabric_library
  in the moodboard grounding.
- _match_library_per_item: gap-gate — a sourcing-gap fabric surfaces zero
  in-house garments.
- embed_texts: gemini-embedding-2 returns ONE embedding for contents=[N],
  silently collapsing N per-item queries to 1; embed per-text instead
  (restores silhouette/print matching that was dropped by a zip).
- _enrich_matches: calibrated similarity floor (moodboard_match_min_similarity
  0.58, env MOODBOARD_MATCH_MIN_SIM) drops nonsense-tier hits.
- Frontend: subtle library-status hint on fabric cards (in / similar / not in
  library) so an empty gap card doesn't read as broken.

Validated live vs real DB: tweed/suede/terry→gap (no in-house), cotton
poplin/twill→have, satin/organza+viscose→similar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 11:23:47 +05:30
Ajay SableandClaude Opus 4.8 c6b0206540 feat(moodboard): dotLottie decode-loading animation + STATUS archive trim
Replace the static pulsing-dots in PdfDecodeProgress with the "cat Mark
loading" dotLottie. Move the file into src/assets (no space) so it goes
through the vite ?url bundle + cache-busting like the other loaders, and
add a DecodingLottie preset to LottieLoader.

Docs: archive the granular 2026-07-25 scraper iteration log to
STATUS-ARCHIVE.md and slim STATUS.md to a summary; mark the moodboard
interactive-flow work as shipped (da212d0, live on UAT).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 10:57:10 +05:30
Ajay SableandClaude Opus 4.8 da212d0606 feat(moodboard): interactive analyze→ask→tailor flow + SAPL grounding + design starter kits (deepseek)
Turns the moodboard from a one-shot decoder into an interactive design partner:

- Interactive flow: analyze deck (Gemini vision) → PAUSE and ask a thorough,
  deck-seeded questionnaire (8 dimensions: direction, colour, fabric, print,
  silhouette, value-adds, commercial, visuals) generated by deepseek → tailor
  the final brief to the answers. New states awaiting_input/synthesising;
  POST /decode-pdf/{id}/answers triggers run_synthesis_job. Vision + structured
  brief stay Gemini; deepseek does questions + answer interpretation (routing
  moodboard→deepseek-v4-flash); everything falls back to Gemini/defaults.

- "What this means for SAPL": brief cross-referenced against SAPL's own fabric
  + garment libraries → make-now / similar / sourcing-gap fabrics + on-DNA vs
  new silhouettes, with a short actionable read (reasoning-dump guarded).

- Design starter kit per hero piece (digitizes SAPL's handmade techpack sheet):
  concept render + technical flat sketch (front/back line drawing) + AI-drafted
  editable structured construction spec + suggested in-library fabric + on-DNA
  badge; PATCH /decode-pdf/{id}/starter-kit persists spec edits.

- Report made calm + collapsible like the scraper: collapsible SectionCards
  (dense sections collapsed by default), collapsible SAPL-grounding + starter-kit
  cards; compact readable type.

Backend: models/moodboard (+questions/answers/sapl_grounding/starter_kits, rbac
self-heal), moodboard_pdf_service split (run_decode_job pauses at questions;
run_synthesis_job), llm/routing moodboard→deepseek. tsc -b + build + py_compile clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 13:06:48 +05:30
Ajay SableandClaude Opus 4.8 4c9486dca2 feat(ai+scraper): OpenCode Go multi-model layer + MOA Deep Research, gender-aware grounded trend scraper, calm bento report
AI / OpenCode Go:
- New services/llm/ shared multi-model layer (opencode_client, moa_orchestrator,
  oai_format, routing) — additive alongside Gemini; all Gemini paths untouched.
- MOA "Deep Research" agent (services/hermes/agent_moa) + POST /api/hermes/ask-with-moa:
  3 parallel reference models + aggregator with fallback chain + transient retry.
- Frontend "Deep" toggle in Ask KAARIGAR (client.ts, AskHermes) rendering model_trace.
- Per-feature routing (services/llm/routing): scraper reasoning → minimax-m3,
  Gemini as automatic first-turn fallback.

Trend scraper:
- Reasoning loop routed to minimax (scraper_agent) with grounding guardrails.
- Grounded in SAPL's own fabric + garment libraries (check_fabric_library /
  check_garment_library) — flags real sourcing gaps, before + after scraping.
- Gender-aware BRAND_URLS (women/men/kids) — agent identifies gender+category
  from the brief; scrape_all_brands sweeps every brand carrying that gender.
- URL fixes (Mango GB, Pull&Bear, Next, UO, Free People, Stradivarius) + worker
  hardening (Inditex CDNs + self-heal, currentSrc/srcset, salvage-on-close, retry).
- Pinterest crash root-caused to a Camoufox/Playwright driver bug; patched via
  services/playwright_patch (idempotent, re-applied on startup in main.py).

Report UI:
- Markdown renderer (tables/lists/headers, compact variant) + BentoReport: a calm,
  visual brief for creatives (palette band, signal confidence dots, "what to make"
  cards, verdict) with grounding/feasibility/gaps behind a "Full analysis" toggle
  (feasibility scorecard + gaps-vs-strengths chips).

tsc -b + backend py_compile clean; smoke tests verified (opencode 10/10, glue 9/9).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 21:37:59 +05:30
Ajay SableandClaude Opus 4.8 8895bb9f4a feat(studio): notifications (the bell) + department-scoped RBAC (7-role model) + admin consolidation
Notifications (models/notification.py, services/notification_service.py,
routers/notifications.py):
- The bell — a notification lands for the content owner (+ thread participants)
  when someone likes/comments on their post or a board/brief/trend they shared.
  Self-actions don't notify; ownerless system events (ranges/orders) don't spam.
- Announcements (admin/management-gated) fan out to every active user and post
  to the feed with a 📢.
- Frontend: NotificationBell in TopNav (unread badge + dropdown) + an Announce
  toggle in the feed composer. Verified smoke_test_notifications 8/8.

Department-scoped RBAC (services/rbac.py):
- One role per department managing only its own features on top of the common
  surfaces (library/boards/ingest): Admin (global) · Designer · Merchant ·
  Planner · Auditor · Leadership · Viewer. Users/Data stay Admin-only.
- Seed keeps code-owned system roles in sync and retires removed ones (0-user
  guard). Personas map to the roles (feed_digest._persona).
- A Designer sees only the design + common nav; shared boards open for all
  (common) but shared briefs/trends stay department-gated.

Admin consolidation (Admin.tsx, routers/users.py):
- Users tab Role/Department become read-only labels (Status + Actions remain);
  the Roles tab "add someone" now sets role + department together.

Verified: feed 31/31, notifications 8/8, playwright 13/13; tsc -b + build +
py_compile clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dehx6goqDV2TiQ92dFFWGP
2026-07-24 12:29:19 +05:30
Ajay SableandClaude Opus 4.8 a37612246e docs: home-feed/design-library plans + WFX OC API dummy-JSON review
- design-library-feed.md / design-library-plan.md: the "OS for design" home-feed
  plan + reconciliation.
- wfx-oc-api-dummy-review-2026-07-16.md: field-by-field review of WFX's dummy JSON;
  grain settled (OC = whole order, deliveries nested), StyleID/OrderCode added,
  history via date-range, docs via temporary per-OC SharePoint URL (download+keep),
  Saved-OC handling = Pratik's Option A (track from email, lot number links later).
- STATUS.md + tna-governance-platform-plan.md: reflect the above.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dehx6goqDV2TiQ92dFFWGP
2026-07-24 10:59:31 +05:30
Ajay SableandClaude Opus 4.8 6817016761 feat(feed): SAPL Home event-feed — curated cross-OS studio feed + KAARIGAR daily brief + share-to-studio
Replaces the static Library landing with a familiar (Facebook/LinkedIn-style)
home feed that surfaces genuinely interesting activity across the whole OS,
curated so routine uploads don't flood it.

Feed (services/feed_service.py + routers/feed.py):
- Derived-on-read events: user posts, TNA-approved moments, boards, moodboard
  briefs, trend reports, big library/fabric drops, weekly WFX new-order digest,
  review milestones. Interestingness scoring suppresses routine uploads unless
  engaged (reactions/comments rescue them).
- Interactions: react / comment / save + user posts (new tables feed_reactions,
  feed_comments, feed_saves, feed_posts — auto-create on boot).
- Cursor pagination + "Load more".

KAARIGAR Daily Brief (services/feed_digest.py, GET /api/feed/brief):
- Persona-aware "what matters today" from Stitch/TNA — designer/planner/merchant/
  quality/fabric/intern see role-relevant items; admin sees the full studio view.

Share-to-studio (models/feed_share.py, POST/GET /api/feed/share):
- Boards / moodboard briefs / trend reports are private by default; owner
  publishes to the feed. Shared items get read-through access (collections/
  moodboard/scraper detail GETs). "Share to studio" button on all three pages.

Frontend: pages/Home.tsx (3-col profile / feed / SAPL-Today+activity),
ShareToStudioButton, client methods; / rewired to the feed, Library -> /library
(old /?facet deep-links redirect). Verified: smoke_test_feed 31/31 +
playwright_check_feed 13/13; tsc -b + build + py_compile clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dehx6goqDV2TiQ92dFFWGP
2026-07-24 10:59:18 +05:30
Ajay SableandClaude Opus 4.8 ca50b67921 feat(tna): Sprint-0 data trust + worklist UX — corpse filter, plain names, pagination
Sprint-0 items from Pratik's governance direction (all zero-decision, unblocked):
- Corpse-archive filter: services/wfx_service.orders_without_tna hides orders >120d
  overdue (CORPSE_MAX_OVERDUE_DAYS, exclude_corpses param) so red stays meaningful —
  scoped to /tna/no-tna (all-orders unaffected); response returns an `archived` count.
  Also raised the /no-tna cap to 500 so the worklist has the full set to paginate.
- Plain-language milestone names: utils/tnaLabels.milestoneLabel maps raw Stitch jargon
  ("Bulk Flow Start IH Date") + Category-template names to merchant-readable labels;
  applied to Create-TNA, LifecycleTimeline, and PERT (raw kept as a hover tooltip).
- Orders needing a plan: 5-per-page pagination (urgency bands become inline headers)
  + an "N archived" note next to the count.
- docs: governance platform plan (synthesis of the 9-Jul blueprint) + STATUS update.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 12:17:47 +05:30
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