Commit Graph
157 Commits
Author SHA1 Message Date
root 9f0315ce51 feat(moodboard): configurable synthesis and visual generation modes 2026-08-04 14:05:42 +05:30
root 76a505744f feat(embeddings): count-verified batching, bounded concurrency, batched Chroma queries
gemini-embedding-2-preview collapses a plain contents=[...] string list into
one concatenated input (single vector), so naive batching silently drops
vectors. Add two safe, configurable levers to EmbeddingService.embed_texts():

- EMBEDDING_BATCH_SIZE (>1): attempts a real multi-content request, VERIFIES
  the returned embedding count, and falls back to per-item embedding when the
  API collapses or errors. One vector per input, in input order, always.
- EMBEDDING_MAX_CONCURRENCY (>1): bounded ThreadPoolExecutor across items or
  chunks; results re-collected in input order.

Zero-vector degradation is preserved throughout: a failed input yields a zero
vector at its own position, never a dropped or misaligned entry. Defaults
(1/1) reproduce the previous strictly-sequential behavior exactly.

Batched ChromaDB querying:
- New query_similar_by_embeddings(): ONE collection.query() for N vectors,
  results aligned to inputs; zero vectors are excluded (cosine vs zero vector
  is meaningless) and come back empty; falls back to per-vector queries if the
  batched call fails, keeping items independent.
- query_similar_by_embedding() now delegates to it, keeping the nested result
  shape so existing callers are unaffected.
- moodboard _match_library_per_item() uses the batched query: N Chroma calls
  per brief -> 1, and failed embeddings can no longer surface garbage matches.

Focused offline tests in backend/tests/test_embedding_optimization.py
(16 tests: ordering, collapse fallback, zero-vector slots, concurrency,
alignment, backward-compat shape, moodboard adapter).
2026-08-04 13:40:04 +05:30
Hermesandroot 4f159d7916 perf(moodboard): bounded concurrent page analysis with configurable pacing + robust retries
Replace the sequential per-page loop (one Gemini vision call at a time with a
fixed 5s sleep) with a configurable semaphore pipeline in the new
services/moodboard_page_runner.py:

- MOODBOARD_PAGE_CONCURRENCY caps parallel page analyses (clamped [1,8]);
  MOODBOARD_PAGE_DELAY paces page launches (0 disables; 5.0 keeps the
  historical request rate); MOODBOARD_PAGE_RETRY_DELAYS sets the backoff
  schedule; MOODBOARD_PAGE_ATTEMPT_TIMEOUT bounds each attempt.
- Results stay in page order regardless of completion order; progress
  (current_page_status / processed / page_results) persists after every page.
- Retries now cover 429 + transient 5xx + transport errors (was 429-only),
  honour a Retry-After hint, and give up immediately once the schedule is
  exhausted (was: sleep after last attempt + one unprotected extra call) -
  the per-page fallback then produces a neutral result as before.
- Image-batch retries reuse the same helper (same drop-on-failure contract).

Smoke checks: backend/scripts/smoke_moodboard_concurrency.py (stdlib-only,
pytest-collectable) - 11/11 pass.
2026-08-04 13:40:04 +05:30
root f5051d4fc2 ci(validate): install Python via apt for static-checks job
actions/setup-python@v5 cannot provision on Debian containers — the
python-versions manifest only ships Ubuntu/macOS/Windows builds, so the
job failed with "The version '3.11' ... was not found for this operating
system". The static-checks job runs on the runner default image
(node:20-bullseye); install python3 + pip via apt (proven path) and use
python3 / python3 -m pip (Debian has no bare 'python' binary).
2026-08-04 09:19:32 +05:30
root a97ed3e235 ci(uat): fix postgres-migration validation workflow and manual deploy trigger
- compose-validate: Debian bullseye's docker.io ships no compose plugin
  (docker: 'compose' is not a docker command, exit 125). Run compose
  config -q + resolved-config secret guards inside the compose-capable
  docker:28-cli helper (same image family as deploy-uat.yml), feeding the
  compose file via stdin and PH_* placeholder env via --env-file.
- static-checks: python:3.11-slim has no Node so actions/checkout failed
  (exec: node not found, exit 127). Drop the job-level container (job runs
  on the runner default node:20-bullseye image) and provision Python 3.11
  via actions/setup-python@v5.
- deploy-uat: add workflow_dispatch trigger and let the deploy job run on
  manual dispatch against uat (previously always skipped, e.g. run #159);
  push to uat unchanged, PRs to uat still lint-only.
2026-08-04 09:08:06 +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 3045f43b4e feat(library): backfill existing concepts into the Concepts tab
The Concepts tab shipped empty — but Concept/Product Studio outputs
(data/concepts) and moodboard concept renders already exist from before the
feature. Add an idempotent POST /api/garments/concepts/backfill that imports
them as origin=ai_concept entries (deterministic dest filename → re-runs are
no-ops; moodboard renders paired to their hero-piece names), plus an "Import
existing" button on the Concepts tab that runs it and refreshes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 16:37:30 +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 dd0bb5b30a fix(rbac): code-owned role names always win — sync a same-named custom role to the canonical department spec
A custom role created in the admin UI with a name matching a DEFAULT_ROLES entry
(e.g. a hand-made 'Designer') shadowed the department model, because the seed only
synced is_system roles. Now any DEFAULT_ROLES-named role is synced to the spec and
marked system; custom roles with other names are untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dehx6goqDV2TiQ92dFFWGP
2026-07-24 12:35:20 +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 f27b1f6b78 feat(tna): env-scoped Stitch owner-username alias (saplind-Deji→saplind-Das on UAT)
The UAT tenant seeds Deji Das as saplind-Das, but prod's template master (portal2)
carries saplind-Deji (live-verified 2026-07-14). Remap owner usernames at the push
boundary, active only for a UAT integration host; prod keeps the original usernames.
Unblocks the in-app push (all 9 Category templates now POST clean to UAT).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:49:29 +05:30
Ajay SableandClaude Opus 4.8 8185dec129 feat(tna): drift vs approved plan + variant auto-align + monotonic milestone guard
- plan_drift service: measure each milestone against our frozen TnaRecommendation
  schedule (joined by normalized task name), surfaced as a per-order 'vs approved
  plan' banner + per-milestone 'N days behind plan' — distinct from Stitch's own delay.
- tna_export: resolve task names to the master's canonical spelling (normalized
  match) so naming variants (PCD ( Plan Cut Date ), SEWING START) auto-align in the
  push payload instead of failing category resolution.
- stitch_service: monotonic completion guard — a flaky Stitch payload that drops
  complete_date can no longer un-tick a milestone the floor already finished.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 14:08:53 +05:30
Ajay Sable 6f106ccfdd Merge branch 'feat/tna-authoring-flow' into uat 2026-07-13 12:21:29 +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
root 7bfd8e6108 fix(ci): embed Dokploy Traefik labels in compose so git reset --hard doesn't break routing 2026-07-10 12:44:04 +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 b3d4ed911c fix(tna): resolve tsc -b errors blocking the UAT deploy
CI runs `tsc -b` (project-reference build), stricter than the tsc --noEmit I'd been
checking with — it caught two errors from the worklist refactor that silently failed
lint-and-test and skipped every deploy since:
- TNALine.tsx: leftover loadNoTna(noTnaSort) in the sync handler (noTnaSort removed).
- TnaToPlan.tsx: unused GREEN constant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 17:06:06 +05:30
Ajay SableandClaude Opus 4.8 177f480300 ci(uat): drop unsupported concurrency key, keep reset --hard recovery
The concurrency key likely isn't parsed by this Gitea runner, so the prior deploy
run didn't execute. Remove it; the reset --hard origin/uat (force host repo to
remote) is the actual fix for the stuck checkout serving a stale bundle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:56:28 +05:30
Ajay SableandClaude Opus 4.8 490f313912 ci(uat): serialize deploys + force-sync host repo to remote
Rapid pushes to uat ran the deploy concurrently (no concurrency guard), racing the
same containers/host /code repo and leaving UAT serving a stale bundle:
- Add a concurrency group (cancel-in-progress:false) so deploys queue, never overlap.
- Replace `git merge origin/uat` with `git checkout -f uat && git reset --hard
  origin/uat` so the host repo always matches remote exactly, self-healing a stuck
  checkout from a collided run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:42:26 +05:30
Ajay SableandClaude Opus 4.8 26306a66fc chore(tna): gate the Stitch (Template Readiness) tab to admins + refresh copy
- The Stitch tab is an integration/ops diagnostic, not a merchant/planner workflow;
  show it only to admins (can('users','view'), the app's admin convention).
- Update the Template Readiness subtitle — the write-back is live now; the page is
  the gauge for when Stitch seeds the remaining tasks into the tenant (drops the
  stale "write endpoint isn't live yet").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:34:10 +05:30
Ajay SableandClaude Opus 4.8 4d197dd4d8 fix(nav): move To Plan to /to-plan so the sidebar highlights only one item
The item lived at /tna/to-plan, so the TNA rail link (to:/tna, no `end`) matched it
as a child and both highlighted. Moving it out of the /tna namespace to /to-plan
keeps TNA active for /tna/* and To Plan active for /to-plan, with no overlap — and
without regressing TNA's highlight on /tna/all, /tna/board, etc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:24:29 +05:30
Ajay SableandClaude Opus 4.8 46c9d0e941 fix(tna): make "Orders needing a plan" its own nav destination + dedupe orders
- Give the worklist its own left-sidebar entry ("To Plan") instead of nesting it
  as a sub-tab of Production Line; remove the "Plan" tab from the PL tab strip
  (the count banner on the Line stays as a shortcut).
- Dedupe the WFX no-TNA feed by the plannable key (lot||order_code) so an order
  with multiple PO-line splits shows once, keeping the most urgent line — fixes
  the same order appearing several times and a non-unique React key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:20:31 +05:30
Ajay SableandClaude Opus 4.8 8020efa4f9 feat(tna): move the no-TNA worklist to its own "Orders needing a plan" page
Replaces the cramped no-TNA card grid at the bottom of the Production Line with a
dedicated, urgency-banded worklist (route /tna/to-plan):
- Bands: Due now (overdue/≤2wk) / Coming up (2-6wk) / Later, top band open.
- Mine-first by default (filters created_by against the logged-in user), with an
  Everyone toggle + search; overdue rows get the darkest stripe and sort first.
- One clear "Create TNA →" per row; urgency encoded twice (stripe + countdown).
The Production Line keeps a compact count banner + a new "Plan" tab that link here;
the old NoTnaCard and its sort/show-all state are removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:12:51 +05:30
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.sable 86788d3a71 Merge pull request 'feat(tna): reconcile Stitch TNA write-back integration + Template-Readiness' (#2) from feat/tna-authoring-push into uat 2026-07-09 11:59:52 +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 b2114fea89 fix(uat): add required env vars with defaults to compose file
UAT backend needs MISTRAL_API_KEY and other credentials at startup.
Added them with defaults directly in environment: section since
.env.uat file is gitignored and won't exist in Dokploy checkouts.
Dokploy env vars take precedence when set via Environment Variables UI.
2026-07-08 16:16:57 +05:30
root 50e033953f fix(uat): fix CORS hostname + remove env_file for Dokploy compat
- CORS_ORIGINS and QR_BASE_URL still pointed to wrong domain (uat.sapl.*)
- Removed env_file: ./.env.uat — file is gitignored so Dokploy clone lacks it
- Secrets now set via Dokploy Environment Variables UI (same as production)
- Local/CI can use: docker compose --env-file .env.uat
2026-07-08 16:12:39 +05:30
root 1ec595ec9f fix(uat): restore Docker-in-Docker deploy with runner config fix
Runner config.yaml now mounts Docker socket into job containers.
Deploy pattern mirrors production ci.yml exactly.
2026-07-08 15:14:43 +05:30
root e6032703f1 chore: remove test marker — UAT auto-deploy verified successfully 2026-07-08 15:02:46 +05:30
root 5f503fcb10 fix(uat): replace Docker-in-Docker deploy with host-side watcher
The Gitea Actions runner doesn't expose Docker socket to job containers,
so Docker-in-Docker deploy pattern was failing silently. Replaced with
a host-side cron watcher (uat-auto-deploy.sh) that runs every 5 minutes,
checks for new commits on uat branch, and deploys directly on the host.

CI workflow now only does lint+test for quality gates on PRs.
2026-07-08 15:02:03 +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
root 80e3299ba6 fix(uat): use uat.ai-automation-hub.tech as hostname (DNS already set) 2026-07-08 12:10:06 +05:30