Wire the autonomous engineering pipeline to operate on dev branch with
no human deploy gate. Pushes to git.semih.ai/root/sase.tr.git dev
trigger the Coolify webhook that redeploys https://dev.sase.tr — the
operator pulls signal from the live URL instead of approving every
merge.
Production (main → sase.tr) stays human-driven: agents never push to
main directly; promotion is an explicit operator decision.
Critical-path policy on dev relaxes for auth/payments/billing/
subscription so the loop can ship fixes there with a regression test;
migrations + dep/config changes still need human ack on dev because
they desync persisted/shared state.
Adds team-charter.md (dev/main topology, severity matrix, routing
rules, council protocol, loop detection) and MEMORY.md updates that
agents consult on every triage. Also gitignore'd fusion's runtime
SQLite/task state so .fusion/agents and .fusion/memory remain the
only authored docs in the tree.
Run 'biome check --fix' on apps/api/src and apps/web/src to clear
the safe-fixable lint backlog (151 files: parseInt → Number.parseInt,
isNaN → Number.isNaN, organize imports, etc.). 769 errors remain
that require manual changes (mostly noExplicitAny).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI lint step failed with "biome: not found" because the binary was
never declared in any workspace. Pin to 1.9.4 to match the schema
version referenced in biome.json.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pnpm/action-setup@v4 errors when both action input "version" and
package.json "packageManager" are set. Drop the action input so
package.json remains the single source of truth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- CATALOG_MAP'e ~40 yeni WMI eklendi (Mitsubishi, Nissan, Volvo, vb.)
- brandFromCatalogCode listesi 55 DB kataloğuna genişletildi
- brand="UNKNOWN" olsa bile catalogCode varsa EMEX sonucu kabul edilir
- emex-catalog: linkPath'li kategorilerde parçalar DB'den yüklenir (scraper bug fix)
- VIN cache: emex_vehicle_vins tablosu ile tekrar VIN'lerde emexdwc.ae atlanır
- getCategoryTree Step 0: VIN cache lookup (~10ms)
- Match sonrası otomatik VIN cache doldurma
- Proaktif cache: ilk decode sonrası matchByName + saveVinCache
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Old queries cached empty arrays for groups and schemas. Bumping cache
key prefix from "pcat:" to "pcat2:" so new correct queries bypass stale
entries without needing manual Redis flush.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
car_groups junction table only has data for 2K of 68K cars. Derive groups
from schema_images.category_id which covers 92.6% of records. Also fix
getSchemaImages to filter by category_id instead of empty group_id.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Expand pcat API with getCars, getCarGroups, getSchemaImages, getSchemaDetail
endpoints. Add frontend routes for the complete flow:
catalog → model → car → groups → schema+parts with hotspots.
Reuses existing SchemaViewer component for interactive part diagrams.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Import parts-catalogs.com dump into pc schema (72 catalogs, 2788 models,
134K groups). Add pcat-catalog service/controller with Redis caching.
Create frontend routes for catalog → model → group navigation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Expand catalog tabs from 2 to 5. Rename existing PL24/Emex labels,
add Sase.Tr, Pcat, Tecdoc as coming-soon placeholders.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add CarBrandLogo component with SVG assets for 11 brands (Alpine, Citroën,
Cupra, Dacia, MAN, Opel, Peugeot, Renault, SEAT, Škoda, Suzuki); falls back
to logoUrl prop then initial-letter avatar
- Use CarBrandLogo across brand-selector, catalog index, subscription page,
dashboard home, and vehicle detail page
- PL24: pre-download schema image buffer while auth headers are fresh to avoid
token expiry on cached URLs; strip buffer before Redis caching
- PL24: fetchVehicleList tries de account first, falls back to main token
- DB: widen catalogVehicles unique index to include serviceName column
- CSS: add Tailwind v4 dark variant declaration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Refactor VIN decode chain to launch PartsCatalogs and EMEX in parallel;
EMEX is race-capped at 3 s — if it doesn't resolve in time, PL24 takes over
- Decision tree: pcat=1 → use pcat; else await EMEX result; else PL24; else candidates
- Add "Member Since" (Kayıt Tarihi) field to profile settings, sourced from user.createdAt
- Add createdAt to auth store User interface
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- vehicles.service.ts: remove Corgi from resolveVin chain; go straight
to PartsCatalogs → PL24 → EMEX without offline WMI lookup
- End condition simplified: return null only when EMEX also fails (no
corgiKnown fallback)
- Add scripts/test-emex-http.mjs: proxy timing test for EMEX HTTP decode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When EMEX DB has no vehicle match (Ford EU, Mercedes etc.), instead of
falling back to full on-demand VIN decode (~8s), use the VIN-decode SSD
directly with QuickGroups.aspx?c={catalog}&vid=0&ssd={vinSsd} (~2-3s).
Tested: Ford Focus CB4 VIN SSD returns 376 categories via QuickGroups.
New step 3.5 in the cascade:
1. pathData → matchByName (DB-only, ~50ms)
2. emexVehicleName → matchByName (DB-only, ~50ms)
3. matchBySsd wizard walk (DB, ~2-3s)
3.5. VIN SSD → QuickGroups shortcut (~2-3s) ← NEW
4. Fallback → full on-demand scrape (~8s)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
VIN-decode SSDs are too long/specific — EMEX returns HTML instead of
JSON when passed to GetWizard2. New approach:
1. Start from root SSD ("")
2. Advance through wizard steps (Europe → Passenger → ...)
3. Find pathData name (e.g. "Focus CB4 2008-2011") in model options
4. Use getWizardVehicles for DB matching
For Ford Focus: wizard finds it at step 3, but Ford DB has no EU
models so it still falls back to on-demand scrape. BMW/Renault with
good DB coverage will benefit from this flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The VIN-decode SSD is model-level, not variant-level. matchBySsd now:
1. Calls GetWizard2 with VIN SSD → gets sales designation options
2. Finds option matching pathData (e.g. "Focus CB4 2008-2011")
3. Uses that option's key SSD for Vehicles.aspx?ft=findByWizard2
4. Matches motor/variant names against DB
Note: Ford Focus still falls back to on-demand scrape because Ford
DB coverage is only 55 US models (no EU models like Focus/Fiesta).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Temporarily disable PartsCatalogs and PL24 decode steps to test
EMEX-only VIN resolution speed and reliability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When EMEX returns candidates, show VehicleSelectModal for selection.
When EMEX returns a single vehicle, show vehicle info card and
auto-navigate to the category page via /vehicles/decode.
Failed results still show JSON output as before.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ConfigService returns empty string (not the default) when env var
exists but is empty. Add || fallbacks in code and set proper defaults
in docker-compose to prevent invalid proxy URL like http://user:pass@:10000.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Node's global fetch doesn't support the dispatcher option. Use
undici's exported fetch when proxy is configured, which properly
handles ProxyAgent as a dispatcher.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add EMEX_USE_PROXY, EMEX_PROXY_HOST, EMEX_PROXY_USER, EMEX_PROXY_PASS
to both api and worker services so the proxy config reaches the
containers at runtime.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
undici was in devDependencies, causing MODULE_NOT_FOUND crash in
production where only prod deps are installed. Move to dependencies
since emex.service.ts imports ProxyAgent at the top level.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EMEX server blocks direct server IP requests (geo/bot protection),
causing all HTTP-based VIN decodes to return empty results. Add proxy
support to fetchEmexHtml() using undici ProxyAgent, reusing the same
EMEX_USE_PROXY / EMEX_PROXY_* env vars as the browser service.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
decodeVinOrCandidates returns { type: "notFound" } or { type: "error" }
instead of null, so data !== null was incorrectly marking these as
success. Now explicitly checks emex result type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
testService() was swallowing errors — services like parts-catalogs catch
internally and return null, so the outer try/catch never fired. Now each
service call has its own try/catch, and null responses get an explicit
error message.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The api client unwraps response.data automatically, which conflicts
with our testService response having its own 'data' field.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin-only page at /dashboard/service-test with dropdown to test
each VIN decode service (Corgi, PartsCatalogs, PL24, EMEX, VIN API)
individually or the full cascade. Results shown as JSON on page,
no DB writes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Backend: add `model` query param to wizard-vehicles endpoint
- Backend: 3-tier matching: exact name → name prefix → model fuzzy
- Frontend: simplified wizard UI, show determined params as tags
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace static DB-based dropdown chain with live wizard from emexdwc.ae.
The wizard API (GetWizard2) works without auth and shows all models/options
for each catalog (e.g. AU1587 now shows 103 Audi models vs 11 before).
Backend: proxy GetWizard2 + search DB for matching vehicles after wizard completes.
Frontend: step-by-step wizard with breadcrumb navigation, search, and reset.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Detect available filter fields from actual data per catalog
- Skip fields whose values are substrings of optionsRaw (e.g. driveType
redundant when optionsRaw="LEFT (GSX)")
- Skip fields that duplicate already-added filters (e.g. engine=LHD same
as driveType=LHD in Land Rover)
- Use optionsRaw as primary variant text in vehicle rows
- Works for all 54 catalogs: Renault (structured), Fiat, BMW, Volvo, etc.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dropdowns now auto-detect which fields have data (engine, body type,
transmission, drive type, fuel type, variant) and show only relevant
filters. Each dropdown cascades — selecting a value narrows options
for subsequent filters. Handles both structured options_raw (Renault)
and standard DB fields (Fiat, BMW, Volvo, etc).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Model → Engine → Gearbox dropdown chain narrows 4000+ vehicles down
to a manageable selection. Parses options_raw key:value pairs for
catalogs that have structured data (Renault). Falls back to engine/
transmission fields for others (BMW, Volvo, etc).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parse emexdwc.ae QuickGroups hierarchy and create a static gid→path mapping
(384 groups). Groups page now shows collapsible tree (Body > Vehicle Front >
Bumper/Parts) instead of flat list. Unmapped groups fall back to flat display.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace stacked images + plain table with interactive SchemaViewer (same as PL24).
Backend transforms response to SchemaViewer-compatible format (Part/SchemaPic types).
Add prev/next pagination for groups with multiple schema images.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redesign emex_* tables from vehicle-centric to catalog-centric structure with
junction tables (emex_vehicle_group_links, emex_vehicle_part_links). Migrate
92M+ rows from emex DB via postgres_fdw. Add EmexCatalogService for browse
and VIN pre-scraped matching, with Redis caching. Frontend catalog page now
has PL24/Emex tabs with full drill-down routes (brand → vehicle → group → parts).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Helmet's default CSP blocked images from storage.sase.tr (different
origin). Add it to img-src and connect-src directives.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Helmet's default CSP blocked images from storage.sase.tr (different
origin). Add it to img-src and connect-src directives.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move SPA fallback from main.ts catch-all (didn't work - NestJS 404
handler runs first) to HttpExceptionFilter. Now non-API GET 404s
serve index.html so client-side routing works for /dashboard/* etc.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move SPA fallback from main.ts catch-all (didn't work - NestJS 404
handler runs first) to HttpExceptionFilter. Now non-API GET 404s
serve index.html so client-side routing works for /dashboard/* etc.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NestJS was returning 404 JSON for SPA routes like /dashboard/search
because ServeStaticModule couldn't find a matching file and the request
fell through to NestJS router. Add explicit catch-all GET handler after
app.init() to serve index.html for non-API routes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NestJS was returning 404 JSON for SPA routes like /dashboard/search
because ServeStaticModule couldn't find a matching file and the request
fell through to NestJS router. Add explicit catch-all GET handler after
app.init() to serve index.html for non-API routes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Traefik was picking the wrong Docker network (_default) for the staging
api container, causing requests to hang. Force it to use the coolify
network which is shared with the Traefik proxy.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Traefik was picking the wrong Docker network (_default) for the staging
api container, causing requests to hang. Force it to use the coolify
network which is shared with the Traefik proxy.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Coolify adds its own Traefik labels when FQDN is set on the service.
This allows different domains per environment (sase.tr vs dev.sase.tr).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>