SuperSeller3000/CLAUDE.md
Simon Kuehn 31c5116a1b docs: add CLAUDE.md and Plan 7 (eBay admin navigation + business policies)
Plan 7 covers: ArticleTypePlatformConfig policy fields, EbayAccountApiClient,
EbayPolicyProvider with live dropdown choices, EbayAdapter reading real config,
and per-adapter admin navigation section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 06:25:14 +00:00

5.5 KiB
Raw Permalink Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

All PHP commands run inside Docker. The app container is named app.

# Unit tests
docker compose exec app php vendor/bin/phpunit tests/Unit/

# Single test file or method
docker compose exec app php vendor/bin/phpunit tests/Unit/Domain/Article/ArticleTest.php
docker compose exec app php vendor/bin/phpunit --filter testSomeMethod

# Integration tests (loads .env.local secrets automatically)
bin/test-integration
bin/test-integration tests/Integration/Channel/EbayAdapterTest.php

# PHPStan (level 9 — must be clean before committing)
docker compose exec app php vendor/bin/phpstan analyse

# CS Fixer (dry-run to check, no --dry-run to fix)
docker compose exec app php vendor/bin/php-cs-fixer fix --dry-run --diff
docker compose exec app php vendor/bin/php-cs-fixer fix

# Migrations
docker compose exec app php bin/console doctrine:migrations:migrate --no-interaction
docker compose exec app php bin/console doctrine:migrations:diff   # generate from entity changes

# Cache
docker compose exec app php bin/console cache:clear

# Create first user / API key
docker compose exec app php bin/console app:users:create
docker compose exec app php bin/console app:api-keys:create

Architecture

Hexagonal architecture: Domain → Application → Infrastructure. The boundary is enforced by convention and PHPStan.

  • src/Domain/ — pure PHP, zero framework imports. Entities, enums, value objects, repository interfaces. Doctrine attributes on entities are the pragmatic exception.
  • src/Application/ — use cases, message handlers, service interfaces (ports). Orchestrates domain via interfaces only.
  • src/Infrastructure/ — all framework/external-system code: Doctrine repositories, Symfony controllers, Messenger handlers, channel adapters, AI clients.

Dependency Injection

Every Domain repository interface is aliased to its Doctrine implementation in config/services.yaml. All Application interfaces (ports) are aliased there too. When adding a new interface+implementation pair, add the alias manually — autowiring alone won't resolve interfaces.

Channel adapters are collected via tagged_iterator app.channel_adapter into ChannelAdapterRegistry. Tag new adapters in services.yaml.

Routing (Symfony 8 gotcha)

routing.controllers auto-discovers all controllers. API controllers must declare the /api prefix in their class-level #[Route] attribute — a yaml prefix: on top of auto-discovery is silently ignored.

AI Pipeline

The AI backend is MistralClient (vision: pixtral-12b, text: mistral-large). The interface is named OllamaClientInterface for historical reasons — the services.yaml alias points it to MistralClient. Web search uses TavilyWebSearch behind WebSearchInterface.

Pipeline A (photo) chains Messages sequentially — each handler dispatches the next: PhotoUpload → SpecsResearch → JsonCoding → Validation → DraftArticle → EbayText

After vision, findCompletedByModelNumber() checks the DB for a cache hit and skips the remaining AI steps if found.

PipelineJobFailureListener catches WorkerMessageFailedEvent after all retries are exhausted and sets AIPipelineJob.status = failed.

Messenger Transports

Three isolated Redis streams — a failing worker never blocks the others:

Transport Worker service Retries Delay
ai_pipeline worker-ai 3 2 s ×2
orders worker-orders 5 1 s ×2
channel_sync worker-channel 5 2 s ×2, max 60 s

Exhausted messages land in failed transport (persistent). Replay with messenger:failed:retry.

eBay Integration

EbayAdapter implements ChannelAdapterInterface. It uses:

  • EbayInventoryApiClient — inventory items, offers, publish/withdraw, stock updates, tracking
  • EbayFulfillmentApiClient — order fetching
  • EbayOAuthClient — Client-Credentials token with cache.app caching
  • EbayTaxonomyService — category/aspect lookup, also cached

ArticleTypePlatformConfig holds per-ArticleType eBay settings (category ID, business policy IDs). ArticleTypeEbayMapping maps each eBay aspect name to either an Article field (SOURCE_ARTICLE_FIELD) or an AttributeDefinition (SOURCE_ATTRIBUTE). EbayAdapter.buildAspects() reads from this table.

EasyAdmin (Admin Panel)

DashboardController carries #[AdminDashboard] and configureMenuItems(). All CRUD controllers in src/Infrastructure/Http/Controller/Admin/ are auto-discovered. Menu items reference controller class names directly via MenuItem::linkTo().

PostgreSQL Schemas

Three schemas: app (all entities), logs (live log entries), logs_archive (rotated). doctrine.yaml sets schema_filter to exclude logs_archive.* and app.inventory_seq from migration diffs — never remove that filter.

Auth

Browser login: form + optional TOTP (scheb/two-factor-bundle). API access: X-Api-Key header — stored as bcrypt hash with prefix for lookup. PermissionVoter checks User.permissions and ApiKey.permissions (both jsonb) uniformly. Permission constants live in PermissionVoter.

Docker

All PHP services (app, worker-*, cron) run as user 1000:1000 with HOME=/tmp. Never run commands as root inside the container — it creates root-owned files on the host.

docker-compose.override.yml exists only for local dev (exposes Postgres/Redis ports). Do not use it on the production server.