Bug Report
Severity: High (affects all orchestrator-mode tenants with sd_caption_enabled=true)
Component: publisher_v2/src/publisher_v2/services/ai.py
Related to: Issue #48 (fix was incomplete)
Discovered: 2026-01-03 during production testing for lbd.shibari.photo
Problem
When sd_caption_enabled=true, captions are generated using generate_with_sd() which uses SD-specific prompts (sd_caption_system_prompt, sd_caption_role_prompt). These are still being overwritten by static YAML even when the tenant provides custom system_prompt/role_prompt.
Issue #48 fixed the regular prompt precedence but missed the SD caption prompt case.
Root Cause
In CaptionGeneratorOpenAI.__init__() (ai.py lines 237-252):
# SD caption prompts: if tenant explicitly provided sd prompts, keep them
if cfg_sd_system: # False (orchestrator didn't provide explicit SD prompts)
self.sd_caption_system_prompt = cfg_sd_system
elif static_prompts.sd_caption.system: # True (YAML has a value)
self.sd_caption_system_prompt = static_prompts.sd_caption.system # ❌ OVERWRITES!
The problem: when orchestrator provides custom system_prompt but NOT explicit sd_caption_system_prompt, the code falls back to static YAML instead of inheriting from the custom system_prompt.
Evidence
Orchestrator config for lbd.shibari.photo:
{
"system_prompt": "Using the image analysis, write a tiny caption between 100-150 characters. 1. Wrap the message in ✨ sparkles ✨...",
"role_prompt": "You are a classy, fun, and playful 30-year-old woman posting on FetLife...",
"sd_caption_system_prompt": null,
"sd_caption_role_prompt": null,
"sd_caption_enabled": true,
"sd_caption_single_call_enabled": true
}
What gets used (from static YAML):
sd_caption:
system: >
You are a fine-art prompt engineer. Produce Stable-Diffusion-ready prompts...
role: >
Write two outputs for the provided analysis and platform spec...
Result
Email subject: "What emotions do you feel when viewing intimate art like this? Share your thoughts!"
Missing: ✨ sparkles ✨, themed headers, no hashtags rule, "| Selfshot unless tagged" suffix
Expected Behavior
When tenant provides custom system_prompt/role_prompt but NOT explicit SD prompts:
- SD prompts should inherit from the custom tenant prompts
- Static YAML should only be used when tenant uses schema defaults
Proposed Fix
Change the SD prompt fallback logic to check if tenant has custom prompts:
default_cfg = OpenAIConfig()
# SD caption prompts: inherit from tenant prompts if they're custom
if cfg_sd_system:
self.sd_caption_system_prompt = cfg_sd_system
elif self.system_prompt != default_cfg.system_prompt:
# Tenant has custom prompts - inherit them for SD too
self.sd_caption_system_prompt = self.system_prompt
elif static_prompts.sd_caption.system:
self.sd_caption_system_prompt = static_prompts.sd_caption.system
if cfg_sd_role:
self.sd_caption_role_prompt = cfg_sd_role
elif self.role_prompt != default_cfg.role_prompt:
# Tenant has custom prompts - inherit them for SD too
self.sd_caption_role_prompt = self.role_prompt
elif static_prompts.sd_caption.role:
self.sd_caption_role_prompt = static_prompts.sd_caption.role
Affected Code Locations
| File |
Lines |
Issue |
publisher_v2/src/publisher_v2/services/ai.py |
237-252 |
SD prompts don't inherit from custom tenant prompts |
Acceptance Criteria
Testing Notes
To reproduce:
- Configure tenant with custom
system_prompt and role_prompt but NO sd_caption_* prompts
- Ensure
sd_caption_enabled=true
- Generate caption → observe it doesn't follow custom prompts
Blocking: Production FetLife prompts not working
Bug Report
Severity: High (affects all orchestrator-mode tenants with
sd_caption_enabled=true)Component:
publisher_v2/src/publisher_v2/services/ai.pyRelated to: Issue #48 (fix was incomplete)
Discovered: 2026-01-03 during production testing for
lbd.shibari.photoProblem
When
sd_caption_enabled=true, captions are generated usinggenerate_with_sd()which uses SD-specific prompts (sd_caption_system_prompt,sd_caption_role_prompt). These are still being overwritten by static YAML even when the tenant provides customsystem_prompt/role_prompt.Issue #48 fixed the regular prompt precedence but missed the SD caption prompt case.
Root Cause
In
CaptionGeneratorOpenAI.__init__()(ai.py lines 237-252):The problem: when orchestrator provides custom
system_promptbut NOT explicitsd_caption_system_prompt, the code falls back to static YAML instead of inheriting from the customsystem_prompt.Evidence
Orchestrator config for
lbd.shibari.photo:{ "system_prompt": "Using the image analysis, write a tiny caption between 100-150 characters. 1. Wrap the message in ✨ sparkles ✨...", "role_prompt": "You are a classy, fun, and playful 30-year-old woman posting on FetLife...", "sd_caption_system_prompt": null, "sd_caption_role_prompt": null, "sd_caption_enabled": true, "sd_caption_single_call_enabled": true }What gets used (from static YAML):
Result
Email subject: "What emotions do you feel when viewing intimate art like this? Share your thoughts!"
Missing: ✨ sparkles ✨, themed headers, no hashtags rule, "| Selfshot unless tagged" suffix
Expected Behavior
When tenant provides custom
system_prompt/role_promptbut NOT explicit SD prompts:Proposed Fix
Change the SD prompt fallback logic to check if tenant has custom prompts:
Affected Code Locations
publisher_v2/src/publisher_v2/services/ai.pyAcceptance Criteria
system_prompt, SD prompts inherit from it if no explicit SD prompts providedrole_prompt, SD prompts inherit from it if no explicit SD prompts providedsd_caption_system_prompt, use that (existing behavior)Testing Notes
To reproduce:
system_promptandrole_promptbut NOsd_caption_*promptssd_caption_enabled=trueBlocking: Production FetLife prompts not working