Production uses pgxstore, whose plain scs.Store.Find deliberately panics
("missing context arg") — only FindCtx works. DedupeSessionCookies called
Store.Find directly, so any request carrying duplicate session cookies
500'd (chi Recoverer caught the panic). Unit tests passed because the
in-memory store implements plain Find.
Mirror scs.doStoreFind: type-assert FindCtx(context.Context, string) and
use it with the request context, falling back to plain Find for simple
stores. Regression test adds a ctx-only store whose plain methods panic.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Browsers that logged in before the session cookie became Domain-scoped
still hold the old host-only descrybe_session for api.descrybe.io. They
then send BOTH cookies — older (stale) first — and Go reads the first
match, so the stale relic shadows the fresh Domain cookie and every
request 401s even immediately after a successful login. Clearing
browser cookies fixed it manually; users should never have to.
New DedupeSessionCookies middleware (mounted before scs LoadAndSave):
when duplicate session cookies arrive, pick the token that resolves in
the session store, rewrite the Cookie header to just that one, expire
the host-only relic (Set-Cookie without Domain only touches the
host-only variant), and re-issue the surviving token on the canonical
Domain cookie. One request converges the browser to a single cookie.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Session (no env needed):
- SESSION_COOKIE_DOMAIN env removed. Config.SessionCookieParentDomain()
derives the cookie Domain from WEB_ORIGIN + PUBLIC_API_URL, which the
API already requires: sibling hosts of one parent (descrybe.io +
api.descrybe.io) share the parent domain so SvelteKit SSR (/admin
gate, user switching) receives the session cookie; localhost, IPs,
same-host, and unrelated hosts stay host-only. Deploying the new build
is the whole fix — nothing to configure.
AI generation prompt page:
- Each section now embeds its formula editor next to the per-language
prompt instructions: Title = full title formula builder (preview,
elements, separator, variable selector, custom variables), Description
= description formula sections editor (type + instructions + export
id, drag reorder), Meta = meta title / meta description formula
fields. One Save writes categories.prompt + title_template +
description_template together; Assign copies all three to the
selected categories.
- New $lib/categories/formula-variables.ts loads every usable field for
the builder: custom variables (/api/variables), company attributes
(/api/attributes — attribute_key, name, unit, example), and standard
fields (/api/standard-fields). Used by both the prompt page and the
title-formula page (which previously ignored attributes).
Verified locally: svelte-check clean for changed files, unit tests pass,
and the full save contract exercised over HTTP as the page does it
(login → load variables/attributes/standard-fields → PATCH prompt +
title-formula + description-formula → round-trip read), then the test
category restored via repair-category-prompts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The per-category prompt editor showed English machine boilerplate
("Role: description. Build JSON …", schema intro, {{var}} context lines)
because seed/sync baked the render framing into categories.prompt. Now the
stored overlay is exactly the legacy wp_product_categories.sql content,
split into the three sections a user would type themselves:
--- Title --- Slovenian naming formula
--- Description --- legacy <H2>/<p>/<ul> body structure
--- Meta --- legacy metaDescription instruction
Schema, role framing, and Name/Description/Category/Attrs product context
stay render-time only (system template + ensureCategoryEnhanceUserContext),
where they already existed.
- SplitLegacyCombinedEnhancePrompt: non-legacy input now returns "" —
categories without seed/legacy content get their override CLEARED
(company default) instead of being stuffed with the canonical template
(e.g. parent categories like "Bela tehnika" absent from the SQL).
- CategoryEnhancePromptNeedsRepair flags stored boilerplate ("parsed as
JSON", "Build JSON", retired Attributes section) so Sync rewrites old
data to the clean shape; repair supports clearing (prompt = '{}').
- seed-a1 apply-category-prompts skips instead of writing template text.
- Web editor compose stores only the user's section text: empty sections
keep bare markers, default bodies and the default preamble are never
persisted (DEFAULT_SECTION_BODIES removed).
- Verified locally: apply rewrote 238 A1+Demo categories (216 exact
splits, 22 cleared), zero boilerplate matches in DB, idempotent re-run
(would_update=0).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Production splits web (descrybe.io) and API (api.descrybe.io). The session
cookie was host-only for api.descrybe.io, so the browser never sent it to
the web host. The /admin SvelteKit SSR gate (fetchMeStaff in
+layout.server.ts) forwards the incoming cookie header to /api/auth/me —
with no cookie to forward it always got 401 and bounced every successful
login back to /login?next=/admin (login POST 200, /me 200 from the
browser, /me 401 from the web server).
New SESSION_COOKIE_DOMAIN env (default empty = host-only, local dev
unchanged) sets the session cookie Domain attribute; set it to the parent
domain (descrybe.io) in production so both hosts receive the cookie.
Leading dot is normalized away. CSRF needs no change — it already seeds
cross-origin via the X-CSRF-Token response header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Category enhance prompts (Sync A1 / seed-a1 / repair) now use the same
three role sections as the rest of the platform: Title / Description /
Meta. The retired "--- Attributes ---" role section is removed from the
canonical template, the legacy-split overlay, and the web prompt editor;
Category/Attrs stay as plain product-context lines (attribute extraction
remains pipeline-level via AppendAttributeConstraints).
- Stored prompts still carrying an attributes section are detected by
CategoryEnhancePromptNeedsRepair and rewritten on the next Sync.
- Legacy wp_product_categories.sql splits now also seed a default Slovenian
metaTitle rule (dumps only carried <metaDescription>), so every A1
category gets all four prompt areas: title formula, description
sections, meta title, meta description.
- Role-sectioned prompts no longer imply the A1 SEO-omit cohort
(CompanyOmitsSEOMeta): sectioned prompts are the canonical prompt-editor
output for every tenant, and the sniff silently disabled SEO meta for
any company that saved a category prompt.
- Verified locally: repair-category-prompts -apply updated 238 A1 +
Platform Demo categories from scripts/seed/wp_product_categories.sql
(216 legacy splits), idempotent on re-run, zero attributes sections
left in DB.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>