Surface Slovenian A1 content in prompt page; purge English defaults

Three bugs hid the split A1 data behind English junk:

1. /api/auth/me company was only {id, name} — me.company.language never
   existed, so the prompt/formula pages ALWAYS fell back to English as
   the primary content language and never showed the Slovenian ("sl")
   prompt sections by default. handleMe now enriches the active company
   with language + content_languages from the companies table.

2. The unified prompt page injected English default texts: meta title /
   meta description fields were pre-filled via getDefaultMetaTitle/
   Description (and silently SAVED on Save), new description sections
   started with English instructions, and type changes overwrote user
   text with English defaults. All removed — empty stays empty
   (placeholders only), stored tenant content is shown exactly as-is,
   meta-only templates now save (sections no longer required).

3. Sync A1 / repair left English formula artifacts on categories with no
   seed content ("Audio video" etc.): earlier repairs backfilled
   DefaultRetailTitleFormula and cats.json-style English description
   sections onto every category. Repair now (a) never invents a default
   title formula — formulas derive ONLY from legacy naming rules — and
   (b) with the seed as source of truth clears title_template and
   description_template on non-seed categories (new
   title/description_template_cleared counters).

Locally verified end-to-end: corrupted Platform Demo Monitorji (legacy
combined blob) + Audio video (English junk), called the real admin
POST /companies/{id}/sync-a1 as the platform admin — Monitorji restored
to the exact split Slovenian Title/Description/Meta + formulas +
Slovenian meta title/description, Audio video fully cleared; repair
idempotent (0 on re-run); me.company.language returns sl for A1;
zero English-default templates remain in A1 + Platform Demo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 01:49:44 +02:00
co-authored by Claude Fable 5
parent c9554f2f7c
commit 0ff24b1534
3 changed files with 94 additions and 51 deletions
+43 -1
View File
@@ -37,6 +37,10 @@ type RepairCategoryEnhancePromptsResult struct {
FallbackTemplate int `json:"fallback_template"`
TitleTemplateBackfill int `json:"title_template_backfill"`
DescTemplateBackfill int `json:"description_template_backfill"`
// *_Cleared count non-seed categories whose leftover formula artifacts
// (English defaults from earlier repairs / cats.json / migration) were removed.
TitleTemplateCleared int `json:"title_template_cleared"`
DescTemplateCleared int `json:"description_template_cleared"`
ByCompany map[string]int `json:"by_company"`
DryRun bool `json:"dry_run"`
SeedPath string `json:"seed_path,omitempty"`
@@ -524,7 +528,19 @@ func repairCompanyCategoryEnhancePrompts(
if needDesc && strings.TrimSpace(legacyParts.DescriptionRules) == "" && strings.TrimSpace(legacyParts.MetaRules) == "" {
needDesc = false
}
if !needPrompt && !needTitle && !needDesc {
// Never invent a default title formula: formulas are derived ONLY from
// legacy naming rules. (Earlier repairs backfilled DefaultRetailTitleFormula
// with English labels onto every category — that junk gets cleared below.)
if strings.TrimSpace(titleRules) == "" {
needTitle = false
}
// Seed is the source of truth: categories with no seed/legacy content must
// not keep formula artifacts (English default sections/formulas from
// earlier repairs, cats.json seeding, or migration).
noLegacyContent := seedLegacy == "" && !legacyParts.WasLegacy && strings.TrimSpace(titleRules) == ""
clearTitleTpl := forceFromSeed && noLegacyContent && jsonbTemplatePresent(titleTpl)
clearDescTpl := forceFromSeed && noLegacyContent && jsonbTemplatePresent(descTpl)
if !needPrompt && !needTitle && !needDesc && !clearTitleTpl && !clearDescTpl {
if company.HasAnyPrompt(m) {
out.AlreadyOK++
} else {
@@ -564,6 +580,12 @@ func repairCompanyCategoryEnhancePrompts(
if needDesc {
out.DescTemplateBackfill++
}
if clearTitleTpl {
out.TitleTemplateCleared++
}
if clearDescTpl {
out.DescTemplateCleared++
}
updatedHere++
continue
}
@@ -640,6 +662,26 @@ func repairCompanyCategoryEnhancePrompts(
}
}
if clearTitleTpl {
if _, err := pool.Exec(ctx, `
UPDATE categories
SET title_template = NULL, updated_at = now()
WHERE id = $1 AND company_id = $2`, id, companyID); err != nil {
return updatedHere, fmt.Errorf("clear title_template category=%s: %w", id, err)
}
out.TitleTemplateCleared++
}
if clearDescTpl {
if _, err := pool.Exec(ctx, `
UPDATE categories
SET description_template = NULL, updated_at = now()
WHERE id = $1 AND company_id = $2`, id, companyID); err != nil {
return updatedHere, fmt.Errorf("clear description_template category=%s: %w", id, err)
}
out.DescTemplateCleared++
}
out.Updated++
updatedHere++
}
@@ -340,6 +340,24 @@ func (s *Server) handleMe(w http.ResponseWriter, r *http.Request) {
out["company"] = c
}
}
// Enrich with content language settings: the dashboard (category prompt /
// formula pages, language switchers) reads me.company.language as the
// primary content language — auth.Company alone is only {id, name},
// which silently fell back to English for every tenant.
if c, ok := out["company"].(auth.Company); ok {
var lang string
var contentLangs []string
if err := s.Pool.QueryRow(r.Context(), `
SELECT COALESCE(language, 'en'), COALESCE(content_languages, '{}')
FROM companies WHERE id = $1`, cid).Scan(&lang, &contentLangs); err == nil {
out["company"] = map[string]any{
"id": c.ID,
"name": c.Name,
"language": lang,
"content_languages": contentLangs,
}
}
}
if credits, err := s.Billing.CreditsOverview(r.Context(), cid, s.Config.LowCreditsThreshold); err == nil {
out["credits"] = credits
}