AACSearch

Журнал изменений

Изменения продукта и публичного API — из CHANGELOG.md в корне репозитория.

Журнал ведётся на английском; раздел API changes перечисляет каждое изменение роутов /api/v1 по версиям API.

Changelog

Mobile spacing + shell polish — 2026-10-31

  • Marketing/saas shells: header/content/footer rhythm (section py-14+, hero/footer/page header), ≥44px chrome (hamburger, locale, footer links, notification bell).
  • Soft elevation on hubs/home strips/auth card/consent banners; AuthWrapper + TopBar safe-area insets; ColorModeToggle coarse-pointer targets.
  • Shared MarketingPage / PageHero / PageHeroBand / ContentBand + saas ConsolePage; normalized inner-page rhythm; wider phone gutters; soft-elevation tiles; opaque sticky marketing header; AuthWrapper landmarks.

Theme + console/marketing IA polish — 2026-10-31

  • Mobile/Safari: viewport-fit=cover, dvh/svh, safe-area on nav/sheets, ≥44px touch rows on mobile, autofill/tap-highlight, soft dialogs, Learn after Pricing.
  • Theme: soft elevation instead of harsh borders; marketing no longer overrides tokens with ink-mono leftovers; Helvetica + #fd366e primary retained.
  • Marketing: SocialProof immediately after IntegrationsStrip; Pricing only top-level (not under Resources).
  • SaaS: index nav Overview → Data → Search (Playground) → Relevance → Experience → Results; denser sidebar; API keys / AI agents in settings hub.

Appwrite pink + Helvetica theme — 2026-10-31

  • UI: product font stack is Helvetica Neue / Helvetica / Arial (dropped Figtree from saas, marketing, docs).
  • Theme: @repo/tailwind-config light/dark tokens follow Appwrite docs surfaces with brand/primary #fd366e.

Green main checks — 2026-10-30

  • API changes: TypeScript SDK methods that take both a JSON body and query params now use (…, body, query?) like PHP/Go (fixes CLI upsertDocuments(index, body) arity under type-check).
  • Tooling: turbo/turbo.ci type-check depends on ^build so @repo/sdk dist exists for aacsearch-cli; root tsconfig excludes Bitrix/vendor JS noise for oxlint; oxfmt/oxlint ignore packages/aacsearch-ui/vendor; design-lint: text-[11px] → text-xs in CollectionHealthChrome (budget stays 8).

Docs + marketing UX polish — 2026-10-30

  • Docs: quote broken RU glossary YAML descriptions (homepage collection load); branded fumadocs not-found; EN search/TOC i18n parity; soft-land locale switch off RU-only glossary deep links; localized Copy Markdown.
  • Marketing: wire home sections through MarketingSection / SectionHeader (PR #22 patterns); no purple/glow kits; no marketing copy key changes.

Search REST explainability parity — 2026-10-30

  • API changes: POST|GET /indexes/{index}/search intent.reasons — human-readable why each constraint/entity fired (already computed by searchForIndex, previously stripped from REST; private-field mentions stay stripped). Ranking unchanged.
  • API changes: same search responses may include constraint_diagnostics only when explain=true (zero-result hard-filter probes). Default payloads omit it.
  • Widgets gallery, Hosting install, per-widget editor, analytics and history: EmptyState for loading / error / empty; HintTooltip on gallery and section titles.
  • Install snippets stay on @repo/ui JsonPreview (including hosted iframe embed); no committed public/embed bundles.
  • i18n: search.embedBuilder gallery / install / analytics / history hints (en/de/es/fr/ru).

Webhooks + org members EmptyState UX — 2026-10-30

  • Admin webhook deliveries: EmptyState for loading / error / empty (filter clear CTA); ConsoleSection hint; missing webhooks.search i18n fixed. Signing secrets are not shown.
  • Organization members / invitations: loading / error / empty via EmptyState; one primary invite CTA focusing the invite form; HintTooltip on members + invite sections (en/de/es/fr/ru).

Relevance settings console chrome — 2026-10-30

  • Relevance: CollectionHealthChrome + scannable AdvancedFlagsChrome for opt-in smart ranking (semantic / graph fusion / rerank / HyDE); EmptyState for loading, health error, schema-empty; HintTooltip on advanced flags — flags stay off by default.
  • i18n: search.searchSettings.advancedFlags and related tips (en/de/es/fr/ru).

API keys / billing / usage EmptyState UX — 2026-10-30

  • Console API keys, billing finance, wallet, invoices and plan usage: EmptyState for loading / error / empty with one primary CTA; HintTooltip on key section titles (en/de/es/fr/ru).
  • Pricing table empty when no other plans; indexes plan-usage section always visible for admins with meters or unavailable EmptyState.
  • No new JsonPreview surfaces (key secret remains copy-once plaintext; no JSON payloads on these pages).

Sync error recovery UX — 2026-10-30

  • Sync dashboard: EmptyState for loading / load error / empty runs; failed-run banner with Retry + Open logs; row and Sheet recovery actions; Schema / Documents links when the failure classifies as mapping or document issues (en/de/es/fr/ru).

Merchandising rule conflicts API — 2026-10-30

  • Domain helper detectMerchandisingConflicts in packages/api/lib/search (overlap, redirect precedence, pin/bury/exclude compete) — no LLM.
  • Console oRPC listMerchandisingRuleConflicts; Rules UI prefers the server payload and falls back to the shared client helper.
  • API changes: GET /api/v1/rules/conflicts?index= returns conflict/precedence reasons for published rules (settings:write).
  • i18n: conflict source labels in en/de/es/fr/ru.

Playground explainability UX — 2026-10-30

  • Playground “Why these results” surfaces API understanding / guide / hitReasons / intent / relaxation / rules as scannable sections with @repo/ui Badge, HintTooltip and EmptyState (no silent query rewrite — adopt / use-relaxed stay explicit).
  • Per-hit reason kind badges on result rows; explain panel lists up to five hits (kinds localized).
  • i18n: search.playground.explainSections|explainHints|reasonKinds|hitReasonsFor|hitReasonsMore|adoptQueryHint*|useRelaxedHint* (en/de/es/fr/ru).
  • Wire landed @repo/ui primitives into real console surfaces (not kit demos); Sidebar stays unused per ADR.
  • Drawer: AdvisorDrawer + Documents import use vaul Drawer (direction="right"); Drawer primitive supports top/bottom/left/right via data-vaul-drawer-direction.
  • Carousel + Menubar: Widget gallery — mobile preview carousel; Configure / Lifecycle menubar replaces flat overflow menu.
  • NavigationMenu: Admin Search ops tab strip (viewport optional for link-only strips).
  • i18n: search.embedBuilder.gallery.menuConfigure|menuLifecycle|carouselPrevious|carouselNext (en/de/es/fr/ru).

Design-lint ratchet (187 → 8) — 2026-10-30

  • Lower pnpm lint:design --max-warnings from 187 to 8 after clearing easy no-raw-colors / no-arbitrary-values findings in apps/saas + packages/ui (no page redesign).
  • Map off-scale type (text-[11px] / text-[10px] / …) to text-xs / text-sm; status amber/emerald classes to console-* theme tokens; JsonPreview hues to json-* tokens in tooling/tailwind/theme.css.
  • Docs: docs/development/design-lint.md ceiling kept in sync.

Console JsonPreview / EmptyState audit — 2026-10-30

  • Replace raw audit/advisor/widget JSON dumps with @repo/ui JsonPreview (admin + org audit metadata, advisor values, widget sample objects).
  • Replace ad-hoc blank/<p> empty list panels with EmptyState / ConsoleEmpty across schema, indexes, connectors, sync, facets, charts, shopping guide, notifications, and related console surfaces.
  • No second JSON viewers; existing copy reused (no new i18n keys).

Data-table view options + faceted filters — 2026-10-30

  • @repo/ui data-table: add DataTableViewOptions (column visibility) and DataTableFacetedFilter (selected-value badges + facet counts + clear facet), presentation-only on existing DropdownMenu / Command / FilterBar stack — no second kit.
  • IndexesTable: wire view-options + status facet counts; Documents browse source filter uses faceted filter chrome.
  • i18n: common.dataTable.viewOptions / toggleColumns / clearFacet in en/de/es/fr/ru.

Schema loading error state — 2026-10-30

  • Schema fields panel no longer stays on “Loading schema…” when getSchemaBuilder fails: show an error EmptyState with Retry (isPending || !data previously treated errors as loading).
  • Side “Default filters” card surfaces the same load failure instead of a false empty state.

Marketing section composition (Tailark / Origin / HyperUI) — 2026-10-30

  • Cherry-pick landing section patterns into apps/marketing only: shared MarketingSection helpers (SectionHeader, StatStrip, FramedBand), Origin FAQ split, HyperUI how-it-works / social-proof strips, Tailark framed final CTA, feature benefit rails.
  • Hero presence stays CSS keyframes (atmosphere, brand rule, rise) with prefers-reduced-motion — no motion npm, no second UI kit, no purple/glow aesthetics.
  • Reuses existing marketing i18n (including unused home.features.badge as eyebrow); ink-mono theme tokens only.

@repo/ui deferred shadcn primitives — 2026-10-30

  • Add remaining deferred primitives to @repo/ui only: drawer (vaul), sidebar (+ ink-mono sidebar tokens), carousel (embla-carousel-react), menubar, navigation-menu.
  • Export from packages/ui/index.ts; keep unified radix-ui (no per-primitive @radix-ui/react-* deps). SaaS shell not rewritten onto sidebar — wire later when a surface needs it.
  • Resizable left to #19 (Playground Resizable + shared Sortable) — not duplicated here.

Playground Resizable + shared Sortable — 2026-10-30

  • @repo/ui: shadcn-style Resizable (react-resizable-panels v4) and thin Sortable helpers (@dnd-kit) for reusable list reorder.
  • Playground Search: resizable results vs facets on md+ (Tabs kept; mobile collapsible facets unchanged). Compare: resizable A/B columns on lg+.
  • Rules merchandising list consumes @repo/ui Sortable; curation pin dialog reuses it for pin order when two or more pins are present (no silent query rewrite).
  • i18n: resize / pin-order strings in en/de/es/fr/ru.

Post-UX-merge type cleanup — 2026-10-30

  • @repo/ui Input forwards refs (RHF / autofocus).
  • EmptyState titleElement allows h1 for page-level empty states (onboarding permission gate).

Admin shared table / filter / KPI format — 2026-10-30

  • Align platform-admin list surfaces with console DataTableToolbar + FilterBar / FilterSelect language (indexes, tenants, keys, DLQ, jobs/billing/webhooks, connectors, audit, business reports).
  • Overview and business report KPIs use shared KpiCard (optional hint) instead of ConsoleMetric; no second UI kit.
  • i18n: filter/search/empty-filtered strings for admin search-ops tabs in en/de/es/fr/ru.

Documents page UX (data-table reuse) — 2026-10-30

  • Documents browse uses landed @repo/ui DataTableToolbar / DataTableSelectionBar / DataTableSkeleton / DataTableColumnHeader with existing nuqs URL filters; DocumentsGrid kept for spreadsheet editing.
  • Bulk export/delete moves onto DataTableSelectionBar; loading uses DataTableSkeleton.
  • Row detail Sheet (?doc=) shows metadata + redacted payload via JsonPreview, with edit / history / delete actions (en/de/es/fr/ru).

Playground primary search layout — 2026-10-30

  • Search Playground page leads with one Search workspace; Compare and Assistant open on demand via Tabs (?tab=). Explain stays in a Sheet.
  • Compare runs two live queries through the existing oRPC search path (no parallel ranking). Drawer/stack Playground layout is unchanged.

Assistant UX (sources, cards, approvals, activity) — 2026-10-30

  • Extend @repo/aacsearch-ui with Sources, AnswerCard, ApprovalCard, and ActivityStatus patterned on vendored assistant-ui (no CopilotKit / parallel chat kit).
  • Console assistant: domain-badge sources, richer answer cards, collapsible activity timeline, HITL interrupt approval wired to assistantResumeInterrupt.
  • i18n: approval + activity strings in all five SaaS locales. Assistant remains behind existing settings/flags; no LLM in the search hot path.

Console Wave 1 UX (Schema / Mapping / Sync) — 2026-10-30

  • Schema: compact field rows with shared FilterBar toolbar, capability grouping, and TanStack Virtual when lists are large (replaces card list + local pagination for the fields table).
  • Mapping: Feed wizard rules and connector MappingFields use the same compact table + filter language as Schema.
  • Sync: runs table groups jobs by source; failed jobs surface first; job Sheet leads with error Alert + StatusBadge.
  • Shared console helpers: ConsoleListToolbar, VirtualRows, schema-field-groups, sync-job-groups (en/de/es/fr/ru).

Rules visual order / preview / conflicts — 2026-10-30

  • Merchandising Rules tab: drag-and-drop evaluation order via @dnd-kit (keyboard + pointer), still backed by reorderMerchandisingRules priorities; Raise/Lower menu kept.
  • Conflict / precedence callout for overlapping match queries, competing pin/bury/exclude on the same document, and multiple redirects — HintTooltip explains existing evaluation order (no silent query rewrite).
  • Merchandising Studio before/after preview shows applied-rule precedence (reasons / appliedRules) with an order hint.
  • i18n for new SaaS strings in en/de/es/fr/ru.

Shopping Guide need → field → value mapping — 2026-10-30

  • Add shared searchable Combobox to @repo/ui (Popover + Command; no new UI kit).
  • Shopping Guide catalog binding UI maps need → field → value with column headers, Combobox field pickers, category value chips, and required-mapping progress — feature flag / existing guide settings unchanged; advanced JSON stays behind Collapsible.
  • i18n for the new mapping chrome in en/de/es/fr/ru.

Onboarding Steps progress (Wave 3) — 2026-10-30

  • /onboarding shows unified @repo/ui Steps for the full setup journey (Create → Data → [Design] → Check → Connect); no Driver.js tour.
  • Create flow is two phases with one primary CTA each (path → Continue, then name/workspace → Create search); permission-denied uses EmptyState.
  • i18n: customerJourney.steps.create, continue, detailsTitle, pathHint, permissionTitle in en/de/es/fr/ru.

Data-table patterns on @repo/ui — 2026-10-30

  • Add presentation building blocks in @repo/ui: DataTableToolbar (composes FilterBar), DataTableColumnHeader, DataTableSelectionBar, DataTableSkeleton — no second UI kit; no TanStack/nuqs inside @repo/ui.
  • Wire org IndexesTable with TanStack Table + nuqs faceted status filter, sort headers, row selection / bulk delete bar.
  • AdminDataTable uses DataTableToolbar / FilterBar instead of a bare search input (server list + existing nuqs query state).

@repo/ui missing shadcn primitives — 2026-10-30

  • Add shared primitives to @repo/ui only: separator, slider, radio-group, breadcrumb, calendar (react-day-picker), pagination, toggle, toggle-group, hover-card, aspect-ratio, context-menu.
  • Export from packages/ui/index.ts; keep unified radix-ui (no per-primitive @radix-ui/react-* deps). Deferred: sidebar, drawer/vaul (Sheet covers panels), carousel/embla, resizable, menubar, navigation-menu.

Console page density — 2026-10-30

  • Paginate schema fields, facets, detected CMS sources/properties and sync history through the existing pagination UI. CMS properties and facets support field search; writes retain the complete underlying field collection.
  • Remove nonfunctional source-selection controls from connection details; keep source selection in the connection wizard. Collapse inactive index navigation groups, and open the Playground assistant on demand.
  • Align onboarding progress with the actual site/API steps, replace nested numbered design steps with section buttons, and remove position-dependent publishing instructions in all five locales.
  • Validation: 372 SaaS tests, 17 i18n tests, SaaS TypeScript and architecture invariants passed; browser pagination/filter/navigation and 390 px layouts checked.

Commerce Flow Studio closure (visitor + versions) — 2026-10-30

  • Visitor SSE: flowTurnToStreamEvents emits product_cards / real ask option chips / honest groundedness; catalog+quote forwarded from the flow turn.
  • Advance claims session revision before shopping-guide quote/catalog side effects; quote create is idempotent per (sessionId, sessionRevision).
  • SearchGuideQuote @@unique([sessionId, sessionRevision]) + P2002 re-read closes residual concurrent quote create; migration 20261030211000_search_guide_quote_session_revision_unique (deploy on authorized DB).
  • Assistant routing: trigger match before defaultFlowId; cancel/clarify commands; techmap sessions can start from assistant; Shopping Guide bindings use schema field selects (JSON optional advanced only).
  • Studio draft try (mode: "draft") works without enabling flows for visitors; public REST stays live-only.
  • Prisma SearchFlowDefinition (org + index): fork builtin → draft → validate → publish immutable versions; runtime resolves builtin or published org flow. Migration 20261030210500_search_flow_definition (deploy on authorized DB).

Commerce Flow org definitions (Studio) — 2026-10-30

  • Prisma SearchFlowDefinition (org + index scoped): draft/published versions with @@unique([organizationId, indexId, key, version]); migration 20261030210500_search_flow_definition.
  • Domain: fork builtin → org.{key} draft, save draft, validate, publish (immutable published row + next draft); runtime resolves builtin or published org flow by id/version (Studio draft try via P4 draft flag).
  • Console oRPC: listOrgFlows / getOrgFlow / forkFlow / saveFlowDraft / publishFlow / validateOrgFlow; Flows Studio fork/edit collect/validate/publish UI (en/de/es/fr/ru).

Index Studio navigation — 2026-10-30

  • Existing index editors are grouped into Overview, Data, Studio, Validation and Results. Existing URLs, command navigation and capability checks are preserved; task-group breadcrumbs do not link to an editor with potentially different permissions.
  • Group labels are available in English, German, Spanish, French and Russian. This introduces no project hierarchy or new persistence model.

Commerce Flow Agent wave 3 — 2026-10-30

  • Techmap flow sessions seed nested guide state; catalog_match / quote reuse shopping-guide product helpers (coerceGuideState, persist via syncFlowFromGuide) and upgrade pendingActions to resolved / incomplete with optional endpoint, catalog, quote.
  • OpenAPI types flow session envelopes (pendingActions statuses + SDK regen); REST/SSE wire tests; docs api-reference/commerce-flows (en/ru/de).

Commerce Flow Agent Studio — 2026-10-30

  • Console Flows Studio at /flows (AI nav): enable flag, default flow, definition preview, step list, JSON+YAML export via shared exportFlow*, try/advance session via oRPC, pendingActions in the try panel.
  • oRPC: getFlowsSettings / saveFlowsSettings / listFlows / getFlow / flow-session create/get/advance; settings persisted with index revision bump.
  • Assistant continues an existing flow session when conversation_id matches; pending catalog/quote actions surface as SSE guide_actions; embed keeps proof on data-aac-proof-*.
  • catalog_match / quote steps return structured pendingActions (shopping-guide handoff / in-process resolve when guide state is attached).

Commerce Flow Agent — 2026-10-30

  • Studio-ready YAML/JSON commerce flows with Flow Runtime (ledger + step cursor) on top of Search Contracts.
  • Shopping-guide techmaps compile to commerce.techmap:* flows; guide REST stays compatible and attaches flowRuntime on the session state.
  • API changes: GET /indexes/{indexSlug}/flows, GET /indexes/{indexSlug}/flows/{flowId}, POST /indexes/{indexSlug}/flows/sessions, GET /indexes/{indexSlug}/flows/sessions/{sessionId}, POST /indexes/{indexSlug}/flows/sessions/{sessionId}/advance (requires settings.flows.enabled).
  • API changes / Commerce Flow: OpenAPI now types flow list/get and session create/get/advance envelopes (pendingActions typed; SDKs regenerated).
  • Assistant stream may start a custom commerce flow turn when flows are enabled and a trigger matches (techmaps remain on /guide/*).

Widget renderer consistency — 2026-10-30

  • Search cache separates consent states and disables personalization after consent withdrawal. React cards honor configured field roles, safe links, images and localized prices; search options preserve explicit zero/false values.

  • Shared assistant SSE and React adapters retain retrieval proof metadata from the stream contract.

  • Hosted pages retain saved widget type and the complete builder configuration through the CDN renderer, which shares search/SSE with aacsearch-ui; React composition remains explicit. Shared URLs retain query, page and recommendation seed.

  • Constructor previews render recommendations and autocomplete with the same type-specific renderer as published widgets, using server-approved draft settings.

  • Console AI/MCP confirmation operations have explicit access policies; assistant mutations require content-write and publication capabilities.

Bitrix public features — 2026-10-30

  • Bitrix 0.7.9 connects compact assistants and recommendations per site, adds voice input and opt-in photo search through the existing API with CMS permission checks.

Bitrix smart filters — 2026-10-30

  • Bitrix 0.7.8 follows native property settings/order, supports numeric bounds and independent multi-select facet counts, and preserves filter state in navigation.
  • Large schema updates respect the API request limit. Serialized CMS cache values are excluded from product description text. Site-owned adapters can narrow and relabel native filter properties without bypassing the applied schema.

Public CMS assistant — 2026-10-30

  • Bitrix 0.7.7 labels native availability as orderability, preserving the distinction between backorders and physical stock.

  • Bitrix 0.7.6 adds a configurable published-assistant component, preserves public URL schemes/ports and refreshes service endpoints during Marketplace updates.

  • Assistant retrieval supports opt-in, metered query preparation for natural-language questions. Public product prices, currencies and stock are included in answer context.

  • Assistant source cards no longer treat orderability as proof of physical stock.

  • Hosted assistant pages honor the URL locale and preserve it in shared links and iframes.

  • The embedded assistant explains the HTTPS requirement before requesting the microphone, in all five supported UI languages.

Resilience / console exports — 2026-10-30

  • O7: engine circuit breaker on the search read path (searchCollection, multi-search). After SEARCH_ENGINE_CIRCUIT_THRESHOLD (default 5) consecutive transport failures (UPSTREAM_UNAVAILABLE / UPSTREAM_TIMEOUT) searches fail fast for SEARCH_ENGINE_CIRCUIT_COOLDOWN_MS (default 10 s) with the same SearchError and a Retry-After header, then one probe closes or re-opens the circuit. Engine 4xx answers do not count. State is per process; =0 disables.
  • O5: /metrics Prometheus now exposes aacsearch_usage_outbox_pending (JSON already had outbox_pending) and aacsearch_engine_circuit_open / _trips_total / _short_circuited_total; JSON gains engine_circuit.
  • E7: DocumentsBrowser «Export selected» downloads the selected live documents as JSONL (exportSelectedDocuments, oRPC console, ≤100 ids, private fields stripped for plain members, unknown ids reported).
  • API changes: OpenAPI now documents 409 INDEX_READ_ONLY on upsertDocuments, bulkUpdateDocuments, deleteDocument, deleteDocuments, restoreDocument and webhookIngest (behaviour unchanged since I21); SDK docs regenerated. A search that hits an open circuit answers 502 UPSTREAM_UNAVAILABLE with Retry-After.

Sync UI — 2026-10-30

  • I28: «Что изменилось». Каждый connector run (generic feeds, CMS) пишет в job.stats.changes точные счётчики added / updated / removed и до 50 примеров на тип (id, title); Sync → Job detail показывает список. Тот же delta-хеш, что и у импорта, второго diff-пайплайна нет. Роли с private-полями индекса получают только счётчики (stripSyncChangeSamples); список listConnectors отдаёт счётчики без примеров.
  • I7: Sync «Расписания» теперь единый список — расписания коннекторов и реиндекса индекса (getReindexSchedule, I22) по ближайшему запуску. Новых планировщиков нет.
  • API changes: none (oRPC console only; public REST unchanged).

Index lifecycle — 2026-10-30

  • I22: scheduled reindex. settings.reindexSchedule (cron + IANA timezone, at most once per hour) is edited on the Sync page (getReindexSchedule / saveReindexSchedule, oRPC console). The scheduler tick (and runJobsOnce) enqueues due indexes through the existing requestReindex → enqueueReindexIndex path with trigger: "schedule" and a system audit actor. Guards: plan feature reindex, index active and not read-only, no parallel rebuild (a busy index skips the window), per-index advisory lock, missed windows run at most once, batch cap per tick. Last outcome (queued / skipped_* / failed) is shown in the console.
  • I21: read-only index. settings.lifecycle.readOnly toggled on the Sync page (getIndexReadOnly / setIndexReadOnlyMode, audited). While on, search and manual reindex keep working; document import/update/delete/restore/rollback, console patch, webhook ingest, connector runs (manual and scheduled — the scheduler skips the window without a failure), file/documents-import enqueue and dead-letter replay are refused.
  • API changes: document write endpoints (POST/DELETE /indexes/:slug/documents…, restore, bulk update/delete by filter, signed webhook ingest) return 409 INDEX_READ_ONLY while the index is read-only. No new public routes.

Documents / geo — 2026-10-30

  • I30: console document history — per-field diff between revisions (getDocumentRevisionDiff, private fields stripped for plain members) and append-only rollback (rollbackDocumentRevision writes the old body as a new revision through the ingest path). listDocumentRevisions rows gain has_payload. oRPC console only; no public REST change.
  • Fix: restoreDocument wrote a second revision row after the import's own, so the next write to that document hit (documentId, revision) uniqueness.
  • K6: Playground geo bar (radius filter + nearest-first + per-hit distance).
  • API changes: sort_by now accepts the native Typesense geo form <geopoint field>(lat,lng[, exclude_radius: 2km][, precision: 0.1km]); only a literal geopoint(…) passed validation before, so every real geo sort was rejected with INVALID_QUERY. Plan gating (geoSearch + customSort) unchanged.

Tooling — 2026-10-30

  • CLI: remaining CocoIndex-inspired gaps — tree-sitter WASM Rust/Java/JSON/Bash; per-chunk #cN contentHash memo (skip identical bodies); Docling --no-ocr + parserOutputHash in state; live coalesce + one retry; richer sync-status/doctors (parsers, grammars, reembed); connector-bridge NDJSON example; update summary includes chunks wrote/reused (--json too).
  • CLI: Docling PDF (--parser auto|docling|pdf-parse) with pdf-parse fallback; tree-sitter semantic --local-chunk for code (WASM JS/TS/Python/Go); API reembed after upserts (reembed: auto); live / event-driven watch + optional --stdin-events filesystem CDC bridge (DB/Kafka CDC stays connectors).
  • Customer CLI integrations/aacsearch-cli (aacsearch): local file crawl + index/search/import/export/status via the generated @repo/sdk → AACSearch API only (no local engine process). Incremental update / watch with content-hash + transform-fingerprint memoization, delete-sync, globs, declarative aacsearch.yaml / .aacsearch/pipeline.json, optional API --chunk and local --local-chunk, PDF via optional pdf-parse, URL-list source (--urls, public http(s) only), crash resume checkpoint, lineage / sync-status (doctors). Legacy scripts/aac.mjs points here.
  • CLI gap-close: suggest, multi-search, indexes, create-index, delete <id>, reembed; CSV rows→docs; HTML <title> + strip on files/URLs; pipeline schema validation + examples/; global --json; Windows watch note / poll fallback.
  • CLI polish: root/integrations README + AGENTS mention; examples/README.md local :3000 walkthrough; env-only AAC_API_KEY without init; clearer missing-scope / index-bound key Hints; docs en/ru/de scope note.
  • CLI CocoIndex-inspired upgrades (still API-only via @repo/sdk): .gitignore honor + exclude ! negation; markdown frontmatter → title/tags; symlink / max-file-size guards; bounded --concurrency upserts; update summary inserted/updated/skipped/deleted; init --pipeline scaffold; sync logic state migration; --preview / --full-reprocess / -q aliases.

Search quality / ranking / billing — 2026-10-30

  • F1: golden sets store topK; CSV import/export; run compare + playground deeplink; assertQualityGate on default profile, enabled rules, synonym import; console quality-gate toggle (settings.quality.gate).
  • F2: compareProfilesPreview / oRPC compareSearchProfiles (ephemeral configs only — no virtual replicas).
  • K8: widget itemHtml stored HTML DSL (sanitized placeholders); ecommerce wizard preset sets cardLayout: grid, facets, currency/price roles.
  • K10: offline answer-eval harness (gradeAnswerCase / evaluateAnswerSet).
  • R2: shared decay.ts (linear / exp / gauss); withDecaySort in profileToSearchParams; popularity/recs use shared exp.
  • B6: SearchUsageOutbox written on reserve, settled on commit/release; scheduler drains stale pending rows; /metrics exposes outbox_pending.
  • B9: DSAR erase returns signed ErasureReceipt (subject hash + HMAC); optional durable job via DELETE …/subjects/:key?async=1 (202 { job_id }).
  • I15/I20: reindex refuses alias swap when settings.quality.gate fails.
  • F2: Profiles console compare UI for two saved recipes.

API changes — 2026-10-30

  • Added DELETE /indexes/{slug}/subjects/{subjectKey}?async=1 (or Prefer: respond-async) → 202 { job_id, status, receipt }; poll GET /jobs/{jobId} for result.receipt / result.deleted. Sync erase (200) unchanged by default.
  • Added POST /indexes/{slug}/documents?async=1 (or Prefer / auto when batch ≥ SEARCH_DOCUMENTS_ASYNC_THRESHOLD, default 100) → 202 { job_id }; ?async=0 forces sync. Job type documents_import.
  • Added GET /jobs/{jobId}?stream=1 (or Accept: text/event-stream) SSE progress frames until terminal status.

CMS modules — 2026-10-30

  • Bitrix aacsearch.search 0.7.5: multi-site storefront ANDs bx_site_ids:=SITE_ID on search/suggest/recommend only when the install has more than one site (matches Exporter); new component templates search.title compact/dropdown, search.page grid/list, recommendations carousel; Marketplace updater copies into local/components and bitrix/components.
  • DIAFAN aacsearch 2.0.6: storefront bool filters, price range + sort, attrs AND-grouping, SSRF-safe API URL on save/connect, fallback heartbeat status, optional click tracking (/aacsearch/track/), filter-only search.

SaaS search UI — 2026-10-28

  • Playground opens the shared InstantSearch widget by default; advanced search deep links and drawers retain their query/filter behavior. RAG uses the same Assistant UI workspace, conversation history and index settings as the assistant page.
  • SaaS dev startup builds embed assets. Sandboxed previews use in-memory routing; preview sessions renew before expiry and failed previews offer a shared retry UI.

Security — 2026-10-28

  • Refresh vulnerable runtime and build dependencies; centralize Next.js, React and their type/peer versions in the workspace catalog. Remove the unused legacy mdx CLI dependency.

  • SSO fetch guard preserves token Request bodies/headers/cancellation and omits endpoint URLs and raw error text from security logs.

  • Dodo webhook identity metadata is normalized once after signature verification; numeric and boolean values cannot become organization/user references.

  • Bitrix 0.7.0 update archives include the storefront endpoints deployed by their updater. Local CI supports macOS process groups and direct loopback probes.

  • Bitrix HTTP transport reads native nested response headers correctly, so the generated SDK decodes JSON responses instead of returning raw text.

  • Project, collection and shared-source forms generate request UUIDs over HTTP development URLs as well as secure browser contexts.

  • Better Auth account.encryptOAuthTokens seals Google/GitHub/SSO tokens at rest (XChaCha20-Poly1305). Backfill: pnpm --filter @repo/auth oauth:encrypt-tokens (#137).

  • SSO sign-in IdP fetches go through safeFetch (@repo/utils); unverified domainVerified providers are refused; migration resets legacy unverified flags (#204).

  • External cron runJobsOnce(rollup|all) runs the same daily billing / key reminder / snapshot prune work as the in-process scheduler (#178).

  • Quality eval calls searchForIndex with log: false and default profile params so replays do not poison SearchQueryLog (#177).

  • Engine failures are logged with SearchQueryLog.failed / error_code and excluded from zero-result rates (#180).

  • DocSearch crawler honors robots.txt / crawl-delay / noindex (#154) and seeds from recursive sitemapindex with stop_urls / follow_links (#199).

  • Query-log retention uses each plan's logRetentionDays, capped by env (#175).

API changes — 2026-10-29

  • Analytics GET /analytics/zero-result-recovery totals now include session attribution: recoveredRevenueCents, unrecoveredRevenueCents, searchesPerSession, recoveredAovCents (from SearchClickEvent joined on queryLogId). Suggest facet_chips may include axis: "price" when the query carries an NL price band.

  • Suggest: optional include_facet_chips on GET /indexes/{index}/suggest and POST /embed/suggest (default on for empty / * / ≤3-char q). Response may include facet_chips (axis/label/query) for size/color/brand/material refine chips. Console oRPC already returned facetChips.

  • Search ranking / recommendations (domain): property-aware signals are schema-first — known commerce keys + prop_*/attr_*/… prefixes + index fields with facet: true only (no open snake*case scan). Bitrix tsvet/ razmer count when facetable. Diversify/personalize/recs pass schema; blocked plumbing (bx*\*, kind, status, …) never becomes affinity. Size hard filters reject single-letter false positives; keys with forcedFilterBysuppress guidefacetSuggestions. OpenAPI HitReason.kindlistsproperty.

  • CMS one-click Bitrix (console ↔ module, no API key paste): oRPC createConnectSession / getConnectSession now include settingsUrl (Bitrix settings.php?mid=aacsearch.search&state=… for an already-installed module; null on other platforms). installUrl remains the Marketplace partner installer. SaaS adds /connect-cms (org hub + per-org page) so the Bitrix module button can start the same session flow without pasting aacs_connect_… / API keys. Module aacsearch.search 0.7.4.

  • Hosting P0 (server): GET /api/v1/embed/widgets/{publicId} has its own per-address rate-limit bucket (embed-meta, 600/min) instead of sharing the 60/min embed budget with session/search; the meta response keeps cache-control: public, max-age=60 and stays non-enumerable (same 404 for unknown/paused/unhosted). A 429 carries Retry-After, x-ratelimit-* and the standard RATE_LIMITED body, so the hosted page can serve a cached copy instead of 404ing. Other embed routes keep the 60/min tier.

  • Hosted pages (D1): POST /api/v1/embed/widget-session now recognises a hosted page from the request itself — Origin must equal the origin the request was addressed to (scheme://Host; X-Forwarded-Host/Proto only behind a trusted proxy) — in addition to the configured NEXT_PUBLIC_SAAS_URL. Hosted pages serve correctly when reached via localhost/LAN/another host while the env names a different one; a foreign customer origin still answers 403 ORIGIN_NOT_ALLOWED, and preview-token and allowlist semantics are unchanged.

  • Saved widgets now store sparse overrides of the index embed settings instead of a full config snapshot: new oRPC getSavedWidget (GET /search/indexes/{indexId}/widgets/{widgetId}) returns the stored config (overrides only) plus effectiveConfig (defaults ← index embed ← overrides), and updateSavedWidget (PATCH /search/indexes/{indexId}/widgets/{widgetId}) accepts the complete widget form and persists only keys that differ from the current index embed, so later index-level PUT /embed changes still reach published widgets. saveSavedWidget publishes sparse as well and duplicateSavedWidget re-sparsifies the copied config; a null value is an explicit per-widget unset. Console policies cover both new routes.

  • Widget publish history + rollback (G10): migration 20261029043000_search_widget_revisions adds SearchWidget.configRevision and the append-only SearchWidgetRevision table. Every widget mutation (publish/create, per-widget update, duplicate, restore) writes its snapshot in the same transaction as the widget row. The history is read with GET /search/indexes/{indexId}/widgets/{widgetId}/revisions (cursor pagination) and rolled back with POST /search/indexes/{indexId}/widgets/{widgetId}/revisions/{revision}/restore, which replays a snapshot as a new restore revision (history is never rewritten). Responses are {items:[{revision,action,name,type,createdAt,actorUserId}],nextCursor} and {widget,revision}; both are org+index+widget scoped.

  • Per-widget analytics (G7): GET /search/indexes/{indexId}/widgets/{widgetId}/analytics (?days=1…90) aggregates the existing SearchQueryLog/SearchClickEvent widgetId attribution into {days,searches,zeroResults,zeroResultRate,clicks,ctr,topQueries} (rates 0–1; top 10 reportable queries; failed searches excluded). Console policies cover the analytics/history routes.

  • Server contract accepts the date widget facet type (WIDGET_FACET_TYPES); persisted widget configs may set facetTypes[field] = "date" ahead of the builder picker / embed runtime slices.

  • C22 database connector closed: oRPC probeDatabaseConnector + console «Test connection»; MySQL/Mongo probes verify table/collection; compose profile database-connectors (mysql/mongo); CI job database-connector runs live probe/page smoke for all three dialects. Console exposes TLS and advanced id/cursor/deleted columns; live suite covers keyset paging and soft-delete tombstones.

  • F5 bulk synonyms: console Import/Export on the Synonyms tab; oRPC importSynonymsBulk (JSON dump / array / CSV paste, replaces the set) and exportSynonymsDump; parser in synonym-bulk.ts (cross-index paste drops source set name so dumps move cleanly). Wizard no longer reads deprecated index environment. Import is all-or-nothing: invalid rows are refused with their CSV line / JSON index (SynonymBulkParseError) instead of being silently dropped, dump version is enforced, locale survives export→import, and a rejected paste never partially replaces the live set. removeSynonym now checks the synonyms feature like the other mutations.

  • Console operation policies cover the C22/F5 procedures and the LTR routes (GET …/ltr/models, POST …/ltr/train, POST …/ltr/models/{id}/activate); those console actions previously answered 403 “no project access policy”.

  • Database connector: host-form drafts require an explicit database name (no silent postgres default), the import confirmation is destructive, the 2000-group cap is stated in the hint, and the database-connector CI job runs in a container so its Postgres/MySQL/Mongo service hostnames resolve.

  • Wizard & UX sprint close: per-query profile weights (ProfileConfig.queryWeights resolved in profileToSearchParams); embed limits raised (facets 20, sort 10, perPage 48); low-CTR reports expose IPS-adjusted ctrIps; WizardShell chrome for SearchWizard; Schema «Add all» discovered fields via field-classifier.

  • Deep personalization without Typesense personalization-model archives (N21): personalize=true re-rank uses query-history content affinity (works with zero click events), brand/category/tag facet affinity, popularity prior, and rank guards (navigational skip, relevance gate, max uplift +8, top-1 lock). Response personalized.reason / moved explain the reorder. Still opt-in via settings.recommendations + consent; no engine personalization models.

  • AI agent marketplace (N19): MCP tool review gate (mcp-tool-review.ts + catalog test) — every generated operation is allowlisted or explicitly denied; unreviewed ops fail CI. Console Settings → AI agents lists reviewed tool categories and Claude / Cursor / generic MCP OAuth connect snippets. Builds on MCP OAuth (#20); credential minting and privacy exports stay denied.

  • HyDE for hybrid semantic search: settings.search.hyde = { enabled, url, model?, timeoutMs ≤ 150 } calls an operator HTTP endpoint for an embedding used only in vector_query (Typesense floats instead of auto-embed []). Lexical q is never rewritten; no in-process LLM. Response field hyde: "applied" | "skipped:<reason>"; explain.hyde when explain=true. Metrics aacsearch_hyde_*. Console Relevance → HyDE section. Off by default. Cross-encoder rerank (settings.search.rerank.provider = "http") unchanged (M13-1).

  • Custom ML ranking (LTR, #152): settings.search.rerank.provider = "ltr" runs a local pairwise L2-logistic model on first-page hits (IPS-weighted click pairs, no LLM). Async job ltr_train; models under settings.search.ltr; oRPC listLtrModels / trainLtrModel / activateLtrModel. Console Relevance → Rerank provider switch. Off by default; activate a candidate explicitly.

  • Feed preview identity + schema mapping (I12 / I13): POST …/feed-preview (and oRPC previewConnectorFeed) returns identity (natural / hashed / mode / autoId). Auto-id hashes sku → url → title (never body). Suggested mapping falls back to sku|url with first_non_empty. Console wizard shows mapped-document preview and an auto-id warning; multi-source transforms edit as a | b.

  • Database ingest connector (type: database, dialects postgres / mysql / mongodb): table/view → documents with composite (cursorColumn, id) keyset paging, sealed connectionUri/password, SSRF host guard, read-only sessions, soft-delete via deletedColumn, and immediate follow-up runs when hasMore (#155 / C22).

  • Hybrid explainability: each SearchHit may include includeExplain (text from Typesense text_match_info, vector.distance, hybrid.rankFusionScore); hit_reasons adds hybrid / vector kinds and richer text detail. Mapped in @repo/search-engine (includeExplainFromNative).

  • Prompt registry + model router (K9): @repo/ai gains versioned renderPrompt / rollbackPrompt (AI_PROMPT_PIN_<id>) and resolveTextModel({ operation, requested?, planId? }) with per-operation output budgets; answer / synonyms / shopping-guide / stream use them. getAssistantSettings also returns modelCatalog + promptCatalog.

  • Merchandising rule engine (F7): console gains banner / query-rule match (queryExact, whenZeroResults) / campaign schedule (daysOfWeek, hours) editors, priority raise+lower, dry-run POST …/rules/preview, and Algolia-style before/after studio via oRPC POST …/rules/studio (compareMerchandisingPreview

    • skipMerchandising search for engine-order hits; missing-externalId diagnostics + light Relevance Inspector reasons on the after column). Domain redirect / banner / pin / bury / boost / exclude / diversify + priorities were already live.
  • Zero-result recovery report (O1): GET /analytics/zero-result-recovery returns top asked-zero queries with dominant fallbackStep, cause bucket (unrecovered / typo / keyboard / broadened / other), recovery rate and window totals; console tenantAnalytics.zeroResultRecovery plus synonym draft deep-link (rules?tab=synonyms&suggest=). Built from existing SearchQueryLog.zeroResults + fallbackStep.

  • Query reports (P1-15 / F14): GET /analytics/query-reports returns low-CTR, declining-volume and revenue-per-query lists (days, limit, optional index); console tenantAnalytics includes the same queryReports block. Built from SearchQueryLog + SearchClickEvent (valueCents / queryLogId); PII / hashed digests are filtered out of the rankings.

  • Popular searches per category / locale / window: SearchQueryLog stores locale + category (migration 20261029010000_…); GET /analytics/popular and filtered GET /analytics/trending; suggest/presearch accept category / locale / days. Search may send analytics locale / category (else script detection + single category:= from filters).

  • Presearch personal recent chips read SearchRecentQuery by durable visitor id (client_id / session_id) before falling back to the query log, so hashed logs under SEARCH_HASH_QUERIES no longer blank the empty-box history. POST /embed/presearch and GET /indexes/{index}/presearch accept client_id as an alias of session_id; embed search and POST /recent share upsertRecentQuery (PII / digest strings are not stored).

  • Search understanding on REST /indexes/{index}/search, embed search and the console Playground now reports rating / year / warehouse / lead-time NL filters (rating_filter, year_filter, warehouse_filter, lead_time_filter plus matching *_filter_by fragments). Caller’s q is unchanged for these phrases; fragments AND into filter_by only when the schema has a matching field. Private-field scrubbing covers the new fragments.

  • Index search defaults add attributeUnderstanding (default on): gates those NL attribute filters from the Relevance console. Schema hints expose ratingFields / yearFields / warehouseFields / leadTimeFields.

  • Built-in Relevance templates: eight advisor presets (ecommerce, marketplace, b2bCatalog, classifieds, knowledgeBase, docsSite, supportFaq, media) and eight search-profile recipes (profile-templates.ts) for the Profiles create dialog.

  • Index search defaults and profiles accept Typesense query_by_weights (one integer per query_by field). Relevance Essentials and the Profiles create dialog edit weights per field; misaligned values are rejected on save and dropped on read.

  • Numeric facet histogram buckets (equal-width bands from stats + discrete value counts) on search facets and POST/GET …/facets/:attribute/search. Console Facets preview and Playground render the distribution; not Elasticsearch-style aggregations.

  • Federated autocomplete by type: POST /embed/suggest now returns kinds when settings.suggest.kindField is set; the embed dropdown and console Suggestions panel render one section per kind (flat list unchanged when unset).

Hosting, widget constructor and console UX — 2026-10-29

  • Hosted customer surfaces (/w/{publicId}) are usable across deployment hosts: the widget-session hosted-page bypass now accepts the origin the request was actually addressed to (request Host/forwarded host behind a trusted proxy) as well as the configured deployment origin — a page served from localhost while NEXT_PUBLIC_SAAS_URL names another host no longer 403s with ORIGIN_NOT_ALLOWED. Foreign origins and missing Origin still fail closed; preview tokens and the origin allowlist are unchanged.
  • The public hosted-widget meta route has its own rate-limit tier (embed-meta, 600/min/IP) instead of sharing the 60/min embed bucket, so server-rendered hosted pages do not exhaust a single platform-wide IP bucket; 429 responses keep Retry-After/x-ratelimit-*.
  • Hosted deep links: ?q=, ?page=, ?locale=, ?doc= (recommendations seed) and ?surface=search are applied by the loader (explicit aac_* URL state still wins), plus branded loading/error states, a clear “link expired” page for stale previews, shared hosted-link/iframe hints and theme chrome passthrough (radius, fontFamily, density, showPoweredBy).
  • Per-widget configuration: publishing stores sparse overrides against the current index design instead of a full snapshot, new getSavedWidget (GET) and updateSavedWidget (PATCH) procedures expose one widget's stored overrides + effective config, and the console has a per-widget editor modal. Re-publishing a widget (hosting/origins) no longer overwrites its customisation, and index-design changes still reach keys the widget does not override.
  • Builder parity with the server contract: facets 20, sort options 10, highlight fields 8, hitsPerPageOptions up to 8 values 4–48, perPage 4–48, and string sorts (name:asc, _text_match:desc) — both the builder and the widget runtime.
  • Assistant/voice: assistant-ui message queue in console and hosted chat; microphone capture with a level meter (localised permission/unsupported states) in the composer and Playground voice mode; the embedded assistant gained dictation, read-aloud, retry and a mic-unsupported message; hosted assistant labels follow the widget's string overrides and its speech language follows the widget locale; InstantSearch voice search templates are localised.
  • Adaptive console: documents become one card per row on phones with a sticky Actions column on desktop; admin tables use mobile cards and pin identity/action cells; the admin Search-ops tab strip collapses to a single section select (Tenants and Engine included); shared primitives (checkbox, switch, select, menu items, dialog close, help buttons) get ≥40 px coarse pointer hit areas; the consent banner is compact on phones; dashboard and members tables stack. ConsoleTableFrame no longer forces a 448 px inner width on a 390 px viewport, so sticky action cells stay visible.
  • i18n: the synonym-import placeholder is no longer raw JSON inside an ICU message (rules page MALFORMED_ARGUMENT fixed); the relevance weights tip points at the existing profiles.weightsTip key; new hosted/builder/ assistant/voice keys land in all five locales (parity 5 461 keys).

API changes — 2026-10-28

  • Search Projects and index environments (production / staging / development) removed from the product. Console project/catalog oRPC routes are unmounted; POST /indexes and clone always create production; API keys are never environment-bound; slug uniqueness is per organization (migration 20261028220000_drop_index_environments).
  • GET /rules/{ruleId}/revisions lists newest-first merchandising rule snapshots (settings:write, same as other /rules routes); console oRPC listMerchandisingRuleRevisions mirrors it.
  • Organization GDPR purge (DELETE /organization/data?documents=true) fails closed with ENGINE_PURGE_FAILED when Typesense document delete errors (missing collections still skip).
  • Diversify/MMR curations are written only on settings/ranking paths; search prepareDiversify is read-only and rejects metric mismatch.
  • Client IP for rate limits / forms respects TRUSTED_PROXY_MODE / TRUSTED_PROXY_HOPS (default proxy hops) so spoofed X-Forwarded-For left hops are ignored.
  • REST search preserves the API key's forced filter when applying a saved search profile. Profile defaults cannot replace key restrictions; request and response schemas are unchanged.

SaaS search integration — 2026-10-28

API changes

  • Public schema descriptions use logical index terminology; legacy_highlights maps to the internal highlight compatibility flag without exposing engine versions.

  • Search feature entitlements apply to unbilled console previews as well as API traffic. Native geo filters/sorts and auto-embedding query_by are gated before execution.

  • Direct scoped engine keys default to 403 DIRECT_SEARCH_DISABLED. Managed SaaS browser clients use embed sessions; self-hosted operators can explicitly enable compatibility with SEARCH_ALLOW_DIRECT_ENGINE_KEYS=true. Run the ACL upgrade script to revoke previously issued direct parents on SaaS rollout.

  • Managed references default to asyncReference=true, cascadeDelete=false; synchronous references and engine cascades are rejected to preserve PostgreSQL mirror/accounting. Native capabilities remain visible to platform administrators.

  • Document usage and per-index limits exclude tombstones; restoring a deleted document reserves quota again.

  • Partial update/emplace imports preserve omitted fields in the PostgreSQL rebuild payload.

  • SDK generators cover inline request bodies and enum/union/array types in TypeScript, optional server-side platform admin credentials in TS/PHP/Python/Go, and no automatic replay of non-idempotent TS operations.

Search administration and managed schemas — 2026-10-28

  • Added the platform-only search engine console: collection ownership, cluster resources, diagnostics, runtime configuration, startup validation and queued maintenance. Writes use the existing domain services and audit; platform credentials remain server-side.
  • Customer schema editors preserve advanced fields and collection settings, resolve logical index references and dictionary IDs, and plan rebuilds for changes to existing engine options. Additional safe search defaults can be edited and persisted without allowing tenant/filter overrides.
  • Customer index/schema/ranking responses omit physical collection names; sync jobs expose canRollback. Admin index deletion uses the shared locked lifecycle and rebuild guard.
  • Public product copy describes AACSearch services instead of its internal search provider in all five locales. Documentation in English, Russian and German uses the current public health/voice responses, logical index names and the managed API; competitor comparisons retain their own product names.

API changes — v1

  • PATCH /indexes/{indexSlug}/schema accepts the shared field contract and collection settings, including settings-only patches. Advanced properties survive merges; nested paths are validated. Create accepts up to 250 declared fields; each patch accepts up to 100.
  • Expanded schema field response metadata to include reference, tokenizer, stemming, storage, range and vector/HNSW settings. The SDK and OpenAPI are regenerated from operation metadata.
  • Schema embeddings support source mapping, query/document prefixes, a provider path, recommendation models, and Vertex AI project/region/tasks with OAuth or service-account authentication. Secrets are encrypted and masked; changing credential destinations requires re-entry. Custom token endpoints receive the same URL/DNS validation as embedding endpoints.

API: Typesense 30.2 integration and protocol audit — 2026-10-28

  • Add platform-admin engine catalog/readiness, diagnostics, native resource management, runtime/startup validation and leased maintenance jobs. Stored credentials are redacted; writes require settings:write, use the existing admin gate/rate limit, and invalidate affected search caches. Managed aliases remain behind publication guards.
  • Expand shared REST/oRPC search options, per-field typo/prefix/infix and multi-field grouping; forward union remove_duplicates=false. Correct facet_sample_slope to uint32; fractional inputs now fail validation instead of failing upstream.
  • Extend collection field contracts with string*, tokenizer and asynchronous JOIN options, voice configuration and numeric-array range indexes. Reject unsupported l2 distance instead of sending an invalid Typesense schema.
  • Fix the live-proven ACL empty-array filter bug with server-derived _acl_public, guarded legacy backfill, cache versioning and retirement of v3 scoped parents.
  • Route native NL/voice document retrieval through the shared search pipeline; force curated hits to satisfy tenant/ACL filters. Audio stays in the request body.
  • Replace the unsupported Compose memory flag with max-indexing-concurrency and explicit analytics settings. Running deployments are not restarted by this change.
  • Add administrator-only native recommendations through the shared search pipeline, and translate personalization model metadata/archive uploads to the native wire format.
  • Record all 101 native routes, 57 startup settings and 88 parser options with explicit integration boundaries in docs/reference/typesense.md.

Security: API key hard restrictions (first slice) — 2026-10-28

  • SearchApiKey gains forcedFilterBy, allowedFields, allowedIps, rateLimitPerMinute, profileSlug (migration 20261028083200_search_api_key_restrictions). Existing keys stay unrestricted.
  • Auth middleware enforces allowedIps (403 IP_NOT_ALLOWED). Per-key RPM overrides the global rate limit (capped by SEARCH_RATE_MAX_REQUESTS).
  • REST search AND-merges forcedFilterBy and binds profileSlug (experiments cannot swap a bound profile). Rotation copies the new fields (#139).

1C-Bitrix module aacsearch.search 0.7.1 — 2026-10-29

CMS connector checklist MUST gaps (#52 / #53 / #168 / #169):

  • Catalog events OnPriceAdd / OnPriceUpdate / OnProductUpdate queue price-stock upserts; OnSuccessCatalogImport1C requests a reconcile pass after mass 1C exchange that may skip per-element handlers.
  • Sale OnSaleOrderSaved / OnSaleOrderPaid nudge conversion drain on the next tick (no SaaS call in the order hit). Agent ticks collect orders when track_behaviour=Y even if interactive consent gates views/carts — salted FUSER only, no PDn (Marketplace 2.E hashed identification).
  • Pack: install/updater0.7.1.php + scripts/pack-marketplace-update.sh 0.7.1.

1C-Bitrix module aacsearch.search 0.7.0 — 2026-10-28

Ported from the older module generations (1.8.6 aacworkflow.aacsearch, AACAPP connector, shogo, aac.search 1.2.0) after a side-by-side comparison of all five (#132).

  • SEO text in search. The META_TITLE, PAGE_TITLE and META_DESCRIPTION the shop set on the element itself (b_iblock_iproperty ENTITY_TYPE E, value from b_iblock_element_iprop) are appended to content. Iblock and section templates are skipped on purpose: on Termokit they are one sentence around the name for every product («Купить {=this.Name} с доставкой…»), so they would make «доставка» match the whole catalog. A template without a stored value is computed by InheritedProperty\ElementValues, at most 200 per batch.
  • Price type (option price_group_id, «Тип цены в поиске», default: the base type as before). price comes from that type, and GetOptimalPrice always gets that one row, the base one by default. Before, it priced every type the guest may buy by, and on Termokit that includes «Закупочные»: a single discount, or mode Y, could have indexed the purchase price.
  • Wholesale tiers are ignored: only b_catalog_price rows that hold quantity 1 make the price (QUANTITY_FROM empty or ≤ 1, QUANTITY_TO empty or ≥ 1), the same rule Bitrix uses.
  • Prices stay fresh:
    • A catalog discount added, changed or deleted (OnDiscountAdd/Update/Delete) starts one full sync, after 120 s without further changes and at most once an hour.
    • An action whose ACTIVE_FROM / ACTIVE_TO passes does the same: the tick polls b_catalog_discount, and b_sale_discount (cart rules) too in mode Y or on shops with use_sale_discount_only (Termokit).
    • This happens only while discounted prices are, or just were, in the index. During that full run delta waits, so on a 300k catalog a promotion boundary costs one full pass.
    • A deleted price row (Bitrix\Catalog\Model\Price::OnBeforeDelete) and a deleted SKU offer (OnBeforeIBlockElementDelete → CCatalogSku::GetProductInfo) re-export their product through the queue.
  • Private properties stay out: purchase price, cost price, margin or markup (PURCHASE_PRICE, COST_PRICE, PRIME_COST, SEBESTOIM*, ZAKUPOCHN*, NACENK*, MARGIN, MARKUP, «закупочн», «себестоим», «наценк», «марж»).
    • They are never promoted and never go to attrs or content.
    • The rule also holds at export time, for mappings stored by 0.6.x and for fields an admin enabled.
    • DELIVERY_COST, MIN_PURCHASE and «Минимальная закупка» stay shop data.
  • No raw ids in facets: a list value whose enum row is gone, or a directory value missing from a directory that was read, is empty instead of its ID / XML_ID.
  • Text normalization in content, attrs and property text: NFC (with intl), zero-width characters, soft hyphens and control characters removed. Content hashes change, so the first sync after the update re-sends documents.
  • Measure ratio: new built-in field bx_ratio (float, only when it is not 1 and all offers agree), and the guide cart rounds the quantity up to a multiple of the ratio.
  • Queue b_aacsearch_queue:
    • New columns REVISION, PRIORITY and NEXT_RETRY_AT, added by install/updater0.7.0.php or lazily by the tick (option queue_schema).
    • done() deletes only the revision it read, so a change queued during a push is no longer lost.
    • Price and stock changes are drained first.
    • A failed row waits 1, 2, 4 … up to 120 min instead of retrying on every tick (still 20 attempts).
  • SelfTest: a new «Agents on cron» row warns when the catalog has more than 20 000 elements and ticks do not run from cron.
    • It is advisory and does not fail the self-test.
    • Ticks from cron agents and from the module's own tools/sync.php --tick both count.
  • Queue index ix_aacsearch_queue_pick (ACTION, PRIORITY DESC, ID) for the price- and stock-first read.
  • Schema upgrade survives two requests that upgrade at the same moment.

1C-Bitrix module aacsearch.search 0.6.1 — 2026-10-28

  • The indexed price is the price a guest pays, with discounts (#129): each product and offer is priced with CCatalogProduct::GetOptimalPrice (all-users group, quantity 1, the iblock's site, no session coupons), the cheapest final price is price, and the base price goes to the new built-in field old_price. Filters and price sorting now match the storefront. New option discount_prices («Цена со скидкой в поиске»): auto (default) prices with discounts only when an active catalog discount exists, Y also honours cart rules, N keeps base prices. Cart rules are opt-in because on the Termokit stand 97 active rules discounted none of 3000 products while each call costs ~1.5–2 ms. The approach comes from DiscountReader of the 1.8 module, with detection on D7 tables.
  • The install wizard checks the database (#125): the module is MySQL / MariaDB only, so on the PostgreSQL edition the requirements step shows a failed «MySQL / MariaDB» row and a CLI install stops with a clear message before touching the site, instead of failing halfway through install.sql.

Security: personalised CMS module ZIP needs the console ticket — 2026-10-28

  • The personalised module download (connect=1) no longer issues or rotates a CMS key on a plain GET and no longer takes the API origin from the URL. A cross-site link could send an admin's session cookie (SameSite=Lax allows top-level GETs) and produce a module pointing at a foreign API. The key and the origin now travel only inside the sealed ticket the console mints (#124). Without SEARCH_SECRETS_KEY the console still shows the connection string, and the server logs a warning that the personalised ZIP is disabled.

API changes — v1

  • Changed GET /integrations/{platform}/module.zip?connect=1 — requires ticket (from the console's connect-download call) unless mode=code; without it answers 403 CONNECT_TICKET_REQUIRED and mints nothing. api_url is honoured only with mode=code; the key mode takes the origin from the ticket. The download no longer answers 402 (the key is issued by the console call).

Embed widget: assistant panel, InstantSearch runtime and card config — 2026-10-28

  • The search widget runtime is built on instantsearch.js (packages/embed-widget/src/{widget,search-client,routing,facet-plan,facet-widgets,results,refinements,drawer,insights,templates,strings}.ts): URL routing is namespaced aac_* so it never collides with the host page's own parameters; facet filters (list, range, toggle, hierarchy, select, rating, buckets) apply the same filter_by the console preview shows; a rule redirect is followed only on an explicit submit, not while typing.
  • AACSearch.init() accepts templates.item (a custom result card receiving the hit, escapeHtml, the card roles and defaultCard) and cssClasses (extra class names appended to the card's elements).
  • New in-widget assistant (packages/embed-widget/src/assistant.ts): when the index has the assistant enabled, a launcher button opens a popover that streams answers from POST /api/v1/embed/assistant/stream, with welcome text, suggested prompts, a stop button, [n] citation links to source cards (the result-card markup), thumbs feedback through POST /api/v1/embed/assistant/feedback, and the index's experience settings (showSources, showProductCards, allowFeedback, showAnswerMeta). The guide's "Ask the assistant" chips and assistant_prompt blocks open the panel with the question; assistantUrl stays the fallback link when the assistant is disabled. AACSearch.openAssistant(question?) opens it from the host page. The widget keeps a local copy of the stream event types, type-checked against assistant-stream-contract.ts; nothing of @repo/api is bundled. New widget strings in all five locales: assistantTitle, assistantPlaceholder, assistantStop, assistantSources, assistantError, assistantSend, assistantHelpful, assistantNotHelpful.

API changes — v1

  • Changed GET /embed/widgets/{publicId} — new frameAncestors: string[] (the widget's allowed origins; empty = any site). The hosted page /w/{publicId} sets its Content-Security-Policy: frame-ancestors from it, so the ?embed=1 variant can be framed only by the customer's sites.
  • Changed widget config (widget.config / settings on POST /embed/widget-session, POST /embed/config, console widget builder) — new optional keys cardLayout (list | grid, default list; result cards only — layout remains the recommendations strip), facetBuckets (field → [{label, start?, end?}] bands of a buckets facet), categoryField, badgeField (card roles, unset = conventional names / hidden); strings.<locale> overrides now also validate queries, products, askAssistant, categories, filters, seeResults, clearAll, showMore, hitsPerPage, currentRefinements and the assistant* keys above.

MCP OAuth and HTTP transport — 2026-10-28

  • Complete the browser login, organization selection and explicit consent flow, discovery endpoints and stateless Streamable HTTP at /api/mcp.
  • Require signed continuation parameters, PKCE S256, exact resource/audience, live organization/project permissions and consent. Deny revoked grants and unavailable Redis storage; restrict anonymous registration independently.
  • Align the OAuth schema with Better Auth 1.7.7 resource policies and token rotation fields. Apply migrations before enabling MCP_OAUTH_ENABLED.
  • Add isolated PostgreSQL/Redis OAuth integration checks and browser coverage. Deployment and client registration are documented in docs/MCP-OAUTH.md.
  • Limit CI Next.js prerender pools to two workers and build applications sequentially to avoid multiplying workers across the shared runner host.

Authentication — 2026-10-27

  • Upgrade Better Auth, OAuth provider, SSO and passkey together to 1.7.7. Keep the verification and account lockout migration from the preceding 1.6 update.
  • Deploy all authentication replicas together; users with an in-flight OAuth or SAML login must restart that login after the coordinated security upgrade.

Dependencies / security — 2026-10-27

  • better-auth 1.6.5 → 1.6.33 and @better-auth/sso, @better-auth/oauth-provider, @better-auth/passkey 1.6.5 → 1.6.33 (last 1.6.x; closes the Dependabot critical/high alerts fixed in 1.6.11, 1.6.13 and 1.6.22, #111). better-call 1.3.5 → 1.4.0 follows the core. packages/auth now declares @better-auth/utils 0.4.2 so the plugins resolve the peer version better-auth itself pins (better-call 1.4.0 pulls its own 0.5.x).
  • Add the two-factor verification and account lockout columns required by the plugin. Existing factors remain verified; new enrollment explicitly requires TOTP verification. Apply 20261027222000_two_factor_verification_state before deploying the updated auth code. CI exercises enrollment, TOTP verification, account lockout and recovery against PostgreSQL.

Dependencies: hono 4.13 and axios 1.20+ (security) — 2026-10-27

  • hono catalog ^4.12.14 → ^4.13.14 (lock 4.12.12/4.12.14 → 4.13.14): closes the open Dependabot alerts (serve-static path double-decode GHSA-5r4p-p66f-jhc7, query parser after fragment GHSA-crvj-82cr-hjcx, parseBody dot-notation DoS GHSA-g6gw-c38x-mqfc, toSSG path traversal GHSA-gqvv-2mrq-wpjv, hono/jsx XSS GHSA-hxh3-vqpv-xpqv). No breaking change for hono/cors (we set allowMethods explicitly, so the new default QUERY method does not apply), hono/logger, hono/route, hono/vercel or app.use; app.mount and hono/cache are not used. OpenAPI/SDKs unchanged (sdk:check in sync).
  • axios (transitive only: typesense@3.2.0 and wait-on via start-server-and-test) 1.15.0 → 1.21.0. start-server-and-test catalog ^3.0.2 → ^3.0.13 (brings wait-on 9.0.5 → 9.5.1, which already requires axios ^1.20.0). typesense 3.2.0 is the latest release and declares axios ^1.15.0. The root pnpm.overrides entry axios: "^1.21.0" keeps all copies on the patched 1.x line without admitting a future incompatible major release; a second override hono: "catalog:" lifts the extra hono@4.12.x copy pulled by @prisma/dev (^4.12.8) onto the catalog version so a single hono build is installed. Remove the overrides once those owners move their ranges.

Project shopping guides — 2026-10-27

  • Added versioned expert tech maps, provenance-bearing session facts, deterministic quantity and package calculations, confirmed catalog bindings, exact-SKU recommendations, expiring quotes and idempotent CMS cart hand-off.
  • The Bitrix module can opt in to exact active-offer documents for guides while its regular storefront search excludes those guide-only projections.

API changes — v1

  • Added POST /indexes/{indexSlug}/guide/sessions and the session answer, recommendation, saved-project, quote and cart routes under /guide/sessions/{sessionId}. They require search:read, use optimistic revisions and never let model output supply Typesense filters or cart prices.

Queued schema rebuilds — 2026-10-27

  • Breaking schema changes from the console and REST store an immutable target separately from the live schema and queue the rebuild. History remains planned until publication; cancellation and failures retain the working schema.

  • Publication rechecks the current configuration, schema version, membership, project publication grant or API-key validity and scope. A revoked grant/key or an intervening configuration edit prevents switching the alias.

  • Copy-phase failures finalize the job and migration in one transaction. Other migration failure writes check the current job lease. Lease recovery finalizes exhausted migrations before publication; an obsolete worker cannot fail a migration owned by its successor.

  • Bitrix, PrestaShop and DIAFAN keep pending targets separate from accepted mappings and confirm them only after a successful job poll. Rejected schema publication also leaves alias-scoped stemming dictionaries unchanged.

API changes — v1

  • PATCH /indexes/{indexSlug}/schema with allow_reindex: true returns 202 with applied: false, migration_id and job_id. applied: true is reserved for an already applied schema. Poll the job for completion; enqueueing alone does not change the live schema.

Project roles and console isolation — 2026-10-27

  • Organization owners and administrators can assign project administrator, developer, content manager, analyst and viewer roles. Publication and deletion require separate grants. Explicit activation preserves legacy read access until the organization enables project access control.
  • Every mounted console search operation has a checked policy. Index lists, server-rendered pages, environment selection, analytics and secondary resource lookups respect project visibility; background workers do not inherit the enqueuer's console identity. Membership deletion removes its project grants.
  • Project roles never grant organization administration, billing, API-key or private-field access. Existing private data cannot be exposed through schema changes, cached source previews, recommendation caches or normalized sidecars.
  • Project team edits use revision checks and an audit in the same transaction. Apply the 20261027104500_project_access migration before deploying this code.

Guarded snapshot publication — 2026-10-27

  • Restores verify every configuration artifact against the manifest, as well as compressed document bytes and record counts, before publication. Damaged snapshots leave the live alias in place and suggest choosing another copy.
  • A PostgreSQL row lock protects the final configuration comparison, alias swap and schema write. A concurrent schema, provider-key or settings edit prevents a stale restore from publishing; encryption-key rotation alone is compatible.
  • Restore retains private-field restrictions from both the snapshot and current settings. Alias-scoped stemming is replayed after guarded publication so a rejected restore cannot change the live dictionary.

Embedding credentials at rest and snapshot restore — 2026-10-27

  • Provider keys and credential URL parameters are sealed before schema writes; runtime collection creation and rebuilds decrypt them. Key rotation backfills legacy fields and migration plans with conditional writes that preserve edits.
  • New snapshots mask embedding credentials. Restores resolve those references against the current connection and refuse unmatched credentials before the alias swap; historical keys are never reused from a snapshot.
  • Embedding model, source field, endpoint and key changes produce a rebuild plan. Creating an auto-embedding field enforces the hybrid-search entitlement.

Embedding credentials in schema responses — 2026-10-27

  • Console index lists, ranking, engine fields, schema history and migration results mask provider keys and URL credentials. REST schema plans mask the same values, including dry runs and rebuild-required errors.
  • Schema edits preserve masked or omitted keys at the original provider and endpoint. Changing either requires explicitly supplied credentials; new connections reject masks and ciphertext. Unrelated field edits keep the working vector provider configuration and original signed URL bytes.
  • Partial schema updates keep unsubmitted field definitions; removals remain explicit, so another field edit cannot discard a working provider connection.

Embedding provider credential routing — 2026-10-27

  • Custom embedding URLs never inherit the platform OpenAI credential. They receive only an explicitly supplied customer key. Built-in models and other providers do not receive the OpenAI server key; default OpenAI models retain managed authentication. This applies to creation, field additions and rebuilds through the shared engine schema builder.

Conflicting records within a source — 2026-10-27

  • New isolated feed and native CMS connectors coalesce identical repeated IDs and quarantine IDs with differing mapped content. Other valid records can proceed; accepted versions, cursors and full-inventory deletion stay protected.
  • XML and YAML carry conflict diagnostics before compatibility deduplication. Connection checks reject ambiguous ID mappings before applying a candidate.
  • DUPLICATE_SOURCE_ID provides a recovery hint in every console locale. Quarantine stores mapped positions and a source pointer, without conflicting content. Retry reads the corrected current source. Existing legacy connectors retain their previous last-record policy.

Source configuration changes during synchronization — 2026-10-27

  • Effective connection, mapping, file-manifest and schedule edits fence running jobs with configRevision. Superseded jobs report SOURCE_CONFIG_CHANGED without overwriting current cursors, freshness, failure counts or schedules.
  • Data-setting edits clear the previous cursor and require a verified fresh synchronization. Draft edits, connection checks without a candidate and credential re-keying preserve active work.
  • File-manifest changes queue one fresh follow-up instead of attaching the UI to an obsolete running job. Skip-set writes are conditional on the revision.
  • Deployment requires pnpm --filter @repo/database migrate:deploy.

Isolated connector identities and CMS inventory deletion — 2026-10-27

  • New pull and file connectors isolate record IDs within their connector. The migration preserves existing connector IDs and native record IDs; accepted records retain their native identity for provenance.
  • Source writes check ownership under the same index lock as engine/mirror import. Conflicting legacy sources fail with SOURCE_ID_CONFLICT and a recovery action in all five locales. Cancellation is checked after lock waits.
  • A complete CMS inventory reconciles missing records even when its provider cannot report deleted IDs. Partial reads never infer deletion; empty inventories require a full run. Deletion and pending cleanup stay scoped to the source.
  • Two-site WordPress engine checks cover equal native IDs, independent updates, independent deletion, legacy conflict refusal and reconnection with isolated IDs.
  • Deployment requires pnpm --filter @repo/database migrate:deploy.

File refresh queues and progress — 2026-10-27

  • The console forwards full file refresh requests to the source queue. Queued file imports can be promoted to a full refresh; a full request during a running import creates one shared follow-up. Both job types expose per-file progress.
  • File and other source enqueues lock the same connector row and validate the organization and index before deduplication. Concurrent regular file requests create one import; an existing source sync also prevents a duplicate file run.

Signed connection URLs without query rewriting — 2026-10-27

  • Shared sources and legacy connectors use one credential store for URL query values, legacy inline userinfo and DocSearch start/sitemap URLs. Console reads mask credentials; client-supplied ciphertext is rejected on create and update.
  • New authenticated URL envelopes preserve raw value encoding, duplicate keys, parameter order and all other URL bytes. Masked edits retain each original value separately; endpoint changes and ambiguous duplicate removal require fresh credentials. Previous encrypted URLs remain readable and rotatable.
  • Plaintext size limits are checked after opening stored envelopes. Runtime diagnostics also mask noncanonical encoded values. A signed HTTP fixture rejects any query-byte change during the real catalog/engine checks.
  • Regular file synchronization retains its skip set even though file connector rows use full mode; only an explicit full refresh forces re-extraction.

Current-source retries and stale-record protection — 2026-10-27

  • All connector failures, including native CMS and file extraction, retry the current source through a queued full synchronization. Retrying no longer imports an obsolete stored body or a file removed from the current manifest.
  • A verified successful full read resolves earlier connector errors in the same owned completion transaction, after deletion cleanup. Partial reads and lost jobs cannot dismiss those errors.
  • Manual retries require the latest failure and an existing never-accepted failed document under the index write lock. Accepted, deleted, purged and orphaned source documents require a fresh import or source connection.
  • Full file refreshes re-extract unchanged-size files; regular runs retain their skip set. Recovery actions are translated in all five console languages.

CMS synchronization completion and recovery — 2026-10-27

  • Native CMS imports use bounded batches with cancellation and worker ownership checks around source requests and writes. A superseded worker cannot advance the source cursor or successful synchronization time.
  • Rejected records retain their previous deletion state. Failed engine deletions remain pending and are retried before synchronization can report completion.
  • Incomplete reads and rejected writes retain the previous cursor. Full reads do not move a timestamp cursor backwards when the newest source record disappears.
  • A disposable WordPress HTTP, PostgreSQL and Typesense integration check covers 1,001 records, worker takeover, cancellation, pending deletions and budget limits.

Connection diagnostics and rotated-key access checks — 2026-10-27

  • Connector and connection-check diagnostics mask credentials in signed URLs, encoded query values and Basic authorization headers. Very short credentials use a fixed error detail while retaining the actionable error code.
  • Corrected the rotation regression test: an environment-bound successor remains forbidden from organization-wide webhooks. A separate positive test confirms that an unbound successor retains access and its expired predecessor is refused.

Full synchronization follow-ups and cancellation — 2026-10-27

  • A full refresh promotes a queued source job or queues a fresh run behind work that has already started. The request no longer disappears into a running delta.
  • Resolving earlier mapping errors is fenced by the worker's owned job row. Connector progress reaches completion after deletion cleanup rather than before it.
  • The sync dashboard shares the worker's cancellation types and protected phases, exposing cancellation for source checks, connector runs and the other queued jobs. Cancelling a connection check clears its validation state atomically and retains any existing working connection. A cancelled scheduled occurrence advances to its next normal time without recording a source outage or retry backoff.

Explicit source attribute mapping — 2026-10-27

  • Source and collection forms map arbitrary attributes to declared index fields, including typed lists, decimal conventions and explicitly defined availability values. Bindings can inherit or replace the source's attribute mapping.
  • Schema compatibility and private-field settings are checked before binding, candidate promotion and synchronization. Sensitive public fields and objects require explicit visibility confirmation; previews mask private source paths.
  • Malformed records enter a diagnostic queue without storing unselected raw values. Valid records can proceed, while failed mappings prevent deletion of older records. Retry queues a fresh full source read; successful complete reads resolve earlier mapping errors.
  • Discovery shows nested fields, observed types and sample uniqueness. Failed mappings retain a clearly marked candidate preview; CSV, XML and YAML previews include the original records. Stable numeric identities retain their identity when their content changes; invalid explicit identifiers are rejected.

Serialized source runs and project suspension — 2026-10-27

  • Manual jobs and scheduler runs share a connector lock. A replacement worker waits in the queue while the previous worker finishes, without recording a source outage or consuming an attempt. Lost or cancelled jobs stop before further import and cannot overwrite the replacement job's outcome.
  • Queued jobs suppress scheduled duplicates. Paused projects stop source synchronization while their existing search results remain available.
  • Disposable PostgreSQL and Typesense checks exercise takeover during a blocked source request, successful queued retry and project suspension.

Verified lexical intent and complete source deletion — 2026-10-27

  • Generic document queries retain brand, category and use-case words when the index cannot represent them as structured search. Explicit sorting retains lexical terms when it replaces the proposed ranking expression. Entity removal respects Unicode word boundaries and preserves unsupported negations.
  • A complete, explicitly requested full read of an empty source removes only that source's records. Large deletion sets drain in bounded pages before synchronization reports success. Cleanup failure keeps the previous freshness and cursor and exposes DELETION_PENDING; budget failures expose BUDGET_EXCEEDED.
  • Destructive prune commits under the current job lock. Previously pending deletions can finish on a later run without affecting another source's cleanup.
  • Added a disposable PostgreSQL/HTTP/Typesense integration check covering source identity, actual indexed results, updates, deletion of 1,001 source records and preservation of published results after a rejected budget operation.

Scoped budgets and leased synchronization — 2026-10-27

  • Organization owners can set independent monthly resource and spending limits for an organization and each project. The console shows usage, reservations, remaining capacity, warnings, currency and the start of accounting.
  • Search, document ingest, AI and rebuilds reserve resources before work. Failed preflight releases the plan quota; partial imports consume only accepted new documents. Actual wallet debits enforce monetary caps inside the ledger transaction.
  • Replayed wallet references do not consume a second budget. Project allocations and the cash basis are recorded with each debit. Provider usage above an AI estimate is retained in project accounting instead of being lost.
  • Manual connector runs now return a queued job. Concurrent enqueues for the same connector share its active job. A worker that loses its lease cannot advance source freshness, overwrite the new worker or start another import slice.
  • Apply 20261027050000_usage_budgets along with the source migrations before startup.

Shared sources and logical collections — 2026-10-27

  • Projects now contain independent collections and reusable sources with per-collection mappings. Sources can feed multiple collections; collections can combine sources.
  • Connection changes are staged and verified by leased preview jobs before replacing a working connection. Preview does not index documents. Source and binding pause, archive and detach operations preserve published documents.
  • Source identities are isolated by default; original record ids and provenance remain in the document mirror. A repeated id in another source cannot overwrite its data.
  • Protected feeds support bearer, basic and custom-header credentials; secrets and signed URL parameters are encrypted, redacted and included in key rotation.
  • Connector synchronization can run as a queued job. Apply the source/collection and source-identity migrations before starting the updated app.

Projects and synchronization correctness — 2026-10-27

  • Organization projects with purpose, languages, audience, seven lifecycle states, readiness checks, index assignment and a localized console in five languages. Existing indexes remain accessible and can be assigned explicitly.
  • Project writes check organization membership and revision, and commit their audit atomically. Creation retries preserve identity after later edits.
  • Incomplete connector reads report partial completion and preserve freshness. The last successful synchronization is stored independently of later attempts; cancellation prevents the final cursor and freshness update.
  • Timestamp cursors compare actual instants across offsets and retain fractional precision. Alias provisioning failures clean up the collection they created.
  • Apply the two new additive migrations with pnpm --filter @repo/database migrate:deploy before starting the updated application.

Outbound request safety and trace privacy — 2026-10-27

  • URL trace attributes now remove authority credentials (user:password@host), including URLs without a query string and protocol-relative URLs. Scheme, host, port and path remain available for diagnostics. Existing query/fragment redaction continues to apply before export.

  • Guarded outbound requests now use fetch and Agent from the same Undici package, avoiding Node bundled-fetch dispatcher incompatibility while retaining the connection-time DNS rebinding guard.

Outbound requests and trace privacy — 2026-10-27

  • Fixed every outbound request made through the SSRF-guarded safeFetch failing with fetch failed / invalid onRequestStart method on Node 22 and 24: the guarded dispatcher comes from the undici package (major 8) but was handed to the global fetch, which Node implements with its own bundled undici (6.x on Node 22, 7.x on Node 24). safeFetch now pairs the dispatcher with fetch from the same package. SSRF checks (shape, allowlist, DNS, connection-time rebinding, redirect credential stripping) stay in force; there is no unguarded fallback.
  • Fixed the tesseract OCR upload leaving as text/plain with body [object FormData]: the form is built from undici's FormData so the paired client brand-recognises it.
  • Webhook deliveries now send an explicit User-Agent, AACSearch-Webhooks/1.0 (+https://aacsearch.dev).
  • Trace URL attributes also remove authority credentials (user:password@host), including URLs without a query string and protocol-relative URLs.
  • Node is pinned to the active LTS line, 24.x, in .nvmrc, package.json engines and CI (node-version-file: .nvmrc).

API documentation — 2026-10-27

  • /api/docs now serves Scalar with a selector for the public Search v1 contract (/api/v1/openapi.json) and the session-authenticated Console/Auth contract (/api/spec.json).
  • Fixed console OpenAPI generation: analytics and refresh use /api/search/organizations/{organizationId}/analytics and /analytics/refresh, with optional indexId filtering. oRPC procedure names and inputs are unchanged.
  • Declared the API package's tsx development dependency so its OpenAPI generator runs after a normal workspace install. Generation still requires the configured environment.

Platform-owner administration — 2026-10-27

  • Added 25 owner report routes, Customer 360, grouped desktop/mobile navigation, 50-row pagination, server-side filters and current-page CSV export. Existing operational controls remain linked from customer, installation, product, billing and security reports.
  • Reported CMS versions and storefront stages, safe site origins, effective plan allowances, activity cohorts, AI tokens, process-local HTTP monitoring and read-only engine capacity.
  • Corrected owner financial definitions: usage statements are not added to wallet top-ups as cash revenue; unknown cash/costs remain unavailable. Catalog MRR excludes trials and overdue subscriptions, uses provider currency overrides and discloses missing contract quantities.
  • See docs/ADMIN-BUSINESS.md for data sources, coverage limits and validation boundaries.

CMS modules — 2026-10-27

  • Bitrix 0.5.2: admin UX from UX-SPEC §4 — 9 CAdminTabControl tabs with a status banner, install requirements check + data-processing consent note (step.php), uninstall with keep/drop data (unstep1.php, check_bitrix_sessid, key always cleared, SaaS disconnect best-effort); shopper behaviour gated by visitor consent (track_consent_required, aacsearch_consent cookie / Main\UserConsent); SelfTest, Updater (updater0.5.2.php), canonical site_url in connect payloads, docs/CONNECT-API-CONTRACT.md; portable Storefront::globBrace (no GLOB_BRACE, musl/Alpine).

  • Bitrix 0.5.1: hotfix — register Behaviour in include.php (search page and scheduler tick no longer fatal before heartbeat); click beacon uses a salted visitor (sessionId / anonymousId, no basket user created) and still salts a legacy int FUSER_ID from old event.php; index/key change or disconnect resets behaviour cursors and the embed token. Batch-level /embed/events wire format unchanged from 0.5.0. Marketplace update: run integrations/bitrix/aacsearch.search/scripts/pack-marketplace-update.sh 0.5.1 to build 0.5.1.zip (0.5.1/updater.php, description.ru windows-1251, changed files; CopyFiles('install/tools', 'tools') → /bitrix/tools/aacsearch/event.php).

  • Bitrix 0.5.0: shopper behaviour for the product graph and recommendations (option «Собирать поведение покупателей», off by default). The scheduler tick reads what Bitrix already writes — product views (b_catalog_viewed_product), carts and deferred items (b_sale_basket), orders (b_sale_order, from 1C and the admin too), the last 90 days on the first run — and sends them to /embed/events with a short-lived embed token minted by the module's key, so installed shops need no new key scope. Search-result clicks are sent by the results page (/bitrix/tools/aacsearch/event.php, session-checked). The shopper is a salted hash of the basket user, never a user id, e-mail or IP. New component aacsearch:recommendations (STRATEGY co_view «Смотрят вместе», complement «С этим товаром покупают», substitute, next) for the product page: graph neighbours first, catalog similarity after, products a guest cannot see dropped, cached 10 minutes, nothing shown while recommendations are off for the index. Verified on the Бизнес stand from real storefront requests: views, carts, orders and a search click arrived, the graph rebuild produced co_view / next / complement edges, and the product page shows «Пантолеты Кости на Пляже» as bought together.

  • Bitrix 0.4.4: the header search box sends Enter to the AACSearch results page (/search/) when the site has one; shop templates point the native box at /catalog/, so on Бизнес / Малый бизнес the full results came from the catalog's own search while suggestions came from AACSearch.

  • Bitrix 0.4.3: a files-only update (sync, manual copy, Marketplace) now also refreshes the storefront components in /local/components/aacsearch and /bitrix/tools/aacsearch on the next scheduler tick (files_version); before, they stayed at the version of the last install. The results page no longer offers API-only facets (hierarchy_lvl*, unit, site ids) or a second «Бренд» built from the property the built-in brand already uses; pickup points get a label.

  • Bitrix 0.4.2: property writes through CIBlockElement::SetPropertyValuesEx / SetPropertyValues (1C exchange, imports) reach the index right away (OnAfterIBlockElementSetPropertyValues(Ex)), for products and offers alike; before they waited for the next delta. Create / update / property / deactivate / delete, price, stock, offers, sections, activity dates, console full_sync and reconciliation verified end to end on the Бизнес and Старт stands (docs/BITRIX-EDITIONS.md §12).

  • Bitrix 0.4.1: search finds what the site's own search finds (CIBlockElement::UpdateSearch) plus what shoppers filter and type by. Properties the shop marks «Участвует в поиске» go to content (when an iblock uses the flag at all; demo / 1C iblocks without flags keep every property), and so do smart-filter, offer-variant, brand and article properties, now also when they are promoted fields. Articles are searchable in latin and cyrillic look-alike spellings (T-100 / Т-100) and are never a facet. Flag-like values («false», «Нет», empty GUIDs, colour codes) stay out of the text. tags carry the section names with their ancestors and the element TAGS; hierarchy_lvl0..3 carry the main section path for hierarchical facets. New sortable signals bx_sort (SORT), bx_views (SHOW_COUNTER) and bx_orders (placed orders, sale module); views and orders change no TIMESTAMP_X, so they are refreshed by a full sync.

  • Bitrix 0.4.1: what the storefront hides stays out of search — elements linked only to deactivated sections are skipped (option «Не искать в отключённых разделах», on by default) and removed by delta and reconciliation; a section switched on or off, renamed or moved re-exports its elements (new OnBefore/OnAfterIBlockSectionUpdate handlers, paged by the delta). Elements whose ACTIVE_FROM / ACTIVE_TO passes appear or expire although TIMESTAMP_X did not change: the delta pages them by (date, ID), and since those dates have no index it scans only when the nearest known date has come (edits update it) or hourly. 0.4.0 read the dates with filter keys Bitrix ignores and re-sent up to 5 000 elements every delta. Smart-filter properties that the 2 000-element sample missed are kept by an SQL count (385 such properties on Termokit). The settings page marks the iblocks the site's own search covers and names the ones not selected yet.

  • Bitrix 0.3.0: facets follow the shop's own catalog setup — properties shown in the smart filter (b_iblock_section_property, with their display type) and offer-variant properties (OFFER_TREE, read only when property features are on) are promoted first; LIST_PAGE_SHOW adds a small bonus.

  • Bitrix 0.3.0: catalog editions (Малый бизнес / Бизнес) export bx_unit, bx_weight_g and bx_store_stock (active pickup points with stock; never back-office warehouses or addresses); bx_stock_qty only with the new «Публиковать точный остаток» option; multi-site installations export bx_site_ids. Старт / Стандарт are unchanged (no catalog).

  • Bitrix 0.3.3: the module has its own admin menu «Сервисы → AACSearch — поиск» (connection and status, sources and fields, site search and schedule) instead of living only in «Настройки модулей».

  • Bitrix 0.3.2: settings page in the admin speaks the admin's language — step times formatted like the rest of the page, run and reconciliation statuses translated, field reasons («справочник + умный фильтр») translated; capabilities show price types, stores, pickup points, smart filter and property features; the analysis summary says when there is no product catalog; service, empty and unsupported properties are folded under «Не используются». Install / uninstall verified on Старт, Стандарт, Малый бизнес, Бизнес and the Termokit shop.

  • Bitrix 0.3.1: store stock changes (OnStoreProductAdd / OnStoreProductUpdate / OnBeforeStoreProductDelete, which the 1C exchange fires too) queue the product for the next delta run; the delta's catalog and price polling reads in TIMESTAMP_X order and resumes from the last row instead of dropping everything past the first 2 000 changed rows; the scheduler tick registers event handlers that a file-copy update did not install.

  • Bitrix 0.3.1: after a file-copy update the scheduler re-registers the running module version, so the panel stops offering an update that is already installed; properties that can be neither a field nor an attribute (service, empty, unsupported) are no longer listed in the panel's field table.

  • Console: the integration card names the edition (Малый бизнес, not small_business) and says what the module found on the site (catalog, offers, smart filter, pickup points, price types, sites). The CMS wizard no longer pre-selects an index that already holds other documents (it proposes the first empty production index or a new one) and proposes Russian as the catalog language for Bitrix / DIAFAN shops. The pull «1C-Bitrix» connector, which needs an export module that is not shipped, is no longer offered for new sources (existing ones keep working).

  • Bitrix 0.3.0: capabilities report price types, stores, pickup stores, smart filter and property features (read lazily, not on every element save); the promoted-field budget counts applied fields that are no longer promoted.

Customer journey — 2026-10-26

  • One first-run entry for website and API users, with workspace creation and a resumable Data → Check → Connect wizard. Profile/avatar setup is no longer required before using search.
  • File import previews and field mapping; scoped API examples; widget installation and external-request verification. Preview traffic does not confirm installation.
  • Auth preserves safe local return destinations, displays only configured OAuth providers, and offers email resend/recovery. Required authentication emails propagate delivery failures, including Resend error responses. Stable widget assets revalidate every five minutes.

Widgets and billing — 2026-10-26

  • Saved widget loader at /embed/v1/loader.js: public-id bootstrap, refreshed sessions, search, autocomplete and recommendation mounts; publication and deletion in the console.
  • Polar: update subscription seats through the installed SDK and pass the initial seat count to checkout.

CMS modules — 2026-10-26

  • Bitrix 0.2.1, PrestaShop 2.1.1 and DIAFAN 2.0.1: isolate integration commands, retry acknowledgements, preserve working connections on failed replacement, reset cursors when switching indexes, and enforce current storefront visibility.
  • Bitrix: bound large catalog registration reports and document transactions; preserve later theme edits when undoing search wiring. DIAFAN state checkpoints now use atomic upserts. Autocomplete ignores responses superseded by cleared input.
  • Deploy script includes the separate Termokit Bitrix stand and bundles the generated PHP SDK.

API changes

Every new, changed, deprecated or removed public route under /api/v1 gets one line here, under the heading of the API version it ships in, in the same PR as the route (see AGENTS.md). Format: - **Added|Changed|Deprecated|Removed** `METHOD /path` — what a client has to know. Breaking changes only ever land under a new major heading (### v2), never under ### v1.

v1

  • Added GET /embed/widgets/{publicId} — token-free public meta of a hosted widget ({id, type, name, index:{name}, hosted:true, theme:{accent,dark}}); 404 unless the widget is enabled and hosted. IP rate limited (embed tier).

  • Added POST /embed/assistant/stream — the widget assistant on the embed session token (token, question, conversation_id?, client_id?, filter_by?). Same SSE frames as POST /indexes/{slug}/assistant/stream (citations → delta… → done | error); conversation_id is handed out bound to the index and visitor; 403 ASSISTANT_DISABLED when the index has the assistant off; 429 per session token (20/min) and per address (30/min).

  • Added POST /embed/assistant/feedback — rate a widget answer (rating: 1 | -1, message_id, conversation_id, client_id, comment); same write path as POST /feedback.

  • Changed POST /embed/widget-session — accepts the deployment origin (the hosted page /w/{publicId}) when the widget has hosting enabled, or with preview: <console session> for a draft share link; the response adds widget.hosted, preview and settings.assistant (enabled + experience fields). POST /embed/config adds the same settings.assistant block.

  • Changed widget config (widget.config / settings on /embed/widget-session, /embed/config) — new optional keys titleField, urlField, descriptionField, facetTypes (field → list|range|toggle|hierarchy|select|rating|buckets), hierarchyFields[], resultsMode (pagination|infinite), hitsPerPageOptions[], layout also accepts list, template, showClearRefinements, mobileDrawer; facets max raised from 8 to 12; recommendations strategy accepts complement|next|co_view|substitute (the legacy personalized|related|trending|popular stay valid as aliases).

  • Changed POST /indexes/{slug}/quality/sets/{id}/queries — judgments[].grade is an integer 0..3 (wider values are refused with 422, not clamped), at most 200 judgments per query; a set over its query cap answers 422 SET_TOO_LARGE with details.max.

  • Changed POST /indexes/{slug}/quality/sets — the created set is returned with queries: 0, runs: 0 (counts, same shape as the list).

  • Changed POST/PUT /indexes/{slug}/rules — a boost/bury action's filter.value is string | number | boolean (numbers and booleans are no longer coerced to strings).

  • Changed POST /indexes/{slug}/snapshots/{id}/restore — the worker verifies configuration and document integrity, retains private-field restrictions, and rejects stale publication after concurrent schema or settings edits.

  • Changed POST /indexes — accepts the documented fields and settings with the same validation as console creation, including embedding configuration. Externally supplied credential ciphertext is rejected.

  • Changed PATCH /indexes/{slug}/schema — returned plans and rebuild-required error plans mask embedding provider keys and URL credentials; masked schema edits preserve credentials only for the unchanged provider and endpoint.

  • Changed GET /jobs/{jobId} — per-file results are also returned for connector_sync jobs that perform a full file refresh, matching file_import.

  • Changed POST /connect/exchange — missing SaaS / session API origin answers 500 API_BASE_URL_NOT_CONFIGURED (neutral public message; was 503, which CMS modules retry). Unused ZIP-issued CMS keys are rotated inside the same exchange transaction so reconnects do not pile active keys.

  • Added POST /connect/opened — CMS Marketplace tab marks a connect state as opened (openedAt; expired → 410 CONNECT_EXPIRED, consumed → 409 CONNECT_CONSUMED).

  • Added POST /connect/callback — partner callback reveals the one-time code (or 200 {ok,error} for BITRIX_NO_RIGHTS without a code); domain mismatch → CONNECT_DOMAIN_MISMATCH.

  • Added POST /connect/verify — Bearer key health after exchange; optional state marks the session verified; returns {ok, latency_ms}.

  • Added POST /connect/disconnect — Bearer key revoke + mark push integration disconnected (index documents kept); already-revoked keys → 200.

  • Added POST /connect/exchange — CMS module exchanges a one-time connect code + state for an index-bound API key (server-to-server; reuse/expiry → CONNECT_CONSUMED / CONNECT_EXPIRED). Optional return_url on success when the session was created with one.

  • Changed GET /integrations/{platform}/module.zip — mode=code&state= embeds {v:2,url,code,state,index} without a key; a broken ticket answers CONNECT_TICKET_INVALID and does not mint a replacement key.

  • Changed POST /indexes/{index}/scoped-key — optional search params (query_by, sort_by, include_fields, exclude_fields, facet_by, per_page, limit_hits, prefix, num_typos, group_by, group_limit) are locked into the scoped key; tenant filter_by + private/acl excludes always win. Private-field refs on those params answer PRIVATE_FIELD.

  • Changed Assistant / RAG context — emails, phones and long digit runs in retrieved chunks are replaced with placeholders before the LLM sees them.

  • Added POST /rules/preview — dry-run a draft merchandising rule against a caller-supplied hit list (with_published also applies live rules); returns hits, redirect, banner, applied_rules. Requires settings:write. No write.

  • Changed POST /indexes/{index}/events — optional tags (string[]) and metadata (string/number/bool map) are stored under the event's metadata (with apiKeyId); never used for ACL.

  • Changed POST /indexes/{index}/search, GET /indexes/{index}/search and POST /embed/search — merchandising may return redirect, banner ({text, url?}) and rules_applied; rules accept match.whenZeroResults and optional action.banner.

  • Changed Curation save (REST + console) — rule.tags triggers via search curation_tags (Typesense curation sets).

  • Changed POST /indexes/{index}/search, GET /indexes/{index}/search and POST /embed/search — article codes stay in q: a hyphenated code (144-14-02, T-100-20) or a descending / glued pair (170-70, 120-60см) is no longer read as a price range (it became price:[144..14] and q was cut to -02); only a standalone ascending pair (1000-2000, 5000 - 9000 руб) is.

  • Changed POST /indexes/{index}/search, GET /indexes/{index}/search and POST /embed/search — a code-like query no longer adds a hard external_id:=… filter — external_id is the connector's document id, not the article; a sku / article / model / part_number / product_code field still gets the exact filter.

  • Changed POST /recommendations — accepts seed_document_ids (cart / multi-seed, max 20) walked with the product graph; strategy also accepts commerce aliases replacement / analog / analogue (→ substitute) and accessory (→ complement). An out-of-stock seed with no strategy/filter auto-selects substitute neighbours filtered by in_stock:=true when the schema has a stock field.

  • Changed Assistant citations may include page (1-based) when chunked file hits carry a page field; LLM calls abort after assistant.llmTimeoutMs (default 30s) and fall back to extractive answers.

  • Added File connector accepts .xlsx (first sheet → TSV text). NL understanding maps warehouse / lead-time phrases onto schema fields when present. Embed AACSearch.optOutProfiling(). Console recommendation settings expose consentRequired and personalizationImpact. Org overview totals include coverageRate / rescuedRate.

  • Changed Query alternatives retry Cyrillic↔Latin unit spellings (128гб ↔ 128gb). Negations like «не красный» become hard color:!= filters. Optional settings.search.distanceThreshold is passed into hybrid vector_query. Embed widget renders guide.blocks (branch / assistant_prompt).

  • Added DELETE /indexes/{index}/subjects/{subjectKey} — DSAR erasure of query logs, click/behaviour events and recent queries for one visitor/user on the index (settings:write, server-side key). Raw ids are hashed with SEARCH_PII_SECRET the same way as intake.

  • Changed POST /indexes/{index}/assistant, POST /indexes/{index}/assistant/preview and POST /indexes/{index}/assistant/stream — optional acl (user:… / group:… / role:…) is applied to retrieval the same way as search: server-side keys only; origin-bound / browser keys stay anonymous so RAG cannot see documents search would hide.

  • Changed POST /recommendations and POST /similar — optional filter_by (stock, brand, …) is AND-ed onto candidate lookups; the seed document is still loaded without it. strategy also accepts bought_with as an alias of behavioural complement (often bought together). Event intake accepts favorite; cart and favourite events feed _popularity between click and conversion.

  • Changed GET /organization/export — the JSON also includes query_logs and click_events (up to 5 000 newest each) so a tenant can take their visitors' search and click history with them; purge via DELETE /organization/data already erased those tables.

  • Changed guide on POST /indexes/{index}/search, GET /indexes/{index}/search and POST /embed/search — guide.route is question or catalog (a question mark, a leading question word, or Russian «ли»; the caller's q is not rewritten). A question adds a chip with kind: "assistant". Chips may also include dropFilter (re-search without that filter fragment) and kind. understanding.route is set when price or commerce understanding ran.

  • Added match on POST /indexes/{index}/search, GET /indexes/{index}/search and POST /embed/search — how much of the query the first hits reflect: {query, coverage, level, matched, unmatched}. coverage is the share of meaningful query words (Russian and English function words such as «для» / «the» excluded; words turned into a price filter, brand or colour count as found) found in the best of the first five hits; level is full, partial (≥ 0.5), weak or none; clients should treat none and weak as «nothing found». It describes the text of the hits, not their relevance. Absent for *, queries of function words only, pages after the first and hits returned without highlights (an include_fields that leaves out the queried fields).

  • Changed zero-result recovery — the or-tokens and drop-last-token steps no longer treat Russian and English function words as query words: «зарядка для самоката» is no longer answered by every product containing «для», and a query with fewer than two meaningful words skips or-tokens.

  • Added POST /indexes/{indexSlug}/feed-preview — reads an xml/yaml/csv/json source URL (scope documents:write, nothing is indexed) and returns detected fields, a suggested versioned mapping, a mapped sample and the index fields it needs. Connector configs accept an optional mapping (fixed transforms: strip_html, to_number, to_bool, split, join, default, lowercase, date_to_unix, first_non_empty).

  • Changed GET /organization/export — nested audit metadata also strips internal topology keys, including physical storage names and aliases; customer document titles and URLs are preserved.

  • Changed POST /indexes/{indexSlug}/scoped-key — browser keys are restricted to the returned public handle for one index/environment. Parents are isolated per API key and index; revocation/rotation invalidate every index of that issuer. Shared and v2 wildcard parents are retired on first mint/revoke after upgrade, so older browser keys must be re-minted.

  • Changed document imports and edits — writes to one index are serialized across app instances, including revision checks, so parallel imports cannot allocate the same revision or leave the engine and mirror in different write orders. Lock contention past the database wait deadline answers 409 CONFLICT.

  • Changed GET /indexes/{index}/suggest, POST /indexes/suggest/multi, POST /embed/suggest — word and phrase completions include additive completionType; validated layout, typo and adjacent-letter corrections are exposed as did_you_mean, with price constraints preserved on selection.

  • Changed search responses expose spelling hints from successful public token matches too; layout recovery reports the query that actually found hits.

  • Added POST /embed/suggest — token-scoped, rate-limited autocomplete; returns document completions and recognized price filters without consuming search quota.

  • Changed GET /indexes/{index}/suggest — additive active_filters and completionQuery preserve recognized prices when a completion is selected; suffix/infix completions use only fields indexed with infix: true.

  • Changed POST /indexes/multi_search — federated multi-search is not index-scoped: the indexes are named in the body, and POST /indexes/{index}/multi_search answers 404. The generated SDKs call the unscoped path.

  • Changed POST /indexes/{index}/reindex — answers 202 {index, job_id, status: "queued", status_url} instead of blocking; poll GET /jobs/{jobId}. 409 JOB_CONFLICT (with details.job_id) while a rebuild is already queued or running. ?sync=true keeps the old blocking 200 (documents, duration_ms) for test suites only.

  • Changed POST /indexes/{index}/docsearch/preview — queues a crawl job and answers 202; the pages, errors and first 25 records are in job.result of GET /jobs/{jobId} once completed.

  • Added GET /jobs/{jobId} — status, phase, progress (0–100), result of a reindex, crawl, file import, snapshot or restore job of the key's organization (another tenant's id is 404 JOB_NOT_FOUND). Needs the scope that started the job.

  • Added DELETE /jobs/{jobId} — cancels a job before the alias swap; 409 JOB_NOT_CANCELLABLE when it finished or is already swapping.

  • Added POST /api-keys/{keyId}/rotate — settings:write; body {grace_days?} (default 7, max 30); 201 with the successor's plaintext key (shown once) and previous_key.valid_until. 409 KEY_ALREADY_ROTATED / KEY_NOT_ACTIVE. A successor never gets scopes the caller lacks.

  • Changed authentication — a key superseded by a rotation whose grace has ended answers 401 KEY_ROTATED (was UNAUTHORIZED).

  • Changed /admin/* (GET/DELETE /admin/snapshots, POST /admin/snapshot, POST /admin/jobs/run) — requires x-platform-admin-token equal to SEARCH_PLATFORM_ADMIN_TOKEN in addition to the API key; 403 FORBIDDEN otherwise, and the surface is closed when the secret is unset. Every call (allowed or denied) is audited.

  • Changed POST /admin/snapshot — 503 SNAPSHOT_NOT_CONFIGURED when SEARCH_SNAPSHOT_DIR is not set (an absolute path on the engine's filesystem).

  • Changed GET /metrics — Prometheus text exposition 0.0.4 with Accept: text/plain or ?format=prometheus; JSON stays the default (new keys gauges.ingest_jobs, cache). The token may also be sent as Authorization: Bearer.

  • Changed GET /status — throttled per client address like /health (429).

  • Added POST /indexes/{index}/snapshots — settings:write + documents:write; 202 {job_id, snapshot_id, status, pruned}. 409 SNAPSHOT_LIMIT_REACHED beyond the plan's snapshotsPerIndex unless ?prune=true.

  • Added GET /indexes/{index}/snapshots — complete snapshots, newest first (documents:read).

  • Added GET /indexes/{index}/snapshots/{snapshotId} — manifest plus a 15-minute signed download URL for the gzipped NDJSON (documents:read); 404 SNAPSHOT_NOT_FOUND.

  • Added DELETE /indexes/{index}/snapshots/{snapshotId} — both write scopes.

  • Added POST /indexes/{index}/snapshots/{snapshotId}/restore — 202 {job_id}; new collection then alias swap. 409 JOB_CONFLICT while a reindex or another restore is queued or running.

  • Added GET /billing/invoices — billing:read; ?limit 1–100; monthly usage invoices.

  • Added GET /billing/invoices/{id} — one invoice as JSON; {id}.pdf returns the PDF.

  • Added POST /billing/invoices/generate — platform admin only (x-platform-admin-token); ?period=YYYY-MM (default: last closed month) and ?organization_id. Idempotent.

  • Changed search responses (GET/POST /indexes/{index}/search) — new rerank field ("applied" or "skipped:<reason>") when the index has a reranker; explain object for explain=true (incl. explain.rerank); hit_reasons[].reasons[].kind gains popularity and rerank.

  • Changed private fields — fields in settings.search.privateFields are removed from hits, highlights, facets, GET /documents/{id} and /documents/export unless the key has the new scope documents:read_private; referencing one in query_by, filter_by, sort_by, facet_by, include_fields or group_by without it is 400 PRIVATE_FIELD. Embed requests never see them.

  • Changed POST /indexes/{index}/documents — response may carry normalizationErrors (per field: values a schema normalize rule for date / price / geo could not parse; written untouched).

  • Changed POST /indexes/{index}/clone — body accepts environment (production | staging | development, default: the source's); the response echoes environment. A key bound to another environment gets 403.

  • Changed every index-scoped route — a key bound to an environment answers 404 for an index of another environment (deliberately not 403).

  • Changed POST /indexes/{index}/scoped-key — refused with 400 PRIVATE_FIELDS_PRESENT for an index with settings.search.privateFields unless the issuing key has documents:read_private (the engine ANDs the embedded filter with the browser's, so found would be an oracle on private values). A key in its rotation grace window gets 403 KEY_IN_GRACE; expires_in is capped at the parent key's remaining validity. Scoped keys are not revoked with their parent.

  • Changed PUT/DELETE /indexes/{index}/presets/{id} — rate limited per key (429, x-ratelimit-* headers) like the other write routes.

  • Changed GET /billing/usage, GET /billing/wallet — documented: require billing:read (403), rate limited per key (429).

  • Changed GET /indexes/{index}/snapshots/{snapshotId} — documented: download is null with a note unless the key has documents:read_private.

  • Changed /webhooks/* — documented: 403 for a key bound to an index or an environment.

  • Changed POST /api-keys/{keyId}/rotate — a successor never widens the caller: 403 KEY_ENVIRONMENT_MISMATCH when an environment-bound key rotates a key of another environment, 403 KEY_LIFETIME_EXCEEDS_CALLER when the target key (or its successor) would outlive the calling key, 403 FORBIDDEN for a target allowed on origins the caller is not, 403 KEY_IN_GRACE when the calling key is itself in a rotation grace window. The successor keeps the environment of the key it replaces (aac_test_ stays aac_test_).

  • Changed authentication — revoking a key in the console also ends its rotation grace window: the next request is 401.

  • Changed GET /organization/export, DELETE /organization/data — require settings:write; 403 for a key bound to an index or an environment (both act on every index of every environment). The export also refuses origin-restricted (browser) keys; the purge with ?documents=true additionally needs documents:write.

  • Changed POST /indexes/multi_search — every searches[].index is resolved like a single search: in the key's environment (another environment's index is 404) and, for an index-bound key, only its own index (403). Previously a slug present in several environments resolved to an arbitrary one.

  • Changed filter_by (search, multi-search, facet search, assistant) — && and || are accepted (were 400 INVALID_QUERY). facetFilters are merged as (<filter_by>) && (<facets>), so a || b plus a facet now constrains both operands. The private-field check runs on the merged filter and on trimmed / quoted / negated field names (" cost_price:10" is 400 PRIVATE_FIELD).

  • Changed GET/POST /indexes/{index}/facets/{attribute}/search — applies the document ACL like search (principals only for server-side keys) and refuses a private attribute, filter_by or facetFilters with 400 PRIVATE_FIELD unless the key has documents:read_private.

  • Changed POST /indexes/{index}/assistant/native — applies the document ACL; private fields are never sent to the model or returned, and naming one in filter_by / query_by is 400 PRIVATE_FIELD.

  • Changed search quota — a search that fails after its unit was reserved (engine error, PRIVATE_FIELD, rerank or rules failure) never consumes the unit.

  • Changed search result cache — changing a merchandising rule invalidates cached responses of the index (the rules version is part of the cache key).

  • Changed POST /quality/sets/{setId}/run — queues an eval job and answers 202 {run_id, job_id, status: "queued", status_url}; metrics appear in GET /quality/runs/{runId} once the run is done. ?sync=true keeps the old blocking answer with metrics.

  • Changed POST /indexes/{index}/reembed — one batch per call (?batch=, default 20, max 200, was a hard 20) with an opaque cursor to pass back as ?cursor= until truncated is false; ?all=true queues a reembed job for the whole index (202 {job_id, status_url}), 409 JOB_CONFLICT while one is active, 501 when the engine has no auto-embedding.

  • Changed GET/DELETE /jobs/{jobId} — new job types eval and reembed (both read and cancelled with settings:write); file_import result gains ocr_pages; a key bound to an environment gets 404 JOB_NOT_FOUND for jobs of other environments. Cancel effect by type: reindex / restore drop the half-built collection, snapshot publishes nothing, file import and reembed stop between files / batches (work already written stays), a crawl's result is discarded, an eval run is closed as failed.

  • Changed POST /indexes/{index}/reindex — 409 JOB_CONFLICT also while a snapshot restore is queued or running, and the same check now applies to ?sync=true (with details.type), which also rebuilds in the index's own environment (a staging rebuild no longer touches production).

  • Changed POST /indexes/{index}/documents and every write path — a metadata value that is already a JSON string is stored as is (restores and reindexes no longer double-encode it); _popularity / graph_* in a client payload are still dropped, and their server values survive full re-upserts, reindex and snapshot restore.

  • Changed GET /billing/usage — documents counts production indexes only; staging and development documents are neither capped by included.documents nor billed as overage.

  • Changed GET /billing/invoices* — overage lines come from the period's own wallet settlements, priced with the plan and block price recorded at settlement (not today's plan); blocks the wallet could not pay for appear as a separate, uncollected overage line.

Unreleased

Tooling

  • Added @shadcn/lint (oxlint jsPlugins) with warn-level no-raw-colors / no-arbitrary-values, plus pnpm lint:design ratcheting max-warnings for apps/saas + packages/ui. Playwright a11y smoke (apps/saas/tests/a11y-shells.spec.ts) covers login/signup shells (desktop/mobile, light/dark, keyboard focus, no horizontal overflow, axe serious/critical). See docs/development/design-lint.md.

API

  • Changed POST /embed/events — each event may carry occurred_at, session_id, visit_id and event_key. Historical occurred_at / per-event visitor / event_key require an embed session minted with backfill: true (events:backfill scope, server-side API-key mint without browser Origin). Ordinary page tokens use server time, ignore client event_key, and store hashed session/visit ids. Events older than 90 days or in the future are dropped (not re-stamped). Oversized value_cents are clamped; invalid events in a batch are listed in rejected (201) so a CMS cursor can advance; write failures answer 503. The OpenAPI contract keeps the typed event item schema for events[] (the handler parses the batch leniently and validates per item), and documents the 201 body (recorded, rejected[]) and 503.
  • Changed POST /embed/session — optional backfill: true adds events:backfill to the token scope; refused with BACKFILL_NOT_ALLOWED when the request carries a browser Origin.

CMS modules

  • Bitrix 0.5.3: shopper-behaviour rewrite for the product graph — cursor-ordered pagination; per-event wire() with session_id / visit_id / occurred_at / site-prefixed event_key ({SITE_ID}:view|cart|order:…, view:ID:TS for repeat views); empty query_id omits the click key; value_cents clamped to 100_000_000; embed session minted with backfill: true; cursor advances only after /embed/events accepts the batch; 429 pauses collection; send window 85 days; rememberShown / recommendations breaker. Keeps the 0.5.2 consent gate (shopEnabled() + visitorConsentGranted() before any FUSER hashing; enabled() requires an interactive hit with consent while track_consent_required) and the portable Storefront::globBrace from #11/#26. Marketplace update: install/updater0.5.3.php next to the 0.5.2 one: B_PROLOG_INCLUDED guard; load Updater from archive __DIR__/lib/Updater.php then the installed module; fail with $updater->errorMessage / exception + Log when missing (no silent return). The 0.5.3 pack file list is a follow-up; pack-marketplace-update.sh is unchanged.

Search corrections and admin usability

  • Reviewed all 55 SaaS page owners and walked their URLs in the local browser; route coverage and validation boundaries are recorded in docs/LOCALHOST-3000-AUDIT.md.
  • Fixed language persistence, malformed onboarding steps and premature OTP validation; added localized accessible labels across account controls.
  • Restricted organization logo upload signing and organization mutation controls to their actual permissions. Subscription cancellation on account/organization deletion now runs in authorized Better Auth lifecycle hooks.
  • Added page loading/error recovery, an expected unavailable-invitation state, environment-correct analytics links and safe document/citation URLs.
  • Added labeled word/phrase completions and selectable spelling hints in the console and embed widget. Keyboard-layout recovery supports phrases, mixed correctly typed words, short words and model numbers without rewriting the caller's primary query. Price and visibility filters stay on recovery probes.
  • Fixed duplicate sidebar keys for Overview/More; groups can be expanded and collapsed, with an active-page marker and larger mobile controls.
  • Fixed trailing search debounce and URL pagination in the user/organization admin lists. Request errors have retry actions, unavailable organizations cannot become create forms, and renaming keeps existing organization URLs.
  • Added safe wallet input validation, selected-period semantics, URL search persistence and refresh feedback to platform Search ops.

Console structure and analytics

  • Search inputs now show prefix/infix completions and removable price chips; selecting a completion preserves the price. New/rebuilt collections index title for infix matching; older collections need a rebuild for suffix matches.

  • Changing a field's infix option is treated as a rebuild instead of a no-op.

  • Index pages share one tenant/environment server guard; overview data and schema role hints are served by protected oRPC procedures.

  • Index tabs and the sidebar share one navigation registry, preserve nested environment links and select the canonical organization dashboard.

  • /admin consistently opens Search ops; its charts share the dashboard period and refresh action. Latency cards show window p95, not the maximum daily p95.

  • Invariants now reject direct search-engine imports from console routes/modules.

2026-10-25 — bug and security fixes

Console, ingest and operator side of the fixes; REST-visible changes are listed under "API changes → v1" above.

  • SSRF — safeFetch judges IPv6 forms with an embedded IPv4 (mapped, compat, SIIT, NAT64, 6to4, Teredo), pins the socket to the checked address (undici Agent, guarded connect.lookup), strips authorization / cookie / proxy-authorization and caller credential headers once a redirect leaves the original origin, and refuses a cross-origin 307/308 of a request with a body. SEARCH_ALLOW_PRIVATE_URLS is ignored in production unless SEARCH_ALLOW_PRIVATE_URLS_IN_PRODUCTION=true.
  • Connector credentials — changing a connector's endpoint without re-entering its stored secrets fails with CREDENTIALS_REQUIRED; a secret value starting with enc: is refused with INVALID_SECRET_VALUE.
  • Secrets key — key chain SEARCH_SECRETS_KEY (current) / SEARCH_SECRETS_KEY_PREVIOUS (decrypt-only) / legacy SEARCH_WEBHOOK_SECRET_KEY; a set-but-malformed key (not exactly 32 bytes) fails with SECRETS_KEY_INVALID; re-encryption with pnpm --filter @repo/api secrets:rotate (webhooks, connectors, SSO client secrets; idempotent, --dry-run). Runbook: docs/RUNBOOKS.md §6.
  • SSO — a domain is verified only by DNS: _aacsearch-challenge.<domain> TXT "aacsearch-domain-verification=<token>" (requestSsoDomainVerification / verifySsoDomain, owner only); new or re-domained providers start unverified and cannot sign in until then. Organization slugs that collide with auth provider ids (google, github, credential, …) cannot be SSO provider ids (SSO_PROVIDER_ID_RESERVED). Runbook: docs/RUNBOOKS.md §7.
  • Field-level security — members (not owner/admin) get console documents without private fields.
  • Jobs — rebuild invariants: environment required by reindexIndex, assertNoActiveRebuild on every blocking path (sync reindex, breaking schema migration → 409), shadow name from resolveShadowCollection, alias-guarded dropShadowCollection, mirror delta and hard deletes replayed before the swap; unleased running jobs failed after SEARCH_UNLEASED_JOB_MAX_RUNTIME_MS (6 h).
  • Billing — overage ledger references carry the organization and are unique per wallet (migration 20261026030000_billing_fixes; before, a second organization's block N was treated as a replay and never charged); dunning ignores out-of-order and replayed provider events (event id and time per provider), resolves the organization from the stored purchase first, and only reacts to the purchase that actually decides the plan; quota notices for documents re-arm only when usage drops below the threshold.
  • Ingest — opt-in OCR for scanned PDFs (settings.search.ocr, http or tesseract-server, compose profile ocr); settings.search.languageFields (per-language stemmed copies); DOCX zip-bomb guard (ARCHIVE_TOO_LARGE) and extraction time budget (EXTRACT_TIMEOUT, 60 s, plus 5 min for OCR).
  • Known limits — see docs/ARCHITECTURE.md §11 and docs/LAYERS-GAP-BACKLOG.md: the SSO plugin calls IdP endpoints at sign-in without the SSRF guard; providers saved before DNS verification keep domainVerified = true until reset (owner decision pending); scoped keys are not revoked with their parent; Lemon Squeezy has no event id (composite id used); the Creem envelope is taken from its docs, not from a live delivery.

2026-10-25 — layers backlog (docs/LAYERS-GAP-BACKLOG.md, 31 tasks)

By layer of the 25-layer model:

  • Perimeter (2) — docs/DEPLOYMENT.md, infra/Caddyfile, infra/docker-compose.prod.yml; /health and /status never leak engine host/port (perimeter-privacy.test.ts).
  • Auth (4) — OIDC SSO per organization (better-auth sso, enterprise plan, console settings/sso, local Keycloak under infra/keycloak); platform-admin token for /admin/*.
  • Tenants (5) — index environments production / staging / development with environment-bound API keys (aac_live_ / aac_test_), separate nonProductionIndexes cap.
  • Caching (10) — Typesense use_cache + Redis result cache keyed by index revision (settings.search.cacheTtl, default 60 s), 30 s in-process config cache, cache hit/miss counters in /metrics and tenant analytics.
  • ML (13) — optional cross-encoder rerank of page 1 over HTTP; click popularity _popularity rolled up daily and usable as a profile tie-break (popularityBoost); SearchIndex.embedModel / embedModelVersion with a config-audit warning on mismatch.
  • Document processing (15) — file connector (PDF / DOCX / TXT / MD via signed upload), document language _lang (facetable), ingest normalization of date / price / geo by schema.
  • Reindexing (17) — reindex and DocSearch crawl run as scheduler jobs (202 + /jobs), abandoned rebuilds recovered after a crash.
  • Index management (18) — tenant snapshots and restore per index (MinIO bucket index-snapshots, plan retention).
  • Billing (20) — monthly usage invoices with PDF, dunning (active → past_due → suspended after BILLING_GRACE_DAYS, default 7), 80 % / 100 % quota notifications.
  • Data security (21) — connector secrets encrypted at rest (AES-256-GCM, enc:v1:), API-key rotation with grace and expiry reminders 7 days ahead, audit log page in the console (settings/audit, CSV export), field-level security (privateFields).
  • Observability (22) — OpenTelemetry spans per search stage (OTEL_EXPORTER_OTLP_ENDPOINT), Prometheus /metrics, optional Sentry via SENTRY_DSN, alert rules in infra/alerts.yml.
  • Documentation (25) — docs changelog page, docs/RUNBOOKS.md, CI pnpm sdk:check.

Known limits are listed in docs/LAYERS-GAP-BACKLOG.md → "Residual gaps after implementation".

  • Smart suggest: document prefixes, tenant-popular completions, typo fallback, did_you_mean, <mark> highlights, 30s per-tenant cache, ETag/304, multi-index endpoint.
  • Semantic and hybrid search via OpenAI embeddings (semantic=true, alpha).
  • Natural-language search, native RAG, in-app RAG with citations and SSE streaming.
  • Voice search (Whisper) and image search (vision → query).
  • Union multi-search merged and re-ranked server-side.

Platform

  • Multi-tenant API keys with scopes, index binding, allowed origins, expiry and rotation.
  • Quotas, wallet overage billing, audit log, zero-result dashboard alerts.
  • Customer dashboard with KPI cards, charts, top/no-result queries, per-index table, plan meters.
  • Signed webhook ingestion, GDPR export/erasure, CSV analytics, slow-query report, on-demand jobs endpoint and engine snapshots.

Developer experience

  • OpenAPI 3.1 document, generated TypeScript and Python SDKs, Postman collection, load-test harness, and 94+ end-to-end checks.

On this page

ChangelogMobile spacing + shell polish — 2026-10-31Theme + console/marketing IA polish — 2026-10-31Appwrite pink + Helvetica theme — 2026-10-31Green main checks — 2026-10-30Docs + marketing UX polish — 2026-10-30Search REST explainability parity — 2026-10-30Embed / widget gallery console UX — 2026-10-30Webhooks + org members EmptyState UX — 2026-10-30Relevance settings console chrome — 2026-10-30API keys / billing / usage EmptyState UX — 2026-10-30Sync error recovery UX — 2026-10-30Merchandising rule conflicts API — 2026-10-30Playground explainability UX — 2026-10-30UX wave 4 — wire drawer / carousel / menubar / navigation-menu — 2026-10-30Design-lint ratchet (187 → 8) — 2026-10-30Console JsonPreview / EmptyState audit — 2026-10-30Data-table view options + faceted filters — 2026-10-30Schema loading error state — 2026-10-30Marketing section composition (Tailark / Origin / HyperUI) — 2026-10-30@repo/ui deferred shadcn primitives — 2026-10-30Playground Resizable + shared Sortable — 2026-10-30Post-UX-merge type cleanup — 2026-10-30Admin shared table / filter / KPI format — 2026-10-30Documents page UX (data-table reuse) — 2026-10-30Playground primary search layout — 2026-10-30Assistant UX (sources, cards, approvals, activity) — 2026-10-30Console Wave 1 UX (Schema / Mapping / Sync) — 2026-10-30Rules visual order / preview / conflicts — 2026-10-30Shopping Guide need → field → value mapping — 2026-10-30Onboarding Steps progress (Wave 3) — 2026-10-30Data-table patterns on @repo/ui — 2026-10-30@repo/ui missing shadcn primitives — 2026-10-30Console page density — 2026-10-30Commerce Flow Studio closure (visitor + versions) — 2026-10-30Commerce Flow org definitions (Studio) — 2026-10-30Index Studio navigation — 2026-10-30Commerce Flow Agent wave 3 — 2026-10-30Commerce Flow Agent Studio — 2026-10-30Commerce Flow Agent — 2026-10-30Widget renderer consistency — 2026-10-30Bitrix public features — 2026-10-30Bitrix smart filters — 2026-10-30Public CMS assistant — 2026-10-30Resilience / console exports — 2026-10-30Sync UI — 2026-10-30Index lifecycle — 2026-10-30Documents / geo — 2026-10-30Tooling — 2026-10-30Search quality / ranking / billing — 2026-10-30API changes — 2026-10-30CMS modules — 2026-10-30SaaS search UI — 2026-10-28Security — 2026-10-28API changes — 2026-10-29Hosting, widget constructor and console UX — 2026-10-29API changes — 2026-10-28SaaS search integration — 2026-10-28API changesSearch administration and managed schemas — 2026-10-28API changes — v1API: Typesense 30.2 integration and protocol audit — 2026-10-28Security: API key hard restrictions (first slice) — 2026-10-281C-Bitrix module aacsearch.search 0.7.1 — 2026-10-291C-Bitrix module aacsearch.search 0.7.0 — 2026-10-281C-Bitrix module aacsearch.search 0.6.1 — 2026-10-28Security: personalised CMS module ZIP needs the console ticket — 2026-10-28API changes — v1Embed widget: assistant panel, InstantSearch runtime and card config — 2026-10-28API changes — v1MCP OAuth and HTTP transport — 2026-10-28Authentication — 2026-10-27Dependencies / security — 2026-10-27Dependencies: hono 4.13 and axios 1.20+ (security) — 2026-10-27Project shopping guides — 2026-10-27API changes — v1Queued schema rebuilds — 2026-10-27API changes — v1Project roles and console isolation — 2026-10-27Guarded snapshot publication — 2026-10-27Embedding credentials at rest and snapshot restore — 2026-10-27Embedding credentials in schema responses — 2026-10-27Embedding provider credential routing — 2026-10-27Conflicting records within a source — 2026-10-27Source configuration changes during synchronization — 2026-10-27Isolated connector identities and CMS inventory deletion — 2026-10-27File refresh queues and progress — 2026-10-27Signed connection URLs without query rewriting — 2026-10-27Current-source retries and stale-record protection — 2026-10-27CMS synchronization completion and recovery — 2026-10-27Connection diagnostics and rotated-key access checks — 2026-10-27Full synchronization follow-ups and cancellation — 2026-10-27Explicit source attribute mapping — 2026-10-27Serialized source runs and project suspension — 2026-10-27Verified lexical intent and complete source deletion — 2026-10-27Scoped budgets and leased synchronization — 2026-10-27Shared sources and logical collections — 2026-10-27Projects and synchronization correctness — 2026-10-27Outbound request safety and trace privacy — 2026-10-27Outbound requests and trace privacy — 2026-10-27API documentation — 2026-10-27Platform-owner administration — 2026-10-27CMS modules — 2026-10-27Customer journey — 2026-10-26Widgets and billing — 2026-10-26CMS modules — 2026-10-26API changesv1UnreleasedToolingAPICMS modulesSearch corrections and admin usabilityConsole structure and analytics2026-10-25 — bug and security fixes2026-10-25 — layers backlog (docs/LAYERS-GAP-BACKLOG.md, 31 tasks)SearchPlatformDeveloper experience