This is the platform changelog. It tracks notable user-facing changes to KaireonAI — new features, APIs, UI additions, and behavioral shifts — in reverse-chronological order. New capabilities land here when they are deployed toDocumentation Index
Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
Use this file to discover all available pages before exploring further.
playground.kaireonai.com.
For the forward-looking view, see the roadmap.
2026-05-12 (later) — Channel coupling, compute error surface, DNC
Channel-level coupling replacesGroup.allowPartial. The legacy
all-or-nothing allowPartial: false switch was a coarse fail that
didn’t distinguish between “empty due to operator’s choice” and “empty
due to contact policy fatigue”. It’s now deprecated and a no-op (the
field still parses to keep existing IRs valid). Per-channel coupling
takes its place:
Channel.couplingMode("partial"default,"atomic"opt-in) — when atomic, an empty placement in this channel suppresses sibling placements in the same channel only. Cross-channel coupling is intentionally NOT supported — different channels are different attention surfaces.DecisionFlow.couplingOverride— per-flow override that beats the channel default. Useful when one channel serves both atomic flows (e.g. weekly digest email) and partial flows (e.g. transactional email).- The post-group coupling pass writes
trace.summary.channelCoupling[]so consumers can distinguish “we cascaded because X” from “this placement wasn’t configured.”
personalization key just went
missing. Now each failure lands in personalization._errors[] on the
candidate with { name, kind, formula, error }, plus a count in
trace.summary.computeErrors. The candidate stays in the response —
operators can filter upstream if they want missing fields to drop the
candidate entirely.
do_not_contact contact-policy ruleType is now properly wired. It
was seeded by industry-accelerator templates but the contact-policy
engine had no explicit case — every DNC’d candidate fell through to the
fail-closed default branch with a misleading “Unknown rule type” error
log. Now there’s an explicit case "do_not_contact" that blocks with a
clear reason and policy id. This is the only contact policy that
suppresses across channels (legal/regulatory boundary).
2026-05-12 — File-arrival triggers (push + poll) + blue_green column cast
Triggers are now first-class — file_arrival no longer waits inside a scheduled run; the run itself is fired by the sentinel landing.- Poll path — the in-process scheduler tick now sweeps every active
pipeline with
trigger.kind === "file_arrival"alongside its schedule sweep. It probes the source’s configured path (via the same cloud-store wiring the source executor uses), matches keys against the trigger’s per-pipelinecontrolFilePattern, debounces againstlastRunAt + debounceSeconds, and dispatches the run in-process. - Push path — new
POST /api/v1/triggers/file-arrivalendpoint accepts both native{pipelineId, tenantId, objectKey}payloads and S3 EventBridge envelopes. Self-authorizes viaCRON_SECRET. Sub-second latency when S3 → EventBridge → API Destination is wired up. - Per-pipeline masks —
controlFilePatternlives on the trigger, so multiple pipelines watching the same inbox each declare their own sentinel (customers.done,accounts.done,propositions.done). Drop one → only the matching pipeline fires. - Sentinel cleanup — after a successful fire, the sentinel is
archived to
atomicity.successFolder(orfailureFolderon a failed run) so the same trigger doesn’t re-fire on every tick. - Deadline enforcement —
trigger.deadline.windowMinutes/onMissis now actually checked.alertemits a system-health warning;failwrites a synthetic failedPipelineRunso SLA dashboards count the miss;skiplogs and continues. Dedup key is the pipeline’slastRunAtso the action fires once per missed window.
runBlueGreen previously did
INSERT INTO <table>_new SELECT * FROM <staging>, which Postgres
rejected with 42804 (column "created_at" is of type timestamptz but expression is of type text) because staging columns are TEXT and
target columns are typed. The target executor now passes its
column-aware projection (NULLIF + ::pgCastType) into runBlueGreen, so
blue_green produces the same explicit casts that append/truncate/upsert
do. A leftover <table>_new from a prior failed run is dropped before
the new CREATE-LIKE.
LineageTab key warning — the row map was using <>...</> shorthand
which doesn’t accept props; switched to <Fragment key={...}> so React
stops warning about missing keys.
2026-05-02 (evening) — Schema-joins UX + customer profile lookup
- Customer Lookup was returning “no customer found” for valid
IDs because the profile route only matched schemas with
entityType="customer". The schema-create form setsschemaType="customer"but leavesentityTypeat its default “custom”, so the lookup missed the actual customer table. Fixed: the route now matches either field. - Schema-joins page redesigned around the customer-as-primary best practice. Primary schema is no longer a picker — the tenant’s customer schema is auto-resolved and shown as a read-only chip with its primary key. The foreign-schema dropdown excludes the customer schema (no self-joins). Foreign-key column auto-populates when a column matching the primary key exists, otherwise the user picks from a dropdown of the foreign schema’s actual columns. The redundant primary-schema field picker was removed.
2026-05-02 (late PM) — Three bugs caught in testing
Surfaced during the IAM-role + custom-PK end-to-end test:- Schema detail no longer fakes an
id BIGSERIALrow when the user defined a custom PK. The list of AUTO columns now matches the actualds_*table contents —created_at+updated_atonly (when a custom PK is set), or those plusid(when no custom PK). - Pipeline Runs
rowsProcessedwas 4× the real count because the run-handler reducer summedrowsLoaded + rowsOutacross every node. Now it only sums target rows. Existing run records in the test tenant were backfilled. - Sample-row preview evaluates the common single-call formulas
inline (
concat,coalesce,min,max,round,abs, identity reference). Complex formulas still defer to the runtime but with a friendlier placeholder. Default sample row no longer miscategorisesstate(and other fields containing “at”) as a date.
2026-05-02 (also PM) — {YYYY-MM-DD} token expansion fix
Found during the same E2E test: archived files in S3 / GCS / Azure
were landing at literal .archive/{YYYY-MM-DD}/ instead of
.archive/2026-05-02/. The local_fs archive helper expands tokens,
but the cloud-store impls took the destination verbatim. The source
executor now expands {YYYY-MM-DD} / {YYYYMMDD} / {YYYY} /
{MM} / {DD} / {HH} / {mm} / {ss} before calling
store.archive().
2026-05-02 (PM) — System Health + load-mode safety
Two coupled shipments in the same day: System Health widget (docs). New top-barActivity icon — distinct from the bell — surfaces operational alerts
across the platform. DB-backed with per-user read state. New API at
/api/v1/system-health (GET cursor-paginated, PATCH read, POST
read-all, DELETE dismiss). Severity taxonomy info | success | warning | error | critical; 30-second polling that pauses on tab background;
error/critical route to existing external Slack/Teams/email
providers when configured. A retention purge cron at
/api/v1/cron/system-health-purge honors each tenant’s retention
settings (data class system_health, default 90 days). The previously
dead bell icon + in-memory notification store are retired.
Load-mode safety (docs). Real
data-loss footguns closed:
truncateandblue_greennow run an empty-source guard by default — pipelines no longer wipe live tables when the upstream produces 0 rows. Override per-target withfailOnEmptySource: false.truncateandincremental_watermarkwrap the destructive + INSERT statements inprisma.$transactionso a failed INSERT rolls back the TRUNCATE / watermark advance.incremental_watermarknow persists the high-water in apipeline_watermarkscheckpoint table (falls back toMAX(target.col)on first run for backwards compat).upsertwith all columns inupsertKeynow throws at SQL build time instead of silently emittingON CONFLICT DO NOTHING. UI +parsePipelineIRblock the misconfiguration before save.- TargetForm gains: full-refresh-shape hard warning recommending
blue_greenovertruncate,failOnEmptySourcetoggle for destructive modes, mode-switch validation matrix, dedicated config panes forupsertKey/watermarkColumn/cdcSource. - Source
onMissAction: alertfinally fires an actual alert into System Health. - Optional
expectedRowCountDeltaper target node emits awarningalert when today’s load is wildly outside the recent average.
id BIGSERIAL column at table creation; the
runtime has supported user-defined PKs since the DDL helper landed.
Docs: System Health ·
Loading Modes ·
File Ingestion · Data Model
2026-05-02 — Flow editor UX cleanup
A focused pass cleaned up rough edges in the IR-native flow editor:- Lineage tab no longer 500s on tables with
bigintor Postgresnumeric/decimalcolumns. The lineage payload now serialises large integers as strings (preserves precision pastNumber.MAX_SAFE_INTEGER) and renders Decimal columns in their human-readable form, solifetime_valueetc. render as"10552.97"instead of internal-state JSON. See Flow Lineage. - Pipeline Runs heading aligned with sidebar — the standalone runs
page H1 now matches the “Pipeline Runs” sidebar entry, the table
uses fixed column widths via shadcn
<Table>, and the Error column truncates with atitle=tooltip for the full text. - Editor is now 2-pane — the redundant “Recent runs” left pane was
removed; the bottom strip + dedicated
/data/flow-runspage cover run history. Center pane fills the freed width. - Branch node form —
thenanddefaultroute inputs are dropdowns of existing IR nodes (excluding self + sources). Stale refs render with a red border. - Enrich node form — output-field input now badges columns missing
from the destination schema and offers a one-click ”+ Add as
<dataType>” button that POSTs to
/api/v1/schemas/fieldswith a sensible default type per provider. - Archive node form — connector picker (Select limited to S3 / GCS
/ Azure / SFTP /
local_fs), per-connector folder-creation help text (cloud=auto-create, SFTP/local=parent must exist), and a Test connection button reusing the existingPOST /api/v1/connectors/test. IRarchiveNodeSchemagains an optionalconnectorIdfield (backwards compatible — runtime executor still parses destination URLs until the cross-cutting wiring lands). - Transform + Validate sample-row preview — collapsible widget
inside both forms takes one JSON row and shows a per-op before/after
diff (added/removed/changed fields highlighted) or per-rule pass/fail
badge. Complex ops (
aggregate,lookup_join,vector_embed,geo_resolve,sentiment_score,language_detect) and theexpressionop render as “preview-limited — run the pipeline” since they need server-side runtime context.
2026-04-29 — Decisioning depth + ecosystem surfaces
A multi-pass sprint landed across 14 capability surfaces. Highlights:- Counterfactual training — pre-train hook augments the
gradient_boostedtraining set with synthetic neighbors of marginal rows. See Counterfactual Training. - Cross-offer ranking constraints — a new constraint type with
three rule shapes (
channel_quota,portfolio_budget,category_cap) feeds the existing Lagrangian solver. See Lagrangian ranking. - KernelSHAP —
POST /api/v1/decisions/:id/shapnow dispatchesgradient_boostedto TreeSHAP andneural_cfto KernelSHAP. See SHAP. - Three new fairness metrics — Gini coefficient, DeLong paired-AUC test, two-sample Kolmogorov-Smirnov. See Advanced Fairness.
- Multi-stage four-eyes approvals + DSAR purge cron — approvals
now move through a sequence of named stages with per-stage state
transitions;
GET /api/v1/cron/dsar-purgesweeps decision traces, interaction history, and AI attachments past the strictest tenant retention setting. See Governance four-eyes. - Negotiation apply-mode + multi-turn sessions — three new routes
(
POST /api/v1/decisions/:id/negotiate/apply,POST /api/v1/negotiate/sessions,POST /api/v1/negotiate/sessions/:id/turn) with a 7-gate apply pipeline. See Negotiation Apply-Mode. - 26 new connector entries added — registry expansion spanning
CRM / MAP / CDP / audience-sync / helpdesk / workflow vendors. New
entries ship as
coming_soonform-only stubs. - 4 new pipeline transforms —
vector_embed,geo_resolve,sentiment_score,language_detectwith HTTP-pointed runtime adapters underlib/flow/runtime/transforms/external-model-call.ts. - SCIM 2.0 + WebAuthn + SIEM audit-log shipping —
/scim/v2/Usersendpoints, full COSE-key parse with ES256 / RS256 assertion verification, and Splunk HEC / Datadog Logs / Elastic_bulkbackends gated by SSRF validation. - Multi-region overlay —
helm/values-multi-region.yamlchart for 2-region active-active topology, plus per-tenant region routing driven by a new tenant-region pinning table. - In-repo SDK + CLI + Postman + MCP scaffolds — TypeScript SDK,
Python SDK,
npx kaireonCLI, Postman v2.1 collection, and MCP marketplace manifest undersdks/. - OpenAPI auto-discovery —
tools/scripts/gen-openapi.mjswalks everyapp/api/v1/**/route.tsand emitsplatform/public/openapi.jsoncovering the full v1 surface.
platform/prisma/manual-sql/09_parity_w11_to_w19.sql
(idempotent) creates 5 new tables and backfills existing single-stage
approvals.
2026-04-17 — Action Insights + Reporting Platform
Four coordinated phases shipped as a single milestone: close analytics gaps, make alerts actually fire, ship a full report builder and scheduler, and deliver a C-suite executive dashboard with Export + Save-as-Report across every view.Pilot deployment posture. This release ships as manual-only
automation. The alert evaluator, report scheduler, and scheduled
report runner all run through
/api/cron/tick, but CRON_TOKEN and AWS
EventBridge are intentionally not wired during pilot to avoid
runaway LLM / notification cost. Run Now buttons, Export buttons,
ad-hoc notification sends, and on-demand alert evaluation all work
unconditionally. See
EventBridge Setup for the optional
wiring path, and the roadmap for the pilot guardrails we
plan to ship before enabling automation by default.Phase 01 — Analytics Foundation
New analytical primitives that power every downstream surface in this milestone.- New
dashboard-datacaseselection_frequency— per-offereligibleCount,scoredCount,selectedCount,selectionRate,avgRank, andrankDistribution[]. AcceptschannelId,categoryId,decisionFlowId,segmentIdfilters. - New
dashboard-datacaseanomaly_candidates— compares current vs. baseline period across acceptance rate, revenue, and degraded scoring rate; classifies severity (info / warning / critical) from z-score + absolute percent change. - New
dashboard-datacasewhy_not_ranked— aggregate misses per offer:scoredTooLow,filteredByContactPolicy,filteredByQualification, andbeatenBy(top-5 competing offers). - Segment dimension added to
acceptance_rate,offer_performance,offer_performance_grouped,channel_effectiveness,daily_trend,revenue_trend. - Enriched decision-trace JSON shapes — structured
rejectionReason,rankBefore/rankAfteron scoring results. - Cross-decision narrative helpers that explain offer underperformance, segment coverage, and anomalies in plain language.
Phase 02 — Notification Providers + AlertRule Execution
Pluggable notification system and live alert evaluation.- Pluggable notification provider interface + registry with Slack, Microsoft Teams, outbound webhook, and Ops-Email (SES) adapters.
- New notifications tab in
/settings/integrations— add / test / enable / disable / delete destinations. - Encrypted credential storage in the platform-setting vault (AES-256-GCM); GET endpoints return redacted configs.
- An alert-rule evaluator compares observed vs. threshold over
windowMinutes, derives severity, fans out to every destination inchannels, and respectscooldownMinutes. - New settings page
/settings/alerts— CRUD for alert rules. - New API surface:
GET /api/v1/notifications/providersandPOST /api/v1/notifications/providersGET /api/v1/notifications/providers/:id,PATCH /api/v1/notifications/providers/:id,DELETE /api/v1/notifications/providers/:idPOST /api/v1/notifications/providers/:id/testPOST /api/v1/notifications/sendGET /api/v1/alertsandPOST /api/v1/alerts(alert rules — later renamed from/alert-rulesto/alerts)GET /api/v1/alerts/:id,PUT /api/v1/alerts/:id,DELETE /api/v1/alerts/:idPOST /api/cron/tick(token-authenticated; intended caller is an external scheduler such as AWS EventBridge).
Phase 03 — Report Builder + Scheduler
User-configurable reports with LLM narration, four output formats, and scheduled delivery through Phase 02 providers.- New persistent objects for report templates, report schedules, and report runs (additive migration; no changes to existing data).
- Report data-source registry — 10 built-in sources (
offer_performance,channel_effectiveness,selection_frequency,anomaly_candidates,why_not_ranked,decision_traces_summary,funnel,revenue_trend,daily_trend,budget_burn). Extension point: drop a new file insrc/lib/reports/data-sources/and register it. - Report format registry — built-in PDF (via
@react-pdf/renderer), CSV, Markdown, HTML. - LLM narrative engine — uses the tenant AI provider, produces an executive summary + per-section narratives + key takeaways, and caps input at ~5000 tokens worth of rows with explicit truncation signals.
- Report runner — loads the template, runs the configured data sources, calls the narrative engine, renders every requested format, records the run, and dispatches it to the configured destinations.
- Full API surface:
GET|POST /api/v1/reports/templates,GET|PATCH|DELETE /api/v1/reports/templates/[id]POST /api/v1/reports/templates/[id]/preview(transient render, no persistence)POST /api/v1/reports/templates/[id]/run-now(immediate run; works without the cron)GET|POST /api/v1/reports/schedules,PATCH|DELETE /api/v1/reports/schedules/[id]GET /api/v1/reports/runs,GET /api/v1/reports/runs/[id]GET /api/v1/reports/runs/[id]/artifacts/[format]
/settings/reportsbuilder UI — compose sources, pick formats/narrative, schedule + destinations, live preview, runs history drawer./api/cron/tickextended to also process due report schedules; response JSON gainsreportsEvaluated,reportsRan,reportErrors.
Phase 04 — Executive Dashboard + Share-as-Report
C-suite-ready view and one-click sharing across every dashboard.- New page
/dashboards/executive— LLM-narrated weekly summary, six KPI cards with period-over-period deltas and sparklines, anomaly feed (last 7 days), segment × offer heatmap, and quick-links to operational dashboards. - Reusable dashboard widgets:
- Period delta — headline + Δ% vs. prior period + sparkline.
- Anomaly feed — severity pill / metric / delta / explain button.
- Segment-by-offer heatmap — top-10 × top-10 selection rate grid.
- Export menu — PDF / CSV / Markdown / HTML dropdown (uses
/api/v1/reports/previewunder the hood; no cron required). - Save-as-Report — modal pre-filled from the current view; creates a report template plus a report schedule in one click.
- Export + Save-as-Report wired into every dashboard — Business, Operations, Model Health, Data Health, Attribution (in addition to Executive).
- Backend support for period-over-period (
summary_with_comparison,model_auc_summary_with_prev, and sparklines on core metrics).
Earlier changes
Earlier changes are tracked in commit history — see the platform repo. Notable recent work prior to this milestone:- Mar–Apr 2026 — Docs remediation and sample-data corrections (connector count corrected to 24, transform count to 15, API response shapes aligned end-to-end with code).
- Apr 2026 — Repo open-sourced under a single “Initial open source release” commit. CI and CodeQL workflows temporarily disabled pending the public repo cut-over.
- Apr 2026 — API Explorer auto-creates an API key on first visit to the playground for streamlined onboarding.