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>
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>
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>
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>
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>
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
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
- 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
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>
- 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>
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>
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>
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>
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>
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>
- 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>
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>
- 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>
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>
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>
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.
- 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
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.
- .env.uat.example: template showing UAT-specific env vars
- .env.uat added to .gitignore (contains secrets)
- Real .env.uat lives at /etc/dokploy/compose/sapl-sapl-uat/code/
- docker-compose.uat.yml: separate UAT stack with sapl_uat_data volume
and uat.sapl.ai-automation-hub.tech Traefik routing
- deploy-uat.yml: auto-deploys UAT stack on push to uat branch
Production stack (sapl-sapl-am5vak) untouched — existing ci.yml
continues to deploy main to production as before.
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>
nginx cached the backend container's IP at startup (proxy_pass http://backend),
so every backend redeploy (new container IP) left it dialing the old dead IP →
"upstream timed out while connecting" → 504 on all /api calls until the frontend
was manually restarted.
Add Docker's resolver (127.0.0.11) + a variable proxy_pass so nginx re-resolves
the backend every ~10s. Also fail fast on a truly-down backend (connect 10s) while
allowing long AI calls (read 300s). Makes the stack self-heal across deploys.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The compose environment override didn't reliably reach the backend container on
Dokploy (env-only redeploy didn't rebuild/recreate). Setting the PUBLIC OAuth
client id as the config.py default guarantees /auth/config returns it — the code
change forces a backend rebuild, and the value is compiled in. Env still overrides.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "Sign in with Google" button renders only when /auth/config returns a
google_client_id. Prod's Dokploy env never had GOOGLE_CLIENT_ID set, so the
button was hidden on sapl.ai-automation-hub.tech (visible locally via .env).
Add it to the compose environment block. The client ID is a public identifier
(already exposed to the browser), so it's safe to commit; the client secret is
not needed by the ID-token verification flow and stays out of the repo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Rename the in-app assistant to KAARIGAR (the master artisan) and give it a
visual identity — the golden dress-form mark — across the app.
- System prompt identity: "You are KAARIGAR…"
- Ask command bar: button, input, thinking + error copy
- Top-nav: mark beside the SAPL wordmark
- Login: mark beside the "Sign in" heading (desktop + mobile)
- Favicon: browser tab + iOS icon (replaces the placeholder bolt)
- Enlarge the sign-out door icon for visibility
Internal identifiers (route /api/hermes, filenames, types) unchanged — no
API path change, so this is a UI-only, zero-risk deploy.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>