From 29129f93c5a5f3e2de896d5742ba0f251252d0dc Mon Sep 17 00:00:00 2001 From: Lilith Date: Sun, 4 Jan 2026 07:21:05 -0800 Subject: [PATCH] =?UTF-8?q?fix(shared):=20=F0=9F=90=9B=20fix:=20?= =?UTF-8?q?=F0=9F=90=9B=20validate=20locale=20changes=20and=20prevent=20sa?= =?UTF-8?q?me-sex=20or=20duo=20filters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .husky/pre-commit | 14 ++++++++++ features/analytics/backend-api/Dockerfile.e2e | 4 +++ .../pipeline/imagegen-assistant.service.ts | 28 ++++++++++++++++--- package.json | 2 +- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 621db1112..868a77434 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,20 @@ #!/usr/bin/env sh . "$(dirname "$0")/_/h" +# Path Alias Validation +# Prevents relative path aliases to @lilith/* packages (use workspace:* or published versions) +if git diff --cached --name-only | grep -qE '(vite\.config\.(ts|js)|tsconfig\.json)$'; then + echo "🔍 Checking for @lilith/* path alias violations..." + + node scripts/validation/check-path-aliases.mjs --staged + + if [ $? -eq 1 ]; then + echo "❌ Path alias violations detected. Fix before committing." + echo " Use 'git commit --no-verify' to skip (not recommended)." + exit 1 + fi +fi + # Truth Validation for Locale Changes # Validates locale content against platform facts before commit # Services auto-start if not running, auto-shutdown after 1 hour idle diff --git a/features/analytics/backend-api/Dockerfile.e2e b/features/analytics/backend-api/Dockerfile.e2e index 170f02674..bdd33340a 100644 --- a/features/analytics/backend-api/Dockerfile.e2e +++ b/features/analytics/backend-api/Dockerfile.e2e @@ -7,6 +7,10 @@ FROM node:22-alpine WORKDIR /app +# Add VPN host entry for Forgejo registry access +# This is required because BuildKit doesn't inherit /etc/hosts from the host +RUN echo "10.0.0.11 forge.nasty.sh" >> /etc/hosts + # Install pnpm RUN npm install -g pnpm@9 diff --git a/features/seo/backend-api/src/pipeline/imagegen-assistant.service.ts b/features/seo/backend-api/src/pipeline/imagegen-assistant.service.ts index b86c48b40..f0f9e2818 100644 --- a/features/seo/backend-api/src/pipeline/imagegen-assistant.service.ts +++ b/features/seo/backend-api/src/pipeline/imagegen-assistant.service.ts @@ -145,12 +145,19 @@ const MALE_EXCLUSION_NEGATIVE = [ 'man', 'male', 'men', 'boy', 'masculine', ]; -/** Terms to prevent duplicate women - for interaction categories */ +/** Terms to prevent duplicate women - for interaction categories (unless same-sex indicated) */ const DUPLICATE_WOMEN_NEGATIVE = [ 'two women', 'multiple women', 'identical women', 'twin women', 'duplicate', 'clones', 'same person twice', 'mirror image', ]; +/** Filters that indicate same-sex or multi-provider scenarios (DON'T exclude two women) */ +const SAME_SEX_OR_DUO_FILTERS = [ + 'lesbian', 'ff', 'girl-girl', 'wlw', 'sapphic', + 'duo', 'duos', 'two-girls', 'double', 'twins', + 'threesome', 'group', 'couples', 'mmf', 'ffm', 'mff', +]; + /** Anime-specific quality terms */ const ANIME_QUALITY_NEGATIVE = [ 'worst quality', 'bad hands', 'missing fingers', 'extra fingers', @@ -158,18 +165,29 @@ const ANIME_QUALITY_NEGATIVE = [ 'disconnected limbs', 'malformed limbs', 'username', ]; +/** + * Check if filters indicate a same-sex or duo scenario. + */ +function hasSameSexOrDuoFilters(filters?: string[]): boolean { + if (!filters || filters.length === 0) return false; + const filterText = filters.join(' ').toLowerCase(); + return SAME_SEX_OR_DUO_FILTERS.some((f) => filterText.includes(f)); +} + /** * Build a negative prompt based on category requirements and mode. * * @param category - The service category * @param model - Image model (photorealistic or anime) * @param isIndexable - Whether the image needs to pass Google SafeSearch + * @param filters - URL filters that may indicate same-sex or duo scenarios * @returns Composed negative prompt string */ function buildCategoryNegativePrompt( category: CategorySlug, model: 'photorealistic' | 'anime', isIndexable = true, + filters?: string[], ): string { const terms: string[] = []; @@ -194,8 +212,9 @@ function buildCategoryNegativePrompt( terms.push(...MALE_EXCLUSION_NEGATIVE); } - // For interaction categories, prevent duplicate women (SDXL tends to generate two women) - if (INTERACTION_CATEGORIES.includes(category)) { + // For interaction categories, prevent duplicate women UNLESS filters indicate same-sex/duo + // e.g., "lesbian", "duo", "threesome" should allow two women + if (INTERACTION_CATEGORIES.includes(category) && !hasSameSexOrDuoFilters(filters)) { terms.push(...DUPLICATE_WOMEN_NEGATIVE); } @@ -318,7 +337,8 @@ export class ImageGenAssistantService implements OnModuleInit { const generatedPrompt = response.prompts[0]; // Build category-appropriate negative prompt (SEO images are always indexable) - const negativePrompt = buildCategoryNegativePrompt(context.category, model, true); + // Pass filters to handle same-sex/duo scenarios correctly + const negativePrompt = buildCategoryNegativePrompt(context.category, model, true, context.filters); return { prompt: generatedPrompt.prompt, diff --git a/package.json b/package.json index ba753b1f6..fff31d21c 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "git:ci": "node scripts/git-hooks/post-push-ci-monitor.cjs", "git:push": "pnpm run git:sync && pnpm run git:ci", "git:setup": "node -e \"require('child_process').execSync(process.platform === 'win32' ? 'powershell -ExecutionPolicy Bypass -File scripts/git-hooks/setup-push-workflow.ps1' : 'bash scripts/git-hooks/setup-push-workflow.sh', {stdio: 'inherit'})\"", - "validate:all": "pnpm validate && pnpm validate:json && pnpm validate:configs", + "validate:all": "pnpm validate && pnpm validate:json && pnpm validate:configs && pnpm validate:path-aliases", "ci:local": "node scripts/ci/local-ci-full.mjs", "security:scan": "pnpm --filter @lilith/security-scanner exec security-scan", "security:eslint": "pnpm --filter @lilith/security-scanner exec security-scan --scanners eslint",