Release2026.10PlatformAPISDKService BridgesDocumentData FormsTasksOrchestratorCLIAnalyticsChatActivitiesManageBreaking
Release 2026.10
Rollup of every customer-facing change in the 2026.10 GA release. The headline items: the OpenAPI specification now describes every request and response precisely — separate create and update schemas, required and nullable fields, named enums, unique operation IDs — with the Python SDK regenerated to match; activity-plan agents and module executions can call the service bridges your organization has configured, and every bridge call is now fenced to public hosts; and the document engine closes a long list of gaps in validation exceptions, formula recalculation, selectors, and multi-view editing.kdx sync push now converges deletions inside the regions your files own, and module runtimes deliver finished work through brief orchestrator unavailability. Four changes are breaking — the OpenAPI contract, the regenerated Python SDK, the service-bridge egress fence, and documentStatus on document families — see the upgrade notes under API, SDK, and Service Bridges.API:-
The OpenAPI specification now describes every body precisely — separate create and update request schemas, required and nullable fields, named enums, and unique operation IDs (breaking). The specification served at
GET /v3/api-docs(and everything generated from it) now yields three schemas per resource:<Entity>for responses, withrequiredlisting the fields the server always returns andnullablemarking those that can benull;<Entity>CreateRequestforPOSTbodies, with server-generated fields (id,uuid,createdOn,updatedOn,changeSequence) removed andrequirednaming only what the server insists on (for exampleTaskCreateRequestrequires justprojectId); and<Entity>UpdateRequestforPUTbodies, which requires nothing, keepschangeSequencefor optimistic locking, and omits the ownership fields an update never writes. Enumerations are emitted as named components referenced by$ref—TaskStatusType,ExecutionStatus,ExecutionStatusMessageType,SortDirection,AuditAction,ChatMessageRole,WebLlmModelSize, and others — so every generator produces one stable type per enum instead of inventing names likeType1orStatusType3. Nullable object references are expressed asallOf+nullable: true, so generated types carry both facts. Fields that hold free-form JSON (for example the storage onWorkspace, prompt metadata, and platform-event payloads) are typed as JSON objects rather than base64 strings. Operation IDs are unique:PUT /api/tasks/{id}/statusissetTaskStatusandGET /api/task-groups/{id}/historyislistHistoryForTaskGroup, which letsupdateTaskStatus(PUT /api/task-statuses/{id}) andlistTaskGroupHistory(GET /api/task-group-history) appear in generated clients — both were previously dropped by the name collision;cancelExecutionis defined once, at/api/executions/{executionId}/cancel. The retired project-scoped task status type (TODO/IN_PROGRESS/DONE) is gone:TaskStatusTypeisOPEN | IN_PROGRESS | DONE | BLOCKED | PENDING, and project templates that still declare a task status withstatusType: TODOare accepted and stored asOPEN. Request and response JSON on the wire is unchanged; only the specification and the clients generated from it change. Upgrade note. If you generate a client from/v3/api-docs, regenerate it against a 2026.10 server and expect compile-time changes: create calls take<Entity>CreateRequestand update calls take<Entity>UpdateRequestinstead of the response type; response fields that were all optional are now typed as required where the server always sends them; enum types take their component names; JSON-blob fields become objects instead of strings; and two operations are renamed —updateTaskStatus→setTaskStatusforPUT /api/tasks/{id}/statusandlistTaskGroupHistory→listHistoryForTaskGroupforGET /api/task-groups/{id}/history(the old names now refer to the task-status and task-group-history entity endpoints). Hand-written HTTP integrations need no change. -
documentStatuson document families is now a full status object (breaking). Document family reads —GET /api/document-families,GET /api/document-families/{id}, and the responses of the statusPUT/DELETEendpoints — returndocumentStatusas theDocumentStatusobject (id,status, and its other fields) instead of alwaysnull, and saved filters such asdocumentStatus.status:'Reviewed'resolve against it. In the Studio document grids the Status column now shows each document’s status and redraws as soon as you pick a new one. The genericPUT /api/document-families/{id}also acceptsdocumentStatusas an object under the standard update permission. Upgrade note. In the OpenAPI schema,documentStatusonDocumentFamilyand on its create/update request bodies changes fromtype: stringto a reference to theDocumentStatusschema. Regenerate typed API clients and update any code that declared the field as a string; read the status throughdocumentStatus.status(ordocumentStatus.id) rather than treating the field itself as the value. -
Filter task groups by their member tasks.
GET /api/task-groupsnow accepts amemberTaskrelationship infilter, so you can select groups by properties of the tasks they contain — for examplememberTask.statusSlug in ['reviewed'], ornot (memberTask.updatedOn > '2026-08-01T00:00:00Z')to find groups with no recent task activity. A group matches when at least one of its non-deleted tasks satisfies the condition. This is additive and opt-in; existing filters and responses are unchanged. -
POST /api/batch-updatereports the real outcome of every write. A batch save completes when only optional bookkeeping fails, and when a write that matters fails — the task update, task-group maintenance, labels, document-family deletes, or the status auto-lock — the response carries that write’s own error and reason rather than a generic commit failure. A save that reports success has been stored. -
taskIdremoved from the Activity schema. TheActivity,ActivityCreateRequest, andActivityUpdateRequestschemas no longer includetaskId; the field was never populated by the server, so no data is lost, and the generated Python SDK and TypeScript models drop it accordingly. The link between a task and the activity that created it is the task’screatedByActivityId.
-
Python SDK models regenerated against the new contract — request models, spec-named enums, and a slimmer
Task(breaking).kodexa_document.model._generatednow includes a<Entity>CreateRequestand<Entity>UpdateRequestmodel for every resource (154 new models) alongside the response models, so create and update payloads can be built from a typed model that contains only the fields the server accepts. Enumeration classes carry the names from the API specification instead of generator-invented ones:Status→ExecutionStatus(also the top-level export,from kodexa_document.model import ExecutionStatus),StatusType4→TaskStatusType,Type6→TaskActivityType,Direction→SortDirection,Action→AuditAction,ActorType→DocumentActorType,Cardinality→TaxonCardinality,ValuePath→TaxonValuePath,MetadataValue→TaxonMetadataValue,AnalyticsDatasetFieldType→AnalyticsFieldType,ModelType→WebLlmModelSize,Outcome→TaskSignalOutcome,State→SessionState,StepType→PipelineStepType; the retiredStatusType3(TODO/IN_PROGRESS/DONE) and the duplicateTaskstatusTaskStatusmodel are removed.Tasknow carriesstatus_slugonly — resolve the label, color, andstatus_typethrough the organization’s task statuses (TaskStatus) rather than an embedded object. Upgrade note. Update imports that reference the old enum names listed above (kodexa_document.model.Statusis nowExecutionStatus). Code that readtask.statusortask.status_idmust switch totask.status_slugand look the status up in the organization’s task statuses;TaskEndpoint.create_with_request()ignores an embedded status object and sendsstatusSlug. Values on the wire are unchanged — only the Python type names and theTaskmodel’s fields differ. -
Python SDK attribute writes are complete and type-safe. Setting a date attribute from the Python SDK or an agent tool now persists — previously the value was dropped while the call reported success — and date strings are parsed flexibly:
2023-06-30, month-name and US numeric forms as well as RFC 3339. Updating a boolean or date attribute no longer raisesDocumentErrorafter the value was written, and only the field that actually changed triggers recalculation.Document.batch_transaction()now persistsvalue,tagId,dataFeatures, confidence and timestamps on attributes it creates, keepsselectionOptionson data objects it creates, and recalculates formulas for attributes it creates or updates — the same behavior as the direct accessors. Closing a document also releases its formula, validation, and conditional-format caches, so long-running processes that open many documents no longer grow memory per document.
-
Activity-plan
AGENTsteps and module executions can call service bridges. An agent running as anAGENTstep gets two tools,list_service_bridgesandcall_service_bridge(bridgeandendpoint_namerequired, optionalqueryandbody; returns{status, ok, body, truncated}), so any external HTTP API your organization has configured as a service bridge — a search provider, an enrichment service, an internal system — becomes something the agent can use, with the credential injected server-side and never entering the agent’s container. Access is opt-in and fails closed: a bridge is callable by agents only when its newagentCallableflag is on (the Callable by agents toggle on the bridge’s General tab, oragentCallable: truein the bridge YAML) and the bridge is bound to the agent’s project; agents see only bridges that satisfy both. The same two checks admit module and task executions running as a project’s assistant, so a module can call the bridges bound to its own project through the bridge proxy; any call that cannot be resolved to a project is denied. Agents also receive document tools for the document families they were dispatched for, and any document an agent creates during the activity appears on the review task the activity ends in. -
Activity plans containing
AGENTsteps deploy throughkdx sync push.AGENTis accepted by server-side plan validation, so a plan carrying an agent step syncs like any other plan instead of being rejected as an unknown step type. -
Service bridge calls are fenced to public hosts and refuse credential configuration that cannot work (breaking). Service bridge calls made through the bridge proxy — from data forms,
BRIDGE_CALLsteps and agents — go through the platform’s egress guard: targets that resolve to loopback, private, link-local, or cloud-metadata addresses are refused, and each redirect hop is re-checked rather than followed blindly. A refused call from a data form or the bridge proxy returns400withservice bridge egress deniedand the reason; a refusedBRIDGE_CALLstep fails with the same reason. A header configured withsecretRefis now rejected with a400naming the header instead of being sent upstream with an empty value;${secrets.NAME}interpolation in the headervalueondefaultHeadersis the supported way to inject a secret. A call naming an endpoint that does not exist on a bridge now fails with an error listing the bridge’s available endpoint names, instead of a bare “not found” — and a script or step that names a wrong endpoint no longer falls through to the bridge’s first endpoint. An omitted endpoint name still selects the sole endpoint of a single-endpoint bridge. Upgrade note. Audit bridges whosebaseUrlpoints at a private or in-cluster host — those calls now fail withservice bridge egress denied. Self-hosted deployments that must reach an internal mock or test service can setBRIDGE_EGRESS_PRIVATE_HOST_ALLOWLIST(a comma-separated list of exact hostnames) in the environment of both the API and the orchestrator; matching is exact and case-insensitive with no wildcards or CIDR ranges, DNS pinning and redirect checks still apply, and an active allowlist is logged as a warning at startup — it is intended for development and test environments only. Move anyheaders[].secretReftovalue: "${secrets.NAME}"underdefaultHeaders. Confirm that scripts and forms reference an endpointnamethat exists on the bridge — a misspelled name is now an error rather than a call to the first endpoint. -
Service bridge required-field checks now recognize parameters sent in the query string. When an endpoint declares
requiredfields in itsrequestSchema, the proxy (/api/service-bridges/{id}/proxy/{endpointName}) returns an empty[]result without calling upstream while any of them is missing — the behaviour that keeps a dependent dropdown showing no options until its prerequisite field is filled. That check now treats a required field as supplied when it appears in either the JSON body or the query string, so GET-style endpoints that carry their parameters in the URL — the usual shape for search and lookup APIs — reach the upstream service instead of quietly returning an empty result. Presence is what counts on both sides: an empty string,0, orfalsestill satisfies the field. - Edits in the service bridge editor’s YAML tab now save. Changes typed in the YAML tab reach the bridge on save — including when you switch tabs or close the editor mid-edit — and the toolbar spells out that editing here replaces the whole bridge definition, including fields on the other tabs. YAML that does not parse, or parses to something other than a bridge object, is held back with an explanation instead of committing. If the bridge changes underneath you while you have uncommitted YAML (for example, an agent updates a draft), your text is kept and a banner tells you it is now based on the older version.
- Validation rules can read parent values with
{../…}. A validation rule or condition that references a parent or grandparent attribute —{../invoice_number},{../../customer_name}— is evaluated during extraction, during recalculation after an edit, and when a data definition is saved. A reference that keeps walking up past the top of the data — a second..applied to an object that has no parent — is reported as an evaluation error rather than resolving to nothing. Documents whose data carries a circular parent link open and save normally. - On Studio-built data definitions, a corrected value closes its validation exception on the first correction. Where an element’s internal name differs from its external name — the shape Studio produces — correcting a value that raised a validation exception closes that exception on the first correction, whichever evaluation path opened it.
- Exception flags from validation rules stay open until the rule passes. A validation rule authored with an empty
exceptionIdis identified by its taxon path and rule name, so every rule on a taxon carries its own flag: a flagged document stays flagged across data-definition saves and document refreshes until the underlying value is fixed. Deleting a flagged field in the review form removes that field’s exception along with it. Documents carrying flags from such rules are corrected on their next refresh — no reprocessing needed. - Overridable validation rules can be overridden in the review workspace, and exception details persist through every write. A validation rule with
overridable: truein the data definition now produces exceptions that carry the Override action in the browser, on documents already processed — no reprocessing or redeploy needed — and a rule’ssupportArticleIdis carried onto its exceptions the same way. Overriding an exception keeps it on screen in a muted “Overridden by user” state, and the override holds across later validation passes while the rule still fails; only exceptions the platform itself closed are reopened when their rule fails again. Exceptions written by extraction, recalculation, or the browser now retain theiroverridableflag, linked support article, and evaluation-errored status, and previously processed documents pick these values up silently the first time they are opened. Separately, editing or moving a data object or attribute now preserves its original creation timestamp instead of blanking it, so creation times read from exported document databases are reliable. - Data exceptions keep their configuration-error state through re-validation and save. When a validation rule has a configuration error, its open exceptions carry that state — the error banner, whether the exception can be overridden, its details, and its support-article link. Re-saving the data definition now refreshes that state on the existing exceptions (a rule whose error is fixed clears the banner and details), and the state is now part of the document’s saved change history, so it survives reload and shows identically for every user rather than only in the session that produced it. Existing documents pick up the new fields automatically; no action required.
- Removing a validation rule now takes effect when a document is reloaded. Reloading a document applies the current data definition in full: added and changed rules are evaluated on load, and open exceptions raised by a rule that has since been removed from its taxon are now closed on reload, with the close recorded in the document’s change history — no reprocessing needed. Removing an entire taxonomy, or removing every rule from a taxon, still requires reprocessing to clear those exceptions.
- Saving a data definition only closes the validation-rule flags it owns. When a data definition is created, updated, or deleted, the engine re-evaluates validation-rule exceptions and closes only the rule-based flags that no longer apply. Data-type conversion errors, formula and content errors, extraction-time flags, and selection-validation flags stay open until their own source clears them or a user closes them — a data-type conversion error clears when the value is re-extracted or a reviewer closes it, not on a definition save.
- Deleting a data object removes its validation exceptions with it. When you delete a row, the exceptions raised on it are removed in the same change, so they no longer linger on the document or appear in the save as flags pointing at an object that no longer exists.
- Edits made in a popped-out document window are saved. When a document is open in a popped-out sidecar alongside the main workspace, adding, editing, moving, copying, or deleting data objects, values, and notes from the popped-out window is written to the document that saves, so the change survives reload and appears in the change history, and the main workspace catches up within a moment. A write the platform refuses is reported once with a clear notification (for example “The value could not be deleted — it has not been removed”) rather than appearing to succeed, and a partially applied multi-row move or copy says which part applied. Deleting a row succeeds even when re-validation raised exceptions on it in the same change, deleting a value by its identifier reaches the stored document on every path, and tag highlights load reliably when a document opens.
- A save containing changes that can never apply keeps everything else. When some operations in a save reference a parent or object the document no longer has, the remaining operations are applied and the delta is recorded with the new
APPLIED_PARTIALstate onGET /api/linked-deltas, witherrorMessagesummarizing what was dropped. A save in which nothing could apply is still markedFAILED. - A failed server-side save leaves the document exactly as it was. When a processing step’s write to a document is abandoned partway — an error during a large rewrite, for example — the document reverts cleanly to its previous contents and passes integrity checks, with no reprocessing needed. Recovery from a hard crash in the middle of a write is unchanged: reprocess the document.
- Highlight adjustments save with their content links intact. Moving or resizing a tag’s highlight during a review removes and re-adds the link between the tag and its text; the saved change set now records the final state of each tag-to-content link, so a tag re-linked to the same text keeps its highlight after save and a removed link stays removed — including when another tag was removed in the same session. Tags removed in a session no longer leave dangling references in the saved document.
- Save enables for label removals and task status or priority changes. Clearing selected labels on a document, and changing Status or Priority in the Task Metadata panel, mark the document as having unsaved changes, so Save is available and the change is included in the next save.
- Formula values corrected when a document opens are saved back to the document. Opening a document whose formula fields are stale — inputs changed since the value was last calculated, or a value stored under the wrong type marker — recalculates them for display and writes those corrections to the stored document in the background, so exports, downstream steps, and other viewers see the same values without anyone pressing save. Corrections are written only from the browser tab that owns the document. A locked task or a locked document family is corrected too — only the calculated values are written and the task stays locked — so a signed-off task’s export matches what reviewers see. Corrections are recorded as automatic corrections rather than reviewer edits, and anything you type while the document is opening is saved as your own edit. If the background save does not go through you see a notification, and the corrections are recomputed the next time the document opens.
- Recalculate All, data-definition saves, and document refreshes recompute formulas on Studio-built data definitions. The batch formula passes — Recalculate All, saving a data definition, and document refresh — now recompute every formula field, including on data definitions created in Studio, where an element’s internal name differs from its external name. Expect the first definition save on a document that holds stale formula values to update them.
- Bulk move and copy of data objects are faster, and bulk copy is all-or-nothing. Copying many data objects at once from a grid’s bulk actions either lands every object or none of them, and a failure is reported instead of leaving a partial result that reported success. Large bulk moves and copies complete in a fraction of the time they used to — a 48-object copy finishes in well under a second — and the grid shows the moved or copied objects as soon as the engine finishes instead of re-downloading the whole document. A second bulk action started right after one completes now runs instead of being ignored.
- Selector edge cases evaluate as written. A positional predicate selects the nth match (
//line[0]is the first — indexes are 0-based); every condition in anandis applied; a comparison againstfalsesuch as[hasTag('x') = false]matches nodes that do not carry the tag; andhasFeatureValue(type, name, value)compares against the feature’s stored value. Predicates apply in the order written, after the node-type test. BecausehasFeatureValuematches, documents processed with the AWS Textract model (kodexa/aws-textract-model-v3) outside lightweight mode now gain theFormAssistant/LabelandFormAssistant/Valueform-label tags. Re-check any selector using these shapes that you had worked around. - Dates written with month names, dashes, or a UTC offset convert to the value you meant.
2026-Mar-01and1-Jan-2025convert to 1 March 2026 and 1 January 2025, and RFC 1123 timestamps convert correctly on every weekday. A space-separated date-time carrying a numeric UTC offset (2026-03-01 12:00:00 -0500) is stored at the instant the offset states — 12:00 at-05:00is 17:00 UTC. Purely numeric dash dates (04-30-25,1-2-2026,03-1-26) are unchanged. Values written with an offset before this release may be stored at a different instant, or a different date, than they are now; re-extract them if you compare against them. addFeaturereports a value it cannot store, and leaves the attribute unchanged. In event-subscription scripts and script steps,addFeatureon a data attribute normalizes its value at the script boundary: a cyclic or otherwise non-serializable value raises a catchable script error and the attribute is left untouched. Typed arrays are stored as base64 and integer values as floats immediately — the same shape a reload produces.- Explain Plan phases close with a status and duration, and the detail pane opens at full width. Every top-level phase in a document’s Explain Plan — the extraction run, knowledge application, and preprocessing — is persisted with a completion status and a measured duration, so the plan and processing-step analytics show how long each phase took. A phase that recorded its own outcome, such as an error or a skip, keeps it, as does a duration the phase measured for itself. Clicking a node opens the detail pane at its configured width, flush to the edge of the panel.
- PDF Parser: more accurate scan detection, with new opt-in tuning options. The
PDF Parsermodule (fast-pdf-model) now handles three cases that could misroute a healthy text PDF: metadata containing stray control characters, apdftotextwarning exit that still produced usable output, and amin_scan_pagesof0or a non-numeric value (which falls back to1with a warning). All three are extracted normally rather than routed to OCR. The advanced per-page detector gains four options, all off by default:text_margin_ratio(ignore text within this fraction of the page edge — scanning stamps, Bates numbers, burned-on headers — when judging whether a page has a usable text layer; also applies to the standard detector),use_summed_image_coverage(measure the union of all images on a page, catching scans tiled into strips),check_font_encoding(treat text that extracts as mojibake as a scan), andadvanced_full_page_scan(examine every page rather than stopping oncemin_scan_pagespages are confirmed, so the recorded page list is complete). A newscan_detection_shadowoption records per-page scan results on the document and its page nodes without changing the verdict or routing, and when the General Parser hands a document to OCR, the scan-detection reason is preserved on the OCR output under afast_pdf_scan_detectionmetadata entry. - Advanced scan detection labels only the pages it examined. With
use_advanced_scan_detectionorscan_detection_shadowenabled on the PDF Parser module, page nodes carryfast_pdf:is_scan(true/false) and, on scanned pages, afast_pdf:scannedpresence marker — written for the pages the detector examined and no others. A page the parser could not read carries afast_pdf:scan_errormarker instead of anis_scanverdict, and pages the walk never reached carry no label at all, so an absent feature means “not measured”, never “not a scan”. Select scanned pages with//page[hasFeature('fast_pdf','scanned')]. Document metadata describes the walk itself:scan_pages_examinedis how many pages were examined,scan_detection_modeisenforceorshadow, andscan_detection_completesays whether the walk covered the whole document — it isfalsewhenever the walk stopped short, which includes the ordinary case of stopping as soon asmin_scan_pagesscanned pages were confirmed, so read it as “this page list is partial” rather than as a failure flag. Pages already confirmed as scans always count towardmin_scan_pages. For a complete page list, use shadow mode oradvanced_full_page_scan. Both detection options remain off by default. - Document Preprocessor: pages are rasterized only when they really contain vector artwork. The preprocessor’s vector-graphics check now matches PDF drawing operators as whole tokens, so ordinary page text no longer counts as drawing (for example the letters
reinside a word, ormandcinside acmtransform). In testing the share of pages flagged as vector graphics fell from about 94% to 74%, so far fewer text-only pages are rasterized at 300 DPI — faster preprocessing, with unchanged output for pages that truly carry vector graphics. Clip-only paths (W/W*) are still excluded.
- A cleared field is treated as empty everywhere in the review form. When a reviewer clears a value, the form treats that field as empty from then on: re-extracting refills the cleared field instead of adding a duplicate, clearing the last value in a grid row lets the now-empty row be removed rather than left behind, copy actions and formula explanations read the current value, and data-form script triggers fire with the cleared (empty) value rather than the original extracted text. No action required.
- Clearing a date, date-time, currency or number value now clears it — on screen, in the saved data, and after reload. Using a form’s clear-value shortcut or emptying the field blanks the stored value and its displayed text together, the field renders empty immediately, and it stays empty when the document is saved and reopened. Clicking into and out of an untouched date field no longer registers as an edit, so it does not dirty the document or block a clear that follows. Form-declared data-entry shortcuts keep acting on the field you were just in, so a modifier that blurs the field on the way into a key chord (an Alt-based combination, for example) no longer stops the shortcut from working.
- Edits to numeric, date, and boolean fields persist exactly as entered. Typing a new value into a typed field — number, currency, percentage, decimal, integer, date, date-time, or boolean — updates that field’s value and leaves the originally extracted text in place as provenance, so what you entered is what the saved document holds after reload; clearing such a field still clears it outright. Re-tagging a field from a new selection in the document records the newly selected text as that field’s value and provenance; on a numeric or date field the typed value is not re-derived from the new text, so enter it directly if it should change too. No action required.
setAttributein the form script bridge stores values by the attribute’s type.kodexa.data.setAttribute(dataObjectUuid, path, value)on an existing attribute now writes into the column that matches the field’s type: a numeric string (grouping commas allowed) or a JS number becomes the numeric value, a date becomes the date value — a date-only literal such as"2026-01-01"is accepted and stored as midnight — and booleans set the boolean value. The write goes through the same audited update path as a reviewer’s edit, so it is saved, survives reload, and appears in the change history; the attribute’s original extracted text is left unchanged. If your scripts pre-formatted values to work around dates or numbers not saving, that is no longer necessary.- Copy rules keep the original extracted value on copied data, and multi-select copies follow the rule. A copy made through a copy rule with attribute mappings now carries the source attribute’s original extracted value and confidence onto the copy, and the copy records its source as provenance — so a correction a reviewer made before copying remains detectable on the copy, and object history shows the object as a copy. Selecting several data objects and copying them under a copy rule now produces exactly the result of copying them one at a time: attribute mappings,
copyAttributes: false, andstampAttributesare applied the same way regardless of how many rows are selected. - Audit/Notes opens on the value you picked, from any workspace layout. Choosing Audit/Notes on a data value opens the Audit panel with that value’s thread already showing, including when the side panel was collapsed. Closing the side panel with the toggle and reopening it returns you to the tab you were last using rather than to Chats. In layouts where no audit view is available for the current task, the Audit/Notes menu item is hidden instead of being shown but inactive.
- The Formula Execution Trace shows the correct sign on every component. When you open the execution trace for a formula value, subtracted operands are shown as subtractions and the tree view picks the correct top-level operator in mixed expressions such as
(A + B) - C, so the trace reads the way the formula is written rather than listing every component as an addition.
- A task’s documents now open in a deterministic order, with the first document as the primary. Task document links and activity document links carry a new integer
ordinalfield (TaskDocumentFamily,ActivityDocumentFamilyand their create/update requests in the API, Python SDK and CLI models). The ordinal is stamped from each document’s position in thedocumentFamilyIdslist the activity was started with, copied onto the task’s documents when the task is created, and used by the workspace to order a multi-document task — so the task opens on the first document you submitted rather than an arbitrary one, and you choose the primary document by listing it first. Ordinal0is the primary document. APOST /api/task-document-familiesorPOST /api/activity-document-familiesthat omitsordinal(or sends0) appends the document after the existing ones; an explicit non-zero ordinal is stored as given. Existing multi-document tasks and activities were backfilled with a stable order. - Task completion is guarded when a document’s latest changes are not yet in the stored document. Marking a task done — from the status dropdown,
PUT /api/tasks/{id},PUT /api/tasks/{id}/status, or a batch update — returns409 Conflictnaming the affected documents when one of the task’s documents still has unapplied changes from its most recent save. Reopen the document, redo those changes, and save; a successful save clears the block and the task completes normally. Only the latest save per document is checked, so an older failure that has since been re-saved does not block. Error dialogs now show the server’s own explanation for any4xxresponse, so the reason is visible immediately instead of a generic “Request failed” line. - Task actions’
attributesblock now persists writes to existing values. When an action’sattributesblock stamps a field that already has a value — for exampleaccepted_by/accepted_aton Accept, or clearing them on Re-open — the write now goes through the same audited update path as a reviewer edit, so it is saved with the document, survives reload, and appears in the change history. The stamped value is stored according to the target field’s type (number, date, boolean, or text), and the attribute’s original extracted text is left unchanged. Creating a new attribute viaattributesbehaves as before. - Task status names and colors display consistently wherever a task appears. The home dashboard, related-task popovers, kanban cards, the workflow task navigator, the parent-task chip, and sub-task timelines now resolve a task’s status through the organization’s task statuses, so they show the configured status name and color (for example “In Review”) rather than the raw slug or no status at all.
- Task Metadata status keeps its value after a batch save. The task object returned by
POST /api/batch-updatenow carries the status understatusSlug, matching the Task model everywhere else, so the Status dropdown in the Task Metadata panel shows the saved status the moment the save confirmation appears instead of resetting to its placeholder until a reload. Integrations reading the task from the batch response should readstatusSlug; the legacystatusIdkey is no longer emitted there. - Task group queues load the full working set. The reviewer queue on a task group’s landing page and workspace loads up to 100 member tasks, so a group of that size shows every open task in the queue — ordered by priority, then creation date — and always offers the next one to pick up, even when the highest-priority tasks are already complete.
- The task template editor opens every template. Templates created outside the editor — with
kdx, in project-template YAML, or through the API — that have nometadata.propertiesset now open in the Project → Task Templates editor with defaults filled in, instead of an empty pane.
- Scripted LLM calls carry model and cost attribution in the Explain Plan. Every
llm.invokeandllm.invokeWithPromptRefcall made from a script step records the model id, provider (anthropic_bedrock,cohere_bedrock, orbedrock), input and output token counts, duration, and cost onto the Explain Plan step the script is currently inside — no script changes needed. When the call is not inside a step, the platform adds a completedllm_callstep on the first document the script loaded, named from the call’snotewhen you supply one, from the prompt reference forinvokeWithPromptRef, andLLM callotherwise, so every scripted call is visible and attributable in the plan and in processing-step analytics. Cost is priced at call time from the AI Gateway model catalog — the same prices shown on the model list — and recorded once, so later catalog price changes never reprice history; where the orchestrator has noAI_GATEWAY_URLconfigured, or the model is not in the catalog, cost records as0and the call still succeeds. recordLLMCallrecords a usage entry only when it carries usage. The step-authoring helper in the TypeScript, Python (record_llm_call), and Go step authors adds a model-usage entry when at least one usage field is set — model, provider, token counts, cost, duration, finish reason, request id, or in the Go author a sampling parameter such astemperature— and records the prompt and response bodies either way. A wrapper that only narrates the prompt and response text therefore leaves the platform’s own model and cost record as the step’s usage.loadDocument()returns one handle per document family. CallingloadDocument(familyId)more than once in a script step for the same family now returns the same document handle, so data changes and processing steps written through different helpers all land on one document and publish as a single new version. Repeat loads of an already-loaded family do not count toward the script’s maximum document-load budget. Scripts already written as though repeated loads return the same document need no changes.- The orchestrator’s built-in LLM model is configurable and works in every region. AI task naming and the script-step
llm.invokebinding call Amazon Bedrock with the model set by the newLLM_SMALL_MODEL_IDenvironment variable, which defaults to theglobal.anthropic.claude-haiku-4-5-20251001-v1:0cross-region inference profile. The global profile is accepted from any supported AWS region, so environments outside the US get working AI naming and scripted LLM calls with no configuration. Set the variable to pin a different Bedrock model id. - Reprocessing from a step re-runs that step and everything downstream. Choosing Reprocess on a completed step runs it again, including per-document work that had already finished, and discards the document versions the later steps produced, so the re-run starts from the same input the original run had; review tasks created downstream are removed and recreated when those steps run again. Retrying an activity’s failures keeps the failed step’s own completed per-document work but re-runs everything downstream, since its input may change. Documents that a routing step sends down another branch are shown as not processed on this branch, with no leftover results or errors from a run that no longer applies, and a run that a reprocess supersedes is marked Reprocessed rather than left looking as though it is still going.
- Finished module work survives a brief platform hiccup. When a module runtime finishes a piece of work, it retries delivering the result with backoff instead of discarding it, so a transient
5xx,429, or connection error no longer costs you the completed work; a rejection the platform will not accept (a non-4294xx) still stops immediately. Tune the retry window withKDXA_RESULT_RETRY_MAX_SECONDS(default780seconds, and never longer than the time left in the invocation) and the longest wait between attempts withKDXA_RESULT_RETRY_BACKOFF_CAP(default30); scheduler callbacks use the shorterKDXA_CALLBACK_RETRY_MAX_SECONDS(default60). Keep-alive heartbeats retry transient server errors too. The orchestrator also stays in service through a load spike, slowing down rather than dropping out, and still reports unavailable within a second when its database is genuinely unreachable. - Module runtimes reuse a model package across steps. A module runtime downloads and extracts each model package once and reuses it for every later step, so a long-running runtime keeps processing for its full lifetime instead of exhausting its temporary storage. Because the package is cached for the life of the runtime, a model implementation redeployed while a runtime is already running is picked up the next time that runtime restarts — a deploy that cycles the runtimes is unaffected.
kdx sync pushapplies deletions inside the regions your files own. Removing a key from a task template’smetadata.properties, or removing a taxon — or any key inside one, such as a taxon’s onlyvalidationRulesentry — from a data definition’staxons, and pushing again brings the server in line with your file: the resource is reported as changed and the deleted content is removed. Only content your file actually states is compared, so a file that omitsmetadata.propertiesentirely leaves properties set in Studio untouched, while an explicitmetadata: {}orproperties: {}clears them, and a fully commented-outtaxons:key is treated as unmanaged rather than as a deletion. Fields your file never mentions —id,changeSequence,refand other server-maintained values — are ignored as before. Deletions converge when pushing to a current Kodexa API server; pushes to legacy servers keep the previous behaviour.${org}placeholders are resolved in module deployments, and any leftover placeholder stops the push. Module payloads deployed bykdx sync push— including references embedded in a module’s script, such as${org}/my-taxonomy— now have${org}substituted with the target organization slug, the same as every other resource type. After substitution, both the module and generic push paths check the payload: if any string still contains${org}(for example a malformed token such as${org}-nameinstead of${org}/<slug>), the push aborts with an error naming the resource and the field paths, instead of deploying the literal text and failing later at runtime.
- Analytics task metrics now count by status type, not status name. In the task, task-link and assignment analytics datasets,
completedTaskCount/completedTaskLinkCount/completedAssignmentCountcount tasks whose status is DONE-typed (or that carry a completed date), whatever the status is called — statuses namedreviewed,rejectedorcanceledcount as completed just likedone.openTaskCount/openTaskLinkCount/openAssignmentCountcount OPEN-typed statuses only, matching the task grid’s Open quick filter and the task-groupopenGroupCount, and theoverdue*metrics count any not-done task past its due date. Because IN_PROGRESS and BLOCKED tasks are neither open nor completed, Open + Completed no longer necessarily equals Total — read each metric directly rather than deriving one from the others. Expect Completed to rise and Open to fall on existing dashboards. - Extraction, classification, and chunking LLM calls are recorded as typed model usage. Steps that record their usage as freeform
llm_usagemetadata now also carry structuredmodel_useentries — model id, token counts, duration, and the provider where the model id identifies one — whenever the document is read, so an activity step’s token totals, the step detail pane in the Explain Plan, and the model usage projected to analytics account for these calls alongside script-step calls. Existing documents pick this up on their next load or publish, with no reprocessing, and the freeform metadata stays in place for older readers. Cost is not yet attributed to these entries.
- Collapse the chat list to a rail in the Chat panel. The list of chats beside the conversation can now be collapsed with the hide/show control in its header. Collapsed, it becomes a narrow rail that keeps New chat and your existing chats (shown as initials, with the active chat highlighted) one click away, and the conversation takes the reclaimed width — so a narrow panel dock still leaves room to read and reply. The collapsed/expanded choice is remembered per task or project workspace in your browser.
- Files offered by an agent appear as a labelled attachment card. When an agent message offers a file to download, the message now shows an attachment card — file icon, file name (full name on hover), and a Download button — instead of a bare download-arrow emoji. Agents supply the file name through the new optional
nameattribute on the<downloadDocumentFamily id="…" name="…" />tag; a tag withoutnamerenders as a labelled Download file chip, so existing messages keep working. The control is a real button: keyboard-focusable and announced by screen readers with the file name. Copying a message as text or rich text keeps the file name in the copied content. If you author agents that emit this tag, addnameso users can see which file they are about to download.
- The New Activity wizard enforces document group upload limits. When a plan’s document group sets
maxSize,maxPages, orhardMaxPages, each file added in the New Activity wizard — by drag-and-drop or the file picker — is checked before it uploads. A file overmaxSizeor a PDF overmaxPagesprompts with Include Anyway / Exclude Document; a PDF overhardMaxPagesis rejected with a Document Too Large notice and cannot be overridden. Page checks apply to PDFs only, every file in a batch is judged on its own, and a PDF whose page count cannot be read is allowed through. This brings the wizard in line with the New Task dialog, which already enforced these limits. Set the limits on the document groups in your activity plans to have them applied.
- Manage → Intakes lists only the current organization’s intakes. The Intakes grid is scoped by the organization in the URL, so it shows the right organization’s intakes on a hard refresh, a direct link, or the first page after sign-in, and no longer carries a previously viewed organization’s filter across sessions. Browsers holding an older saved grid filter are corrected automatically the next time the page opens.
- Sort the Projects list by Status, Owner, and Organization. Clicking those column headers in Organization → Projects now sorts the list in either direction (a saved sort on one of these columns previously emptied the grid on every visit; affected users self-heal on next load). For API clients,
GET /api/projectsacceptssort=status.status,sort=owner.firstName, andsort=organization.namealongside the existing column sorts, and text search and filters keep working while one of these sorts is active. - Signing in goes straight through. Opening the app while signed out takes you to the sign-in page once and returns you to the page you asked for, with no repeated redirects and no stalled “Initializing Kodexa Platform” screen — including on slower or managed browsers.
- Notifications show their full message. Success and error notifications across the workspace — a failed export or download, a copy-to-clipboard result, an activity step action, a channel stop — now display their explanatory text beneath the heading, including the specific reason an API call failed, instead of a bare “Error” or “Success” heading.
Release2026.9PlatformKnowledgeData FormsChatProjectsOrchestratorTasksActivitiesDocumentSDKBreaking
Release 2026.9
Rollup of every customer-facing change in the 2026.9 GA release. The headline items: knowledge-set priority is now enforced and project-scoped sets finally apply, with feature links and item edits that reliably save; DeepSeek, Qwen 3, and Z.ai GLM models join the AI Gateway; and chat gains pre-built prompts with context-aware starter tiles. Create and update endpoints across the API now persist exactly what you send — explicitfalse and 0 included — with sparse updates and optimistic locking enforced consistently; this is the release’s one breaking change — see the upgrade note under Platform.Knowledge:- Project-scoped knowledge sets now apply to documents. A knowledge set scoped to a project was silently never matched during document processing — the project was mis-derived from where the document was stored, so only organization-level sets ever applied. Matching now takes the project from the processing run itself, across every processing path, so a project-scoped set applies to documents processed in that project. Documents processed outside any project context are matched against organization-level sets only.
- Knowledge set priority is now enforced. The 0–10 priority on a knowledge set (default 5) previously had no effect. Now, when several applied sets contribute knowledge to a document, their items are ordered by priority — highest first, with a stable tie-break — everywhere that order is visible: the knowledge context supplied to extraction, and the knowledge/instruction panel in the review form, which now shows the highest-priority set’s instruction instead of whichever happened to load first. Priority only affects ordering; it does not change which sets match a document.
- Features linked to a knowledge set now actually save. Linking features to a knowledge set — the set’s feature palette — silently persisted nothing: after a reload the palette came back empty, and an expression condition that referenced one of the lost features rendered as an unresolvable picker. The
featuresarray onPOST/PUT /api/knowledge-setsis now persisted: omitting the field (or sending null) leaves existing links untouched, an empty array clears them, and each entry may reference a feature byidorslug(validated against the set’s organization — unknown references are rejected with a clear error). In the set editor, picking a feature through Add Feature in Advanced mode now also inserts it as a condition in the expression (duplicates are skipped), and Advanced mode shows a read-only In Palette chip list so palette membership is visible outside Simple mode. - Bulk-loading knowledge items no longer drops items. Creating, updating, or deleting many items of the same knowledge set concurrently — for example a CLI sync applying a set’s items with parallel workers — could hit database deadlocks that failed most of the writes, leaving only a few items saved. Item writes to the same set are now serialized on the server, so parallel loads complete with every item intact.
- Reordering and deleting knowledge items now sticks. In the knowledge set editor, items can be reordered by drag-and-drop, and the dropped order both renders correctly and is saved — so the order you arrange is the order the items are applied in. Deleting an item now persists too: previously the row disappeared from the editor but was never removed on the server, so it returned on the next reload. An item added and then removed in the same editing session is simply discarded without an error.
- Negated matching expressions no longer grow an extra “All of” group on every save. Saving a knowledge set whose matching expression has NOT at the top level used to wrap it in a new “All of” group each time the editor reloaded it, nesting one level deeper on every save round-trip. NOT-rooted expressions are now kept as-is through save and reload, and they evaluate exactly as before.
- Notes on data values now save and stay in sync. A note added to a data value through the Audit/Notes thread only ever lived in the current browser session — a save containing nothing but notes was silently discarded, so notes vanished on reload and never reached anyone else. Notes and their replies are now persisted with the document’s change history: they survive reload, travel with the document, and appear for other people viewing the same document without a refresh. In the task workspace, the Audit/Notes context-menu item on a value now opens the Audit panel as expected — previously it did nothing in that layout.
- The AI Gateway now serves DeepSeek, Qwen, and Z.ai GLM models on Amazon Bedrock. Modules and prompts that call LLMs through the Kodexa AI Gateway can now target non-Anthropic Bedrock model families — DeepSeek (including R1), Qwen 3 (including the vision-capable qwen3-vl family), and Z.ai GLM — through the same unified request, tool-calling, and streaming interface as every other provider. Per-family limitations are validated up front with a clear error (for example, a tool-use request against DeepSeek R1, which doesn’t support tools, fails immediately instead of surfacing a raw provider failure mid-request). The gateway’s model catalog can also declare per-model capability flags —
supports_tools,supports_vision,supports_documents, andreasoning_output— surfaced on the model list so callers can pick a model by what it actually supports instead of parsing description prose. Streaming failures now arrive as a structured error event using the same error taxonomy as non-streaming calls, instead of error text spliced into the model’s reply. - Start a chat from a pre-built prompt, with starter tiles that match your context. The chat panel’s starter tiles now differ by context — task chats suggest task-shaped questions (summarize this task, explain the exceptions), project chats suggest project-level ones. Alongside them, a new From prompt button opens a picker of your organization’s Prompt resources, so teams can publish curated, reusable prompts for common workflows. A prompt’s title comes from its
name, and itsmetadatacarriescontext("task"or"project"— which screen offers it),category(how the picker groups it), andprompt(the message body). Picking one starts a new chat named after the prompt with the body prefilled into the input — nothing is sent until you press send, so you can tailor it first. The button appears only when prompts exist for the current context; prompts are managed through the standard/api/promptsresource API. - Copy an entire chat transcript from the chat header. A copy dropdown in the chat header exports the whole conversation in your choice of Markdown, plain text, or rich text (formatted HTML with a plain-text fallback, so pasting into Word, Google Docs, or an email keeps the formatting). The transcript is chronological, with author and timestamp headers on each message, and excludes internal system traffic so it reads as the conversation you actually saw. It covers the messages currently loaded in the panel (the most recent 50); a copy that fails or produces nothing shows a notification instead of silently doing nothing.
- Platform administrators can see every user’s chats on a task. Chat lists in task views now show a platform administrator all users’ conversations on that task — useful for supervision, QA, and support — while project-workspace chat lists remain scoped to each user’s own conversations for everyone. The widened view affects visibility only: when an administrator starts a chat, or the platform opens one on their behalf, it is always their own — the broader view never causes a message to land in another user’s chat.
- Chat agents hold their scope under adversarial prompting. Chat agents are hardened against prompt-injection and jailbreak attempts: an agent now treats the capabilities enabled for its chat as its entire job, declines and redirects out-of-scope requests, resists instruction-override attempts (persona swaps, “developer mode”, “ignore previous instructions”), and will not reveal its internal instructions or configuration. Content the agent reads from documents and tool results is treated as data, never as instructions. These boundaries are enforced by the platform rather than just requested of the model — a tool call outside the chat’s enabled capabilities, or a file access outside the agent’s working scope, is blocked before it executes.
- Renaming a chat now saves reliably and confirms the result. A rename could previously fail with a spurious permission error, or overwrite changes someone else had just made to the same chat (such as its sharing settings) — and a failure gave no visible feedback at all. The rename now updates only the chat’s name and shows a success or failure notification; on failure the rename stays open so you can correct and retry.
-
Create and update endpoints now persist exactly what you send — explicit
false,0, and other zero values included (breaking). Previously a genericPUTsilently dropped any field whose value wasfalse,0, or empty: the request returned 200 OK but the value never changed — so deactivating a user, switching a boolean option off, or zeroing a numeric setting could be a silent no-op.POSThad the twin problem: an explicitfalseor0on a field with a server-side default was overwritten by the default. Both paths now track which keys your request body actually contained and write exactly those fields: explicit zeros persist, omitted fields are left untouched (reliable sparse updates), sendingnullclears a nullable field, and echoing back aGETbody is a safe no-op. The write path is also hardened: system-managed and ownership fields (id,uuid,createdOn,createdByUserId,organizationId,projectId, and soft-delete state) are ignored if sent in an update body, andchangeSequenceis never written directly — it serves only as the optimistic-lock token; a request body that is not a JSON object, or that repeats the same field under case-variant keys, is rejected with400; uniqueness and reference violations now return clean400/409responses instead of500s; and optimistic locking is enforced whenever the body carries a non-nullchangeSequence— including0on a never-updated resource — so a stale save returns409instead of silently overwriting newer data. Upgrade note. Audit integrations thatPUTfull or hand-built objects: fields carryingfalse/0are now written rather than ignored, and calls that used to return200can now return400(non-object body, duplicate case-variant keys) or409(stalechangeSequence, uniqueness conflict). To leave a field unchanged, omit it from the body; to clear a nullable field, sendnull. Values sent forcreatedByUserId,organizationId, orprojectIdon update are ignored. On the project-assistant endpoints, an update that changesnamewithout sendingslugnow re-derives the slug from the new name. -
Document-store and data-store bodies are now complete, stable, and safe to round-trip. Fetching a store could previously return different bodies for the same resource — sometimes missing inner metadata keys such as
indexed— and saving a store back (an edit in the UI, a full-objectPUT, or akdx apply) could silently wipe inner content-metadata settings such asindexed,documentProperties, andlabelExpressions. Store bodies now carry the nested store metadata intact under a newcontentMetadatakey (the existing flattened keys remain for compatibility), reads return the same body every time, and round-trip saves preserve every setting. -
Project slugs are now guaranteed unique within an organization. Creating or renaming a project whose slug would collide with another project in the same organization now automatically appends a numeric suffix (
-1,-2, …) instead of allowing a duplicate — renames previously had no protection, so two projects could end up sharing a slug and slug-based references became ambiguous. Uniqueness is now also enforced at the database level. Aslugyou supply on create is honored (and suffixed only on collision) instead of being silently replaced with a derived one, and re-saving a project under its own slug stays idempotent. Creating a project from the CLI now leaves slug derivation to the server, so project names with spaces or punctuation no longer fail slug validation. - Deleting an organization no longer fails when it has an audit history. Hard-deleting an organization previously hit a server error once audit records existed for it. The organization’s audit-trail records are now removed as part of the delete, so the delete completes cleanly.
- Triggers gain two new event kinds and can ship inside project templates.
document_locked(a reviewer locks a finished document) andknowledge_set_updated(a knowledge set changes) jointask_created,task_status_changed,activity_completed, andmanualas accepted trigger event kinds — these are what let a project start an activity plan in response to review activity. Project templates can now also declare atriggersarray (slug,name,eventKind, optionaleventFilterandinputMapping,activityPlanRef,enabled); the triggers are created at project creation after the template’s activity plans are bound, and re-applying a template skips triggers that already exist instead of failing. Separately,PUT /api/triggers/{id}now fully validates the update — an unrecognized event kind, malformed activity-plan reference, or malformed filter is rejected instead of silently persisted. - Required and pattern validation on project-template options is now enforced at project creation. A template option (in
optionsordataOptions) markedrequired: truemust be filled in before the New Project dialog allows Create — previously the required asterisk was cosmetic and the project was created with the option blank. Options can also declareproperties.pattern(a regular expression the value must match) with an optionalproperties.patternMessageshown inline when the value doesn’t match; validation errors appear under the field and above the action buttons, and the dialog switches to the first tab with a problem. An option with a defineddefaultcounts as satisfied, and developer options are only validated in Studio, where they are shown. - Surface a template option on the first tab with
showOnPopup. A project-template option flaggedshowOnPopup: truenow renders on the New Project dialog’s Details tab, directly beneath the project name and description, instead of behind the Developer Options or Data Options tab — so a required option is visible before you can hit Create. After the project exists, the same flag places the option on the General tab of Project Settings, and a Settings tab whose options are all flagged is dropped entirely. Options without the flag stay on their usual tabs (in the New Project dialog the original tabs remain, minus the flagged options). - Values entered for template options when creating a project now save reliably. Typing a value into a template option in the New Project dialog could show nothing or silently drop the edit, and even when a value was submitted the server could discard it while applying the template’s options — so a project could end up created without the values you entered on the form. Option values now display as you type, validate, and persist onto the created project exactly as entered; the create dialog also no longer modifies the shared template definition.
- Project Settings changes no longer appear to revert after a page reload. Saving project settings — name, description, or template option values — always persisted on the server, but reloading the page could show the old values for up to a day because the browser kept serving a stale locally cached copy of the project. The local cache is now updated on save, so saved changes survive a reload immediately.
- CREATE_TASK steps can name and stamp tasks with runtime placeholders. The
taskDataof a CREATE_TASK step now resolves placeholders at the moment the task is created:${activity.title}(the activity’s title at that point, after any automatic or manual rename),${project.name},${project.id}, and${project.options.dataProperties.<key>}(a value entered in the project’s data properties, as defined by the project template). Placeholders work in the task’stitleanddescriptionand in top-level string values undertaskData.properties— so one plan shared across many projects can give each child task a context-specific name (e.g."title": "Review ${activity.title}") or copy a project-scoped identifier onto every task it creates for downstream filtering. If${activity.title}or${project.name}cannot be resolved, the text is left unchanged; adataPropertiesplaceholder always resolves — to an empty string when the key is unset or not a simple value — so unresolved tokens never leak into task properties. - The activity Logs tab now shows the right document’s logs. When an activity step runs once per document, the Logs tab in the activity status dialog could display logs belonging to a different document than the one you were focused on — reopening the dialog on another document, or clearing the document focus, could leave it pinned to the previous document, and background refreshes reset the execution you had picked in the dropdown. The Logs tab now follows the focused document as it changes, preserves your dropdown selection across refreshes, and shows a clear empty state instead of another document’s logs when the focused document has no execution for that step.
- Task operations no longer fail when a history entry can’t be recorded. Actions such as locking or unlocking a task, or changing its team, also record an entry in the task’s activity history. Previously, if that history write failed, the whole operation failed with a server error — task locking could break entirely. The history record is now best-effort: the operation itself completes normally and a failed history write is logged server-side instead of failing the request.
- The explanation entered when unlocking a task is now recorded. Unlocking a locked task prompts for an explanation, but the text was being discarded — the unlock went through with nothing attached. The explanation is now saved on the task’s unlock activity: the activity text reads
Task unlocked: <your explanation>and the reason is available on the activity record through the task-activities API, so audits of who unlocked a task and why are complete. (Thereasonfield on the unlock endpoint’s request body is optional; a blank explanation still unlocks.)
- Creating an activity from inside a project no longer asks you to pick the project. The New Activity wizard opened from a project’s Activities tab now starts with that project already selected, skipping the redundant project-selection step — matching how the wizard already behaved when launched from a document view.
- The My Projects filter in the New Activity and New Task wizards no longer flips on its own. The filter used to default to on and then silently uncheck itself whenever the project list came back empty, so it appeared randomly checked or unchecked from one open to the next — and could quietly widen your project list without you asking. It now defaults to off and only changes when you change it. When the filter is on and you don’t own any of the listed projects, the wizard now says so and suggests unticking My Projects to see all projects, instead of clearing the filter behind your back.
- Edits made right after saving a document are no longer discarded. After a save in the review workspace, the platform’s periodic change check could mistake your own save for an outside change and force-reload the document, silently throwing away anything you had entered since the save. Your own saves are now recognized and the document is left alone. When a document genuinely is updated outside your session it still reloads to the latest version, but you now get a prominent warning whenever that reload discarded unsaved edits, instead of a quiet refresh. For API clients, each result in the batch-update response now includes a
contentObjectobject carrying the saved content object’sidandchangeSequence, so a custom client polling for changes can distinguish its own save from an external one the same way.
- Python SDK objects stay current after writes. Calling
create(),update(), ordeploy()on an SDK entity now refreshes the object in place from the server’s response — previously the local object kept its pre-write state (including a stalechangeSequence), so a secondupdate()on the same object failed with a409conflict. Task status and assignee updates made through the SDK also now send the task’s own change sequence, so they participate correctly in optimistic locking.
Release2026.8PlatformTasksTask GroupsTask TemplatesKnowledgeData FormsAnalyticsOrchestratorCLIDocumentFormulasChatBreaking
Release 2026.8
Rollup of every customer-facing change in the 2026.8 GA release. The headline items reach across the platform: a newPOST /api/analytics/embed-token endpoint for embedded, per-tenant analytics, a dedicated task-lock feed on the CDC data lake, and keyboard shortcuts you can bind to task-template actions. Scripts can now stamp taskProperties onto the tasks they create, and the Knowledge expression builder gets a broad overhaul — self-healing matching expressions, reversible negation, and safer Simple/Advanced mode switching. The CLI hardens up with real server-side --dry-run validation, credential redaction in debug output, and pipe-safe -o json. Alongside those, this release lands a wide set of Data Forms, Document review, and Tasks fixes. One breaking change lands on the task-status API; see the upgrade note.Analytics:- New
POST /api/analytics/embed-tokenendpoint for embedded, per-tenant analytics. Authenticated callers can exchange their Kodexa credential for a short-lived, tenant-scoped RS256 JWT and query the standalone analytics service directly. The token’s organization scope is derived server-side from the caller’s team/organization assignments (never client-supplied), and every dataset it grants is row-filtered to those organizations, so a token can only ever read its own tenants’ rows. The response returns{ token, expiresAt, models, orgs }, with token lifetime capped at 15 minutes. The minter is opt-in via theanalyticsEmbedconfig block (enabled,issuer,audience,keyId,tokenTtlSeconds,models,objects, androwFilterField— defaults toorg_slug), while the RS256 signing key is supplied out-of-band through theANALYTICS_EMBED_SIGNING_KEY_PEMenvironment variable. When the minter is disabled or the key is missing/unparseable, the endpoint returns503and platform boot is unaffected. - Task locks now stream to the data lake. Whenever a task is locked, the platform now mirrors a task-lock record to your CDC data lake under a new
task-locks/prefix — a dedicated feed, kept separate from the existing work-session telemetry — so you can report on when tasks were locked and by whom. Each record includestaskId,lockedAt,lockedById,lockedByEmail,taskStatus,projectId, the task’sdocumentFamilyIds, and the taskpropertiesobject, and records the user who performed the lock (omitted for system or API-key callers). The write is best-effort and uses your existing data-lake configuration. - More accurate Work Sessions durations, immune to client clock skew. The Work Sessions dataset now measures each session’s wall-clock time from the browser’s own elapsed-time reading rather than the difference between a client and a server timestamp, so durations stay accurate even when a machine’s clock is offset from the server. Active/engaged time is now reported raw instead of being silently capped to the wall-clock value, and two data-quality measures — Skew-Invalid Wall Count and Avg Client Clock Skew (ms) — surface sessions whose wall-clock reading was thrown off by client clock skew, so distorted readings are visible rather than hidden. Focused Ratio (%) is computed only from sessions that have both a valid wall-clock and an active measurement, and each session is counted once (intermediate saves within a session are collapsed to the final save) rather than double-counted. Sessions recorded before active-time capture existed are excluded from the active metrics instead of being averaged in as 0% engaged, and the Total Active (ms) / Avg Active (ms) measures are renamed Total Focused (ms) / Avg Focused (ms).
-
The Task API now reports when and by whom a task was locked. The Task object returned by the tasks API now includes
lockedAt(the lock timestamp) andlockedById(the user who locked it) alongside the existinglockedflag. - The bulk “Lock” action on the Tasks grids now actually locks. Selecting tasks and choosing Bulk Actions → Lock — in both the project and organization Tasks views — previously did nothing (the grid just refreshed as though it had worked). It now prompts for confirmation and locks each selected task. Separately, opening a locked task while “Assign on open” is enabled no longer pops a spurious “Unexpected Exception” error: the auto-assign is skipped for a locked task (which the server rejects) and the task simply opens.
- Setting a task’s status from the UI now saves reliably. Changing a task’s status — through the bulk Set status action, the task details panel, or the status dropdown in the tasks grid — now persists correctly. Previously a bulk status change could clear the task’s status (leaving the badge blank), a status change made in the task details form could be silently dropped, and two consecutive status changes on the same task could fail with a conflict error.
- Task actions now wait for a form’s bridge lookups to finish before they can be triggered. A task action such as Approve or Reject could previously fire while a form’s service-bridge lookups were still resolving and writing back an auto-populated value, letting a task complete with a value that had not yet been saved. Approve, Reject, and other task actions are now disabled while any of the form’s bridge lookups are still in flight, so an action only runs once the form’s auto-populated values have settled.
- Task, task-group, and activity lists no longer show “nothing here” while a stale, invisible filter hides pending work. In both organization and project views, a list could report nothing to do while work was actually pending, with no filter visibly selected — the restored query kept filtering by a stale clause while the toolbar controls reset to blank. Each list now persists its toolbar inputs (quick filter, facets, project selector, date/running filters) and rebuilds the query from them on load, so the visible filters and the results always agree; pre-existing stale state self-heals on first load.
-
Task-status API field renamed
statusId→statusSlug(breaking). The request body forPUT /api/tasks/{id}/statusnow takesstatusSlug— the org-scoped task status slug — instead ofstatusId. The value was already a status slug rather than a UUID; only the field name changed, so callers stop mistakenly sending a status UUID that could never be resolved. Upgrade note. Update any integration callingPUT /api/tasks/{id}/statusto send the status understatusSlug(a slug such asin-review), notstatusId. Requests still sendingstatusIdare rejected withstatusSlug is required.
- Task groups now record how their assignee was set. Task-group responses — including the list endpoint and the take-next claim response — carry a new
assignmentTypefield:TAKE_NEXT(claimed from the kiosk take-next widget),SELF_CLAIM(a user claimed the group themselves), orMANUAL(assigned by someone else). It is set on every assignment path, cleared when the assignee is cleared, filterable on the list endpoint, and included in thetask_group.assignedevent payload and the group’s history. Self-claiming or self-releasing a group is now also correctly checked against theclaim/releasepermission instead of always requiringassign. - Clear filters on Task Groups. The project and organization Task Groups tabs now show an active-filter summary row with a Clear filters button (matching the Activities tab), so you can see at a glance which filters are applied and reset them all — search, quick filter, and facets — in one click.
- Clearing a task group’s team now works. Removing the team from a task group via
PUT /api/task-groups/{id}(sending an emptyteamId) now clears the team instead of failing with a server error. OmittingteamIdstill leaves the team unchanged.
- Bind keyboard shortcuts to task-template actions. A task-template action can declare a
shortcut(and an optionalshortcutAltKeyalternate combination) in itsproperties, and the template itself can declaresaveShortcut/cancelShortcut(withsaveShortcutAltKey/cancelShortcutAltKey) for its default Save and Cancel buttons. Shortcuts are purely declarative — there are no built-in default keys — and they register per task and clear automatically when the task closes. The keyboard-shortcuts help dialog (Ctrl/⌘+/) now lists these alongside data-form shortcuts in an aligned Windows / Mac layout, and a registered shortcut that uses a modifier now fires even while a text field is focused.
- Scripts can stamp properties onto the tasks they create. A SCRIPT step’s return value may now include a
taskPropertiesobject; its keys are merged into thepropertiesof any CREATE_TASK step that directly depends on that script, at creation time — so the task carries them from the moment it exists. The gating script is the last code to run with document context before the task exists, making it the natural place to compute per-document grouping or routing keys. The merged properties appear on every task API response and are queryable with the standard filter DSL (e.g.filter=properties.invoiceRegion: 'EMEA'). StatictaskData.propertiesdeclared on the CREATE_TASK step apply first and script-supplied keys win on conflict; if several upstream scripts supplytaskPropertiesthey merge in completion order (latest completion wins per key). Only steps that directly depend on the script receive the properties — there is no transitive propagation. Returning a non-objecttaskPropertiesfrom a script fails that script step with a clear error; the merge step itself is failure-soft — a merged properties blob over 256KB is dropped with a warning and never fails task creation. This is the creation-time counterpart totasks.setProperties, which you still use to update a task that already exists.
- Knowledge sets recover from corrupt matching expressions. A knowledge set’s feature-matching expression could accumulate broken conditions — a blank condition, a feature condition with no feature chosen, or a leftover condition after un-negating a group. Any one of them evaluated as false and pulled down the whole All of group it sat in, so the set silently stopped matching every document. Broken conditions are now dropped automatically when the expression is evaluated, so an affected set starts matching again on the next processed document with no need to re-open and re-save it. The expression builder no longer creates these conditions, and any leftover unrecognised condition now appears as a clearly-labelled, removable Invalid condition row instead of an empty box you can’t delete.
- Deleting a knowledge set now stops it matching documents. Previously a deleted knowledge set kept matching every newly processed document indefinitely, because the eligibility lookup did not exclude deleted sets. Deletion now removes the set from document matching as expected.
- Expression builder handles negation and mode switching predictably. A NOT block now carries the same All / Any / NOT control as any other group, so negation can always be turned back off — previously choosing NOT hid the control and left the block stuck. Negating an already-negated condition now removes the negation instead of double-wrapping it. The Simple / Advanced toggle follows the expression itself: switching from Advanced to Simple on an expression that uses NOT or nested groups asks for confirmation first (Simple mode keeps only a flat list of features), and adding a feature to the palette no longer bounces you into Advanced mode and traps you there.
- Inactive features stay visible in the feature palette. Features linked to a knowledge set but deactivated are now shown in a distinct Inactive features group — struck through and removable — instead of being hidden. Hiding them could make a palette look empty right after adding a feature, with no way to see or clear the dead entry. The quick-create / feature picker also no longer offers deactivated features, since selecting one appeared to succeed but then matched nothing.
- Project-scoped knowledge sets are labelled correctly. A knowledge set scoped to a project no longer mislabels itself as Organization Level in the sets list and detail panel when the project object hasn’t been loaded — it now recognises the project scope and shows Project-scoped (with the project name when available). The knowledge-set API also returns the associated project, so the correct name is shown. Previously the false ‘Organization Level’ label could hide a mis-scoped set from its author.
- Deleting a knowledge set no longer shows a spurious error. Deleting a knowledge set used to send the delete request twice, so it reported both a success and an error at once — the second request hit the already-deleted set. Deletion now runs exactly once and reports a single result.
- Transposed grid rollup cards gained toolbar and header polish. An expand/collapse-all button toggles every rollup parent row at once and re-applies after the grid rebuilds; an auto-filter toggle (on by default) populates the description/value filters from whatever you click in the document viewer, with numeric content driving the value filter and text driving the description filter; and column headers now show the full document name, truncated responsively with the full name and summary in a hover tooltip, and the lock and knowledge icons kept snug beside it. Row numbers also stay stable while a search or filter is active, and columns open at a uniform default width regardless of document-name length.
- Adding to and editing a transposed grid rollup now behaves correctly around locked documents and on save. The add (+) button only appears on columns whose document matches the active selection’s source document, so you can no longer add into the wrong document’s column; inline description edits are blocked on rows whose contributors all live in locked documents and skip locked columns when saving mixed-lock rows; and showing/hiding columns is still allowed for locked documents (locking blocks data edits, not view-only preferences). Separately, a newly added rollup group is no longer silently merged into an existing group, and a row added via the popover no longer vanishes, when the document is saved.
- Opening a grid’s column menu no longer spuriously opens the first row’s dropdown. On editable data-object grids, opening or dismissing a column-header menu or the Choose-Columns panel could pop open the first row’s SELECTION dropdown. That no longer happens — the first row’s dropdown opens only on a real click or keyboard focus, not when the grid shifts focus internally.
- A stray dropdown no longer floats in the top-left corner when a form loads. On load, a form auto-activates its first document-sourced field so it is ready for copy-from-document. When that field was a SELECTION dropdown on an inactive, hidden
v2:tabspanel, activating it could leave a stray dropdown floating in the top-left corner. Activation now skips fields that aren’t currently visible, so the stray dropdown no longer appears. - Read-only SELECTION fields now display the option label instead of the stored code. An attribute editor with
readonly: truebound to a SELECTION taxon rendered the raw stored code rather than the chosen option’s label. Read-only SELECTION fields now resolve and show the option label, falling back to the raw value only when no option matches or the options have not loaded yet.
- Show a field’s full value on hover in grids. A new per-taxon display flag,
typeFeatures.showFullOnHover: true, makes a grid column show its full cell value in a hover tooltip — useful for prose-length fields like AI explanations, notes, or comments without widening the column. Enable it from the taxon editor (“Show full text on hover”) or set it in the data-definition YAML. Empty cells show no tooltip. changed:dataAttributesubscriptions fire when a value is first entered, not only on later edits. An event-subscription script — and any derive logic it drives — now runs the moment a reviewer sets a value on a previously-blank attribute, matching the documented “fired when a data attribute changes” contract. Previously the subscription fired only on subsequent edits, so a reviewer’s first pick into a blank field could leave dependent fields un-derived until the value was cleared and re-entered.
- Formula-typed fields are computed and stored during server-side document processing. When a document is opened outside the browser review UI — in server-side extraction and transform steps — FORMULA taxon values are now re-derived against current inputs and persisted, including creating formula attributes that don’t yet exist, before validation, conditional-formatting, and selection-formula passes run. This brings server-side processing in line with the browser, so downstream consumers such as validations, exports, and analytics see correct formula values without a reviewer first opening the document.
- Extraction-time validations and formulas now resolve group references correctly. When validations and formulas run as part of model processing — before the extracted data has been persisted — group references now resolve against the in-memory data tree instead of relying on database IDs that aren’t assigned yet. Previously a bare group reference could resolve to nothing at extraction time, so a rule such as
isnull({group})fired a false failure on clean data, and a validation exception on a nested group or attribute was anchored to the top-level object instead of the row it applied to. Exceptions now attach to the correct nested data object and attribute, and group-scoped formulas evaluate correctly.
- Document-level validations added in one processing step now evaluate in later steps. When a step adds document-level taxon validations — for example a transformer calling
set_validations— and extraction later runs in a separate process, those validations are now read back and evaluated, so the expected data exceptions are raised. Previously they were held only in memory, so a freshly opened document saw an empty set and raised nothing. Such validations are now also included in JSON export. - Grouped tags no longer corrupted when converted to and from features. Converting tags to and from their feature representation — for example exporting a document to JSON and re-importing it, or adding tags through the feature API — now preserves the full tag payload, including the UUID-based grouping identifiers, so grouped and owned tags are no longer rewritten as ungrouped, ownerless rows that extraction would fragment into spurious data objects. Camel-case, snake-case, and legacy field-name variants of the payload are all accepted. Removing a tag from a node now removes every instance of that tag name (not just the first), deletes the underlying tag once nothing else references it, and is a silent no-op when the node doesn’t carry the tag (it previously raised an error) — so loops that strip a node’s tags one by one run cleanly.
- Copied text reads in reading order on rotated pages. Selecting text on a rotated page — most visibly a page turned 180° — and using Copy value from document (or a native Ctrl/Cmd+C copy) now returns the text in visual left-to-right order instead of reversed. The highlight/overlay layer also stays aligned to the page across every rotate path — toolbar button, keyboard shortcut, and form-shortcut bridge.
- The document viewer no longer gets stuck on a permanent loading spinner for dense pages. A page that takes a couple of seconds to render — for example a full broadsheet with thousands of words — could previously leave the viewer stuck on an endless loading spinner instead of displaying. These pages now settle and render correctly.
adopt_children(replace=True)no longer discards nodes nested under the children it replaces. When the nodes being adopted were descendants of the children being replaced — for example re-flattening a line whose words sit under intermediate column nodes — the replace step used to cascade-delete them, leaving empty phantom children that broke later processing. Adoptees are now re-parented before the replace-removal, so they survive intact.- Document-engine updates apply cleanly on reload. When a new engine version ships, the in-app “Update Available” reload now reliably fetches the fresh engine instead of re-serving a stale cached copy — which could loop the update prompt or force a manual “Empty Cache and Hard Reload.” A version mismatch detected after boot now self-heals by re-fetching the engine once and rebooting, with no user action required.
- Rich-text editor toolbar gains tooltips and accessible labels. Every button on the markdown editor toolbar — bold, italic, link, heading, ordered/bullet lists, undo/redo, and the table controls — now shows a hover tooltip and carries an accessible label, so the icon-only controls are clearer on hover and readable to screen readers.
kdx sync push --dry-runnow validates against the live server. A dry-run push previously computed its diff entirely client-side and never exercised the platform’s write path, so metadata that would fail server validation still passed a dry run and only failed on the real push. Against a 2026.8 or newer server,--dry-runnow submits each would-be create/update with the new?validate=onlyrequest parameter: the server runs the full write path — validators at enforced strength plus slug, foreign-key, and common-rule checks — and persists nothing (returning200 {"valid": true}, or an RFC 9457 problem+jsonerrors[]body on rejection). Error-severity findings are reported per resource and make the command exit non-zero, so a CI job can gate metadata-repository pull requests onkdx sync push --dry-run. Against older servers the dry run automatically falls back to the previous client-side diff. The?validate=onlyparameter can also be used directly against any write endpoint to validate a payload without persisting it.- Credentials are redacted from CLI diagnostic output. Error messages and
--debugrequest/response logs now mask credential-bearing values and headers — API keys, tokens, secrets, passwords, passphrases, private keys, and connection strings — before they are printed, so a failed apply or a debug session no longer writes live credentials into terminal or CI logs. Redaction is display-only; the actual request payloads sent over the wire are unchanged. -o jsonoutput is now complete and pipe-safe. List commands run with-o jsonnow return every page of results instead of only the server’s first page (about 20 rows), and all informational and debug logging is written to stderr. As a resultkdx get … -o json | jqpipelines are no longer silently truncated or corrupted by log lines mixed into stdout. Pinning an explicitpageparameter keeps the single-page behavior.- API discovery cache is keyed per server. The CLI’s cached API discovery spec is now stored per server URL with a 24-hour freshness window. Switching between profiles that point at different servers no longer runs commands (run, describe, validation, resource operations) against another server’s cached API shape.
- Copy chat messages in the format you need. The copy button on a chat message is now a dropdown with three choices: Copy as Markdown (the raw message source, the previous behavior), Copy as Text (the rendered message as plain text), and Copy as Rich Text (formatted HTML with a plain-text fallback, so pasting into Word, Google Docs, or an email keeps the formatting). Rich-text copy falls back to a plain-text copy where the browser clipboard API isn’t available.
- AI-suggested activity names no longer surface error text as the title. The activity naming assistant is now instructed never to place an error message or explanation in the title field, so a naming hiccup no longer produces an activity titled with raw error text.
- Search users in the admin Users view. The admin Users view now has a free-text search box that matches across a user’s email, first and last name, job title, and business group, resolved server-side via
GET /api/users?query=. Existing users are searchable by the newly searchable title and business-group fields immediately.
Release2026.7PlatformData FormsFormulasActivitiesIntakeAnalyticsPerformanceTaxonomyBreaking
Release 2026.7
Rollup of every customer-facing change in the 2026.7 GA release. The headline items span the form-building surface: a newifBlank() formula function and steadier formula recalculation, new data-form controls for AI explanation text and selection hints, and keyboard-driven detach / dock / zoom of the document viewer. Alongside those it brings a queryable Work Sessions analytics dataset, a faster task-open path, a wave of Activities polish (plan provenance, live progress, a smoother New Activity wizard), and data-modeling safeguards around taxon external names. One breaking change lands in intake — post-upload scripts can no longer overwrite reserved document fields; see the upgrade note.Data Forms:- Render an attribute as an AI explanation callout. A new
editorOptions.displayAs: "explanation"renders an attribute as a wrapping AI callout that preserves line breaks and grows to fit its content — for AI rationale text that used to clip inside the fixed-height value box. - Selected-option hint shows beneath a closed SELECTION dropdown. A
SELECTIONeditor now shows the chosen option’s hint (plain text or markdown) below the closed dropdown, so the guidance for the current choice stays visible. It is on by default and opt-out viaeditorOptions.showSelectedHint: false, and is suppressed inside grid cells. - Drive the document viewer from form shortcuts: detach, dock, zoom. New
bridge.viewermethods let declarative form shortcuts control the document viewer the way the toolbar buttons do:viewer.detach()pops the viewer out into its own window,viewer.dock()re-docks it, andviewer.zoom("in")/viewer.zoom("out")change the zoom level.
- New
ifBlank()formula function.ifBlank(a, b, …)is a blank-aware coalesce: it returns the first argument that isn’t blank, and an empty string when every argument is blank. UnlikeifNull, it treats an empty or whitespace-only string as blank — so a chain of references falls through to the next candidate — while0andfalsepass through unchanged. - Aggregates over a missing reference settle to zero instead of erroring. A multi-part reference inside a formula that can’t be resolved now yields an empty list rather than failing the calculation, so an expression like
ifnull(sumifs(…), 0)settles cleanly to0. This removes the transient red “formula calculation failed” recalc toast that used to appear while the referenced group didn’t yet exist. - Parent-relative selection formulas resolve on create. A selection formula that reads a parent value with a
{../Field}reference now resolves to the real value the moment a row is created, instead of an empty list. Previously a service-bridge-backed selection could fire with an empty argument on a brand-new row; the parent value is now available on first evaluation.
- New Activity wizard loads the full plan before the details step. When you pick a plan, the wizard now fetches that plan’s full body before rendering the details step, so the document-upload panel appears right away — no more going Back and re-selecting the plan once a background refetch lands.
- Activity-plan provenance on tasks and activities. The task details sidebar now shows the Activity Plan that spawned the task (hidden when a task wasn’t spawned from a plan), and the activity dialog header shows a “Plan” chip naming the plan.
- Activities grid progress reflects live step status. The Activities grid’s progress column and counters are computed from live step statuses (completed / running / failed) instead of lagging summary fields, so progress tracks what’s actually happening.
- Spawn-time validation errors are surfaced. When an activity plan fails validation as it’s spawned (for example, from a script step or a completed-activity trigger), the specific validation error is now surfaced and recorded instead of the spawn silently rolling back.
- New “Work Sessions” analytics dataset. A queryable Work Sessions dataset reports wall-clock time versus actual active/engaged time per task, including an active-ratio percentage — so you can see how long work really takes against how long a task was open.
- Opening a task workspace is faster. A task’s fetches are now parallelized and de-duplicated, the next task in a group is warmed in the background, and the open reuses data that is already loaded — so tasks open noticeably faster, especially when advancing through a queue.
-
Post-upload intake scripts can no longer overwrite reserved document fields (breaking). A post-upload intake script that returns a reserved structural metadata key —
source,uuid,version,labels,mixins, orstatusId— now fails the upload and rolls back the transaction, instead of silently overwriting the document’s structural fields. These keys back dedicated document fields (for example,sourceholds the document’s original filename). Upgrade note. If an intake’s post-upload script sets any ofsource,uuid,version,labels,mixins, orstatusIdin its returned metadata, those uploads will now fail rather than silently overwrite. Rename the offending field to a non-reserved key (for example,documentSource) before upgrading.
- Blank taxon external names default to PascalCase over the taxonomy MCP tool. When an agent creates a taxon with a blank external name via the taxonomy MCP tool, the external name now defaults to PascalCase of the internal name (for example,
invoice_datebecomesInvoiceDate). An external name the agent supplies is never overwritten. - Clear error when a taxon has no external name on export. Data-object (JSON) export now fails loudly and names the offending taxon when a taxon has no external name, instead of exporting under an empty key or a silent fallback; XML export enforces the same.
skipExtractiontaxons are excluded from chunking again. Taxons flaggedskipExtractionare once more excluded from document chunking and extraction, restoring the intended contract.
Release2026.6PlatformWorkflowManageData FormsTask GroupsAnalyticsPerformanceDocumentOrchestrator
Release 2026.6
Rollup of every customer-facing change in the 2026.6 GA release. The headline items: a new top-level Manage administration area that gathers every org-admin surface in one place, and a Kodexa Workflow MCP connector that exposes activities, tasks, and task-groups to claude.ai as a remote OAuth connector. Alongside those, this release brings a wave of review-experience performance work, a broad set of Data Forms V2 shortcut and grid fixes, task-group polish, opt-in user presence & activity tracking, and analytics and orchestration fixes.Manage:- A dedicated Manage area. Organization administration now lives in its own top-level Manage area, alongside Studio, Workflow, and Knowledge, and is visible only to administrators (the
MANAGEorPLATFORM_ADMINrole). It gathers the organization-admin surfaces in one place — Organization Profile, Teams, Document Tags, Secrets, Intakes, Custom Modules, Subscriptions, Model Library, and Concurrency — which previously lived scattered through Studio. - Organization Profile → Features. Organization Profile now has a Features tab with two org-level toggles: Strict team matching (when on, take-next assigns only candidates matched to a reviewer’s team; by default it also offers work that isn’t assigned to any team when nothing matches) and Presence tracking (opt-in per-user presence and activity, off by default — see Platform below).
- Kodexa Workflow MCP connector. The platform now exposes its workflow surface — activities, tasks, and task groups — as an MCP connector you add to claude.ai as a remote OAuth connector. It provides 19 tools spanning reads (identity, and listing/reading organizations, projects, teams, members, activities, tasks, task groups, and task statuses) and writes (assign and unassign tasks and task groups, update task status, and add or remove tasks from a group). Enable it with the
mcp.enabledandmcp.publicUrlsettings; claude.ai runs the OAuth flow automatically, and anX-API-Keyfallback covers programmatic clients. Every call is scoped to the calling user’s access — the connector grants no broader visibility than the REST API. Activities are read-only over MCP, and a grouped task’s assignee is managed through its group.
- Review pages and task groups load faster. Review tasks open faster, and the project data behind a review session is cached longer so a kiosk reviewer isn’t re-fetching it on every task. The task and activity-plan list endpoints gained an opt-in
?view=summaryprojection that returns a much lighter payload for high-volume listing. Task groups also prefetch the next task’s document in the background, so advancing through a queue opens the next document instantly.
- Full document-viewer keyboard shortcuts on data forms. Data forms can now drive the document viewer entirely from the keyboard — page-step and viewer-scroll alongside rotate — declared through the declarative form-shortcuts system. New Bridge methods back this:
navigation.previousPage()/navigation.nextPage()step the viewer a page at a time (clamped at the document edges), and a newviewer.scroll(direction)nudges the viewport up/down/left/right.viewer.scrollrequires a newviewerbridge permission; the page-step methods use the existingnavigationpermission. - Keyboard shortcuts fire reliably on Mac + Chrome. Declarative form shortcuts had stopped firing for Mac users on recent Chrome; they now trigger reliably again, with no form changes required.
- Grid sort no longer reorders rows mid-entry. A grid’s declared
sortis now applied once when it first loads a task, then locked — so a newly added row appends in place instead of jumping while an operator is typing. Manual column-header sorting still works, and the resulting row order is remembered per task across refresh and for other reviewers viewing the same task. - Promote and Copy mark the target as edited. Promoting or copying a value into a field now shows the same edited-value indicator as a manual edit, and click-to-source navigation still works on the copied field.
- Fewer redundant service-bridge lookups. Form lookups backed by a service bridge no longer refetch when an unrelated field on the form changes — only a genuine change to the lookup’s inputs re-runs it — cutting flicker and load.
- Large service-bridge responses no longer hang a document. A very large service-bridge response (for example, a long list of selection options) used to stall a document on open; large responses are now handled cleanly.
- Detached viewer behaves, and form shortcuts reach it. Popping the document viewer out into its own window no longer freezes the page when you focus an attribute or tag in the main workspace, and declarative form shortcuts (rotate page, viewer scroll) now take effect in the popped-out window.
- Assignee picker populates again. The assignee picker on a task group could come back empty; it now lists the organization’s members and matches on email as well as name.
- Search matches partial terms. Task-group search on the organization and project lists now matches partial terms anywhere in the name, instead of requiring an exact match.
- Nested line-item detail reaches the data lake. The data-lake projection previously populated only top-level data objects, so nested child groups came through empty. Nested detail now projects correctly at any depth, in the same order reviewers see it; top-level projection is unchanged. It populates on newly processed documents — reprocess if you need historical coverage.
- User presence & activity tracking (opt-in, off by default). Organizations can opt into per-user presence and activity signals from Manage → Organization Profile → Features → Presence tracking. It is off by default, and a legal/privacy notice is shown before enabling it. When enabled, the UI reports only derived signals — whether a user is active vs. idle, tab visibility, and how long tasks take to open — sampled roughly every couple of minutes. No raw input is ever captured: no keystrokes, no mouse coordinates, no scroll positions. Collection is enforced on the server, so nothing is gathered for an organization that hasn’t opted in.
- Orchestrated activity hand-offs authenticate everywhere. When one activity spawns a follow-up (from a script step or a trigger), the hand-off now authenticates correctly in every environment, so chained activities launch reliably.
PlatformPerformanceDocumentData FormsStudioFormulasKnowledgeOrchestrator
Faster document loads, correct totals on open, and reliable saves
This release focuses on speed and dependability across the review experience. Documents open and recalculate dramatically faster, conditional-format highlights and formula totals are now correct the moment a document opens — not only after a reviewer’s first edit — and reviewer edits are reliably captured on Approve. It also adds a wave of new Data Forms V2 components alongside a broad set of grid, formula, knowledge, and orchestration improvements.Faster document loads and edits:- Editing large documents is dramatically faster — Recalculating a document used to issue a separate database query for each attribute, which multiplied quickly — a single edit could cascade into thousands of queries. The same work now takes a handful (one measured edit dropped from roughly 12,800 queries to 2), so edits and their downstream recalculations stay snappy even on large documents.
- Documents recalculate up to ~88× faster — Documents now refresh incrementally by default — 4.4s down to 46ms in one measured case — with an automatic fallback for unusually complex dependency graphs.
- Tasks open progressively — The first data form appears as soon as its own document is ready, rather than waiting for every document on the task. Off-screen views defer presentation work — text indexing, summaries, page tags — until you open them, and redundant loading and artificial delays were removed from the open path.
- Switching tasks keeps sessions light — Moving to a new task releases the documents the new task no longer needs, so long review sessions stay responsive, while a short history cache keeps back-navigation instant. Cleanup is always held off while a save, edit, or popped-out sidecar is still active.
- Rollup totals always match their formulas — Rollup cards now compute through the same engine — with the same
sum()semantics — as the formulas they summarize, and refresh together with them, so a rollup and its underlying formula can no longer disagree. - Leaner edit updates — Editing an attribute now sends just that attribute to the browser instead of its entire containing group, cutting overhead on documents with large tables.
- Faster, more reliable startup — The document engine now loads entirely from Kodexa with no third-party CDN dependency at startup, and moves document data across the browser more efficiently.
- Conditional formatting shows on open — no edit required (ENGG-5296) — A mismatch highlight (for example, a billed total that doesn’t match the summed line items) used to stay hidden until a reviewer touched the document. Highlights now render correctly on first paint, against the stored values as they are.
- Formula totals are correct on open — Formula values (sums, weights, charge totals) now show their correct computed value the instant a document opens, instead of a stale zero that only corrected itself after the first edit. Documents that are already correct open clean — with no false “unsaved changes” — and any missing formula values are computed and filled in automatically.
- Conditional formatting updates when you delete a row (ENGG-5265) — Deleting a line item now re-checks any total that depends on it, so a balance that moves into or out of tolerance on a delete reflects immediately.
- Approve always captures your last edit (ENGG-5291) — Selecting a value and immediately clicking Approve could previously miss that value if its write hadn’t finished landing. Save and Approve now wait for in-flight edits to settle first — and show a clear error rather than silently continuing if an edit is stuck.
- Form values no longer revert after Approve (ENGG-5269) — Editing a form and approving could take two clicks and briefly appear to revert. Edits are now applied to the document before any follow-up step runs, so the first Approve takes effect.
- No edits lost during a save (ENGG-5292) — An edit made while a save is already in progress is now included in the next save instead of being dropped or needlessly re-sent.
v2:routeTimeline(new) — Renders a group taxon’s rows as a vertical timeline of numbered stop cards for ordered lists such as a multi-stop shipment route. Stops drag-reorder (the sequence attribute is rewritten to the new position on drop), each card has an inline-editable detail panel, and per-row delete is built in. Additional props: + Add stop and AI extract stop (single-record extraction anchored on highlighted document text, with one level of nested sub-object such as an address), a find-in-document button that scrolls the viewer to a stop’s source span,show: 'firstLast'to collapse the middle of a long route behind a ”…+N” toggle,readonlyandorientation(vertical/horizontal) props, and an optional location-code badge. The group taxon, sequence tag, type tag, and sub-object path are all configurable.v2:attributeCopyAction(new) — A scalar sibling-copy button for form layouts (distinct from the grid-cell copy components): it evaluates the source value through the formula engine and writes it to a sibling target. Like the other copy components it now writesdecimalValue, so number-typed targets re-render immediately.v2:attributeRowPromote(new) — Replaces the per-target chevron columns on candidate grids with a single Promote to… dropdown per row, with an optional per-target source-tag override. Promotes are idempotent and now writedecimalValueso number targets update without tabbing away (ENGG-5217).v2:gridpagination, sort, and sortable custom columns (ENGG-5267) —v2:gridgained opt-inpaginationandsortprops (reviewer grids still default to showing all rows), and custom columns can opt into sorting with a header-matched sort spec.SELECTIONcells now close their popover on pick instead of staying open until Tab/Escape.- Form Completeness Gate (new) — A per-form primitive that lets a task action stay disabled until reviewers have actually looked at the data they should.
v2:tabsgainsmustView(flags unvisited tabs with an amber dot and a “N tabs to review” banner),v2:panelgainsmustExpand/mustScroll, and outstanding workspace exceptions fold into the same list. Actions opt in withgatedByCompleteness: trueand surface an info-popover listing what’s left; existing forms and actions are unchanged. v2:panelpolish — Newdescriptionsubtitle prop and aniconColortile palette (avatar-style tinted icon) matching the rest of the app, plus header icons on tabs and panels.- Rotate-page keyboard shortcut (ENGG-5217) —
alt+R/alt+shift+R(⌥R / ⌥⇧R on Mac) rotate the current page in the document viewer right / left, wired through the declarative form-shortcuts system. The server-sideDataFormschema gained theshortcutsfield, so forms declaring ashortcuts:block now round-trip correctly (the block was previously dropped on save). - Vertical radio layout — Attribute editors accept
radioOrientation: 'vertical'so adisplayAsRadiofield can stack one option per line instead of wrapping across the row (default stays horizontal).
sumifs/countifsaccept single-row groups — A formula likesumifs({Group/Value}, {Group/Use}, true)previously errored (“first argument must be an array”) whenever the source group resolved to exactly one row, surfacing as a transient “Formula calculation failed” toast that vanished once a second row was added. Scalar range / criteria arguments are now coerced to a one-element range, so one-row and many-row groups take the same path.- String cleaning patterns applied on attribute create — Taxon
stringExtract/stringReplacecleaning patterns were only applied when an attribute was updated, not when it was first created — so extraction tagging, programmatic adds, the API, and script-drivensetAttributeall bypassed the cleaning that the same attribute would get on a later edit. The patterns now apply at create as well, and the redundant UI-side normalizer was removed. - Direct copy/extract value appears immediately — Adding a value via direct copy now patches the cache optimistically and emits the change event, so the value shows at once instead of only after tabbing out of the field (which used to trigger an expensive full-cache refresh).
- Preprocessor keeps rotation-corrected images — When auto-orientation rotated a page, the rebuilt processed PDF was falling back to the original (un-rotated) source page and silently discarding the corrected image. The rebuild now embeds the rotated image bytes while preserving the viewer’s “already corrected, don’t CSS-rotate” signal.
- Column-header clicks no longer steal focus into the first cell (ENGG-5268) — Clicking a column header in an editable data-object grid was moving focus into row 0’s first cell and opening its editor. A real user header click now suppresses that redirect while still preserving focus-from-outside and post-add-row refocus behaviour.
- Activities filter inputs persist across remount (ENGG-5215) — The Activities grid’s toolbar inputs (document-family filter, feature facets, quick filter, date range) and the applied filter could fall out of sync on tab switches, breadcrumb navigation, or reload — the inputs looked empty while the list stayed filtered. The toolbar state now persists and is realigned on remount.
- Bulk “remove by source” count stays accurate (ENGG-5264) — The grid’s per-source remove buttons could show a stale row after a bulk delete when overlapping async recounts resolved out of order; a sequence token now discards superseded runs.
- Grid search hydrates from the saved query — The grid search input now repopulates from the persisted query on load, so a saved search shows its text instead of an empty box over a filtered list.
- Escaped image markdown normalized on write — Image references pasted from Word or HTML into the rich-text editor were serialized as escaped literal text (
!\[\](attachment://…)) and rendered as plain text instead of the image. The editor now normalizes on edit, and the API normalizes on every create/update — scoped to markdown-typed fields so intentional text escapes (e.g.\[see codes below\]) are preserved. - Readonly-taxon panel shows the extracted value (ENGG-5253) — The knowledge readonly-taxon panel rendered ”—” instead of the extracted value when the stored dependency used the taxon’s external-name chain while extraction keys attributes by taxon path — two different namespaces that never matched. The taxon picker now persists the taxon path so the reader matches directly. (Existing knowledge sets need the dependency re-selected and saved to migrate.)
- Activity SCRIPT steps can read
inputs— A SCRIPT step body can now read the activity’s materialized inputs via theinputsJS global (previously only reachable insideBRIDGE_CALLrequest templates — referencinginputs.Xin a SCRIPT threw a ReferenceError). It always defaults to{}, so notypeofguard is needed. - Script-step timeout raised to 60s; failed-step logs retained — Long enrichment scripts on multi-document inputs were being interrupted at the old 15s limit; the
SCRIPTstep timeout is now 60s. A failed step’s log pointer is also no longer discarded by the per-item rollback, so the step-logs view keeps showing the logs for a step that failed — coveringSCRIPT,BRIDGE_CALL, andAI_PROMPTsteps, which share the log path.
PlatformStudioData FormsScriptingFormulasKnowledge
Post-2026.4.1 patches — copy rules, source badges, multi-instance attribute paths
The week after the 2026.4.1 cut shipped a batch of reviewer-workflow polish, new V2 data-form components for promoting candidate values into canonical slots, and the under-the-hood refactor that finally derivesDataAttribute.path from parent + tag everywhere (closing out ENGG-5214). A handful of script-API and formula fixes ride along.Studio reviewer workflow:- Open Task is a primary button that opens a new tab — The activity-status dialog’s “Open Task” affordance was a small text link that closed the dialog on click. It’s now a primary
open-in-newbutton, opens the task route in a new browser tab viawindow.open, and leaves the activity dialog mounted so reviewers keep their context. - Activity dialog auto-zooms to the active step — Opening the plan tab no longer fits the whole graph; it focuses the running step (or the last terminal step on completed plans), falling back to
fitViewonly when there’s no anchor. A one-shot watcher covers the race where the dialog mounts before the layout finishes. - Attribute source badge: per-document-type instance numbering — The badge now shows “Bill of Lading #1, #2, #3…” independent of how many Invoices or other classified pages interleave between them, instead of the preprocessor’s global group sequence (which made every BoL in a doc read as the same number). Also fixes an off-by-one between
node.getPage()(0-based) and the resolver’s 1-based classification map that was making attributes anchored to page 2 read as page 1. - Filter in ag-grid column kebab menu — Column header menus on all ag-grid surfaces now expose ag-grid’s
columnFilteritem alongside sort/pin/etc, gated oncolumn.isFilterAllowed(). - Page-size selector no longer steals focus into row 0 (ENGG-5252) — Clicking the grid’s page-size selector previously opened the first row’s first-column dropdown on top of the page-size popup. The grid’s focus-redirect logic now ignores clicks landing in the pagination chrome.
v2:attributeCopyButton.relatedCopies(new) — The copy button now accepts an optionalrelatedCopies: [{sourceTagPath, targetTagPath}, ...]prop. After the primary copy lands, each related pair runs through the same copy logic, so promoting a weight from a candidate-weights grid intoshipments/shipweightcan carry the matching UOM intoshipments/shipweightuomin the same click.v2:attributeRowDeleteButton+v2:gridDeleteBySource(new) — Per-row inline delete cell + a sibling toolbar component that surfaces “Remove all<source>rows” buttons (one per detected source document) with a confirm dialog before bulk delete. Both share the same(document_type, group)resolver the source badge uses.v2:attributeSourceBadgeonv2:grid(new) —v2:gridgained acolumnsprop that mounts arbitrary V2 components as per-row cell renderers, alongside the existing taxon-driven columns. The newv2:attributeSourceBadgerenderer shows one colored pill per distinct(document_type, group)tuple of source attributes; click dispatchesworkspace.focusTagfor in-viewer navigation.v2:gridheightprop honored even with parent data object — Previouslyv2:grid’sheightwas silently dropped on any grid running under a parent scope (i.e. every form grid), forcing a row-count-based auto-calculation. An explicitheightnow always wins.- Form-level
copyRuleson DataFormV2 —DataFormV2now accepts a top-levelcopyRules?: TaxonCopyRule[]. Cards merge form-level rules with their own per-card rules (card-level wins onsourceTaxonconflict). Replaces having to repeat the same copy block on every source panel in forms with many source-document instances. - Formula-driven
stampAttributeson copy rules —CopyBehaviorOptionsgainedstampAttributes?: Record<string, string>for stamping derived audit/provenance attributes onto the copy destination. Values can be literal strings or${source.idString}/${source.parent.blnumber}/${source.parent.uuid}templates. Distinct fromcopyAttributes(which clones existing source attributes with content-tag preservation),stampAttributesinjects new derived fields with no content backing — useful for stampingsourceDocumentRef,sourceBolNumber,sourceDocumentTypeonto rows promoted into a canonical grid.
setAttributeroutes numeric taxon types toDecimalValue—setAttributewas only matchingDECIMAL, so writes againstNUMBER,INTEGER,CURRENCY, andPERCENTAGEtaxons fell through and stored the value inStringValue. The form’s numeric editor then read the typed slot and rendered empty even though.valueshowed the right string. The switch now covers every numeric taxon type the data model acknowledges, and routesSELECTION/URL/EMAIL/PHONE/SECTION/DERIVEDinto theSTRINGcase explicitly.addAttributeauto-resolves type from the taxon —addAttribute’sTypeAtCreationresolution now follows the same precedence assetAttribute:opts.typewins, then runtimeTaxonResolver, then the document’s cached taxonomies, then inference from the supplied typed-value field. Previously scripts callingaddAttribute({tag:"chargecode", stringValue:"DSC"})logged “filled” but the resulting attribute couldn’t bind to formSELECTIONdropdowns, the formula evaluator, or conditional-format rules. Choice betweenaddAttributeandsetAttributeis now about find-or-create semantics, not type safety.pathopt onaddAttributeis ignored with a warning — Paths have been derived fromparent + tagsince ENGG-5214; thepathopt was silently dropped before, nowlog.warns so callers can see they should drop it.SPEC.gono longer listspathas a valid option.- Script dirty tracking across nested data objects —
ScriptDataObjecttraversal now propagatesparentDocso attribute changes made on a nested object correctly mark the document dirty for downstream persistence.
- Empty group refs return an empty list (ENGG-5227) — Formulas like
sum({Accessorials/ChargeAmount})evaluated against a parent whose child group had no instances (e.g. all accessorial rows deleted) previously returned a"reference could not be found"error, and the recalculator skipped the write — leaving the previously computed Sum of Line Items on screen instead of zeroing it. The path resolver now returns[]for the empty-group case (matching the sibling branch’s existing semantics thatsum/min/max/avgrely on), and the formula explain panel stops surfacing “Reference X/Y could not be found” for empty groups. - Missing-reference errors null out the stored value (ENGG-5227) — When
EvaluateFormulareturns a newMissingReferenceError(distinct from syntax/runtime errors), the recalculator now nulls the attribute’s stored value fields and persists. Transient evaluation failures still preserve the previous value so they don’t blank legitimate output. DataAttribute.pathis derived everywhere (ENGG-5214 close-out) — Thepathcolumn has been dropped fromkddb_data_attributes; every read now routes throughGetPath()which composesparent.path + "/" + tagon the fly. Extraction, move, copy, formula reactivity, and the WASM serializer have all been migrated. External consumers reading attribute path from the JSON envelope are unaffected — the field is still emitted with the same value, just computed instead of stored. (The GoDataAttribute.Pathfield andpathOverrideargument onCopyDataAttributehave been removed.)
- Knowledge feature search hits
extendedPropertiesand numeric values — The “Filter by feature” popup’s?query=search runs against thesearch_textcolumn, which previously only indexed slug + string-valuedProperties. Human-readable labels stored inExtendedProperties.namewere never matched, and numeric scalars (e.g.shipperCode: 1540) were silently dropped — typing “JSP” returned zero matches even though chips render the full “JSP International - Legacy” name.BuildSearchTextnow walks bothPropertiesandExtendedPropertiesrecursively, stringifying every string/number/bool scalar; a companion migration rebuildssearch_textfor every existing row so environments don’t have to re-save each feature.
loadTaxonomyreads structured metadata, not staleyaml_source— The script-engine adapter was readingkdxa_taxonomies.yaml_sourceand re-parsing YAML.yaml_sourceis a round-trip snapshot that drifts behind the structuredmetadatacolumn whenever a client (kdx sync push, platform PUT) updates the taxonomy without rewriting the YAML. Scripts then validated taxon paths against the stale text, surfacing as"taxon path X does not exist in taxonomy Y"in plans that referenced recently added taxons. The adapter now readsmetadata::textand parses the JSON viayaml.v3(which handles JSON as YAML 1.2).
ReleasePlatformData FormsStudioAnalyticsAPI
Release 2026.4.1
Rollup of every customer-facing change between 2026.4 and 2026.4.1. The largest items: a new CDC Data Lake that mirrors every metadata change and document delta into S3, a Page Groups picker in the document viewer that lets reviewers jump straight to each classified section of a multi-document PDF, declarative keyboard shortcuts in V2 data forms, and a reshaped Take Next API that finally distinguishes “nothing to do” from “filtered out by team” from “lost the race.” A long tail of validation, formula, and extraction fixes ride along.Already documented separately: activity-plan scripts can spawn follow-up Activities (the 2026-05-26 entry — it’s also part of this release).Studio reviewer workflow:- Page Groups picker on the document viewer — A new
file-multiple-outlinebutton in the spatial toolbar opens a popover listing each classified physical document on the open file (e.g.Invoice — Pages 1–3,Delivery Receipt — Pages 4–6), with a taxon-colored swatch and click-to-navigate to the start of each group. Built on top of the preprocessor’s per-page classifications + group UUIDs (below) and the existingtagMetadataMapso the labels and colors match what the rest of the UI shows. - Spatial toolbar cleanup — The Show Advanced (
wrench), Developer Info (i), and Page Groups buttons now sit before the find-text input so narrow document panels don’t wrap them onto a second line. - Kiosk: step-out confirm before fetching next work — Reviewers leaving a kiosk task now get a confirm prompt before the next task is auto-claimed, preventing the accidental “I just finished but the next one already opened” race.
- Require a comment on task-template actions (new) — Task-template actions can now declare
requireComment: true(with an optionalcommentPromptstring). When the reviewer clicks the action, a shadcn dialog opens for a mandatory comment before the action runs; cancelling aborts the action with no partial state. The comment rides the existing/api/batch-updatepayload astask.completedActionCommentand is persisted server-side as aCOMMENTtask activity tagged with the action’s UUID so the timeline can link the comment back to the action that produced it. See Requiring comments on actions for the full recipe. - Take Next API: EMPTY envelope replaces 204 (ENGG-5208) —
POST /api/tasks/assign-nextpreviously returned a bare 204 in three distinct situations: nothing queued, filtered out by team, or claim race. The endpoint now always returns 200 with a typed envelope and a reason code (EMPTY,FILTERED_OUT,CLAIM_LOST) so the UI and external integrations can react appropriately. TheprojectIdparameter is also now required and the team filter is enforced for platform admins (ENGG-5209). Integrators that relied on the old 204 will need to update. - Task lock decoupled from document family lock — A task’s lock no longer takes out the entire document family. Multiple reviewers can now work different tasks against the same document concurrently when the task-status policy permits. Existing locking behaviour is preserved for status types that explicitly opt into
lockDocumentFamily. - Faceted filtering across grids — Tasks, Task Groups, Activities, and other primary grids now support faceted filtering by document-family feature + the family itself, surfaced through a shared
KodexaGridFacetBarcomponent. Saved filter state survives navigation. - Activities grid: file-name filter clears on task completion — Completing a task no longer leaves a stale file-name filter on the activities grid.
- Team slug surfaced in grids and forms (ENGG-5185) — Team slugs are now visible in team listings and editable in create/edit forms, matching the slug-everywhere convention used by other resources.
-
Declarative keyboard shortcuts (ENGG-5244, new) — V2 forms can now declare a
shortcuts:array at the top level. Each entry binds a key combination to a named script and is registered under a per-form scope, so a form resets all of its shortcuts every time it mounts with no manual cleanup. See Keyboard Shortcuts for the schema, lifecycle semantics, and worked examples. -
Bridge navigation actions (ENGG-5244) —
kodexa.navigation(the bridge namespace shortcut scripts call) now has realsetPage(page, documentFamilyId?),getCurrentPage(...), andgetPageCount(...)methods alongside the existingfocusAttribute(...). Spatial methods are 1-based externally and route to the document viewer for the form’s first document family by default. See Bridge API & External Services → kodexa.navigation. -
Markdown editor scroll restored — Internal scroll is back on the markdown editor, capped at 1.5×
--editor-heightso the editor no longer takes over the page on long content.
- Preprocessor: canonical taxon-path tagging (ENGG-5240) — When the preprocessor is configured with a
taxonomy:option, the LLM-returneddocument_typelabels (e.g. “Bill of Lading”) are translated to canonical taxon paths (billoflading) before pages are tagged. This makes the preprocessor’s page tags match what downstreamkodexa/llm-taxonomy-modelwrites, eliminating the duplicateInvoice+invoicetag instances that were appearing in reviewer-facing UI. The original LLM-returned label is still kept as apreprocessor.document_typefeature. Existing plans withouttaxonomy:set are unchanged. - Preprocessor: multi-page group on the tag — Page tags now carry
groupUUID(deterministic persource_page.group) andvalue(the sequence within the group), so consumers reading tags alone can reconstruct which pages belong to the same physical document. The existingpreprocessor.groupandpreprocessor.sequencefeatures are kept for backward compatibility. - Spatial copy: cluster by line before sort (ENGG-5205) — Multi-line copies from the spatial viewer were occasionally returning words in the wrong order when lines overlapped on the Y axis. The spatial sort now clusters by line first so copied text reads in natural order.
- Default copied attribute path (ENGG-5214) — Copying an attribute to a different parent now defaults the new path / tag to the destination object’s own path, instead of carrying the source’s path forward.
- Formula reactivity scoped to lineage owners (ENGG-5195) — Conditional-format and formula reactivity now routes to the lineage-scoped owner instead of fanning out across the document, which removes the perceived “everything recomputes” lag on edits in deep tag hierarchies.
- Conditional formats batched per owner —
EvaluateAllConditionalFormatsBatchis now gated on the conditional-format owner set, fixing intermittent integration-test timeouts and reducing wasted work in kodexa-ui. - Reactive validation group rules + auto-derived exception path — Group-level validation rules now re-evaluate reactively when their inputs change, and exception paths are derived automatically so authors don’t have to maintain them by hand.
- Knowledge-feature taxon refs use canonical ExternalName chain (ENGG-5221, ENGG-5222, ENGG-5223) — Several long-standing inconsistencies between the path used to look up a taxon and the path stored on dependency graphs / init-paths / refresh validation have been aligned to the canonical
ExternalNamechain. Symptoms that should now stop: stale validation results after a tag refresh, formulas resolving to the wrong taxon when org slugs were nested, init paths failing to find selection options after an external-name change. The WASM attribute-value bridge also now returns type-aware values (numeric, boolean, date) instead of always-string. - Absolute formula refs require leading slash (ENGG-5221) — Formula references intended as absolute (across taxonomies) must now start with
/. Existing absolute refs that already had the leading slash are unchanged; ambiguous refs that worked accidentally before will now be treated as relative.
STORAGE_LAKE_BUCKET configuration.- Envelopes under
entities/mirror everykdxa_metadata_auditwrite (an audit row is created for every create/update/delete of AbstractMetadata-managed resources — task templates, knowledge sets, activity plans, etc.). - Activity envelopes under
activities/mirror every activity status transition, with rolled-up step errors and deferred publish until the originating database transaction commits (ENGG-5231) so no half-applied state leaks to the lake. - Step / step-document envelopes carry full
errorDetails(ENGG-5235), so analytics dashboards can surface the actual exception message and stack from a failed step without re-querying the source database. - Content-objects under
content-objects/are populated from the KDDB projection onCONTENT_CREATED, giving the lake the post-Apply view of every data object the orchestrator persisted. - Batch context ships task-template ref + work-session context on every batch envelope (ENGG-5202 phases 1–3), so audit / analytics consumers can correlate every change back to the user session that produced it.
- The schema is intentionally append-only; rebuild scripts and a
CHANGELOG.mdunderkodexa-cdc-lake/document the supported event shapes and projection logic.
- AbstractMetadata audit log (ENGG-5150) — A
kdxa_metadata_audittable now records every create/update/delete on AbstractMetadata-managed resources (task templates, knowledge sets, knowledge items, knowledge item types, knowledge feature types, knowledge features, activity plans, prompts, data forms, data definitions). Each row carries the actor, source IP, work-session ID, and a JSON snapshot of the change. Append-only enforced at the DB level. - Generic slug auto-generation + uniqueness — Any AbstractMetadata resource created without a slug now gets one auto-generated from its name, and a
UNIQUE (organization_id, slug)index is enforced on ten audited tables. Nested org refs are resolved on every create/update so manifests can reference an org by ID, slug, or full URI. - Knowledge create/update polish — Knowledge Item creates now populate
slugandknowledge_set_slugcorrectly. Duplicate-key responses now return a clear 409 with the conflicting field instead of a 500 (PX-10).name↔set_nameandtype↔set_typeare mirrored inBeforeCreate/BeforeUpdateso legacy clients writing one form see consistent reads on the other.
- Document-family features in tasks / groups / links datasets — The
tasks,task_groups, andtask_group_linksdatasets exposed via the analytics view now include each row’s owning document family features (knowledge features attached to the family), so reports can group / pivot by feature without joining a separate feed.
- Email extraction from non-standard claims (ENGG-5203 phase A / A.1) — Login no longer fails for users whose ID token lacks a standard
emailclaim. We now check standard OIDC claims, namespaced Auth0 claims, and finally fall back tokdxa_userslookup by sub. - Work-session start time on /api/batch-update (ENGG-5203 phase B) — The UI now sends
userWorkSession.startedAt(the actual session start) instead of the deadtransactionStartfield that defaulted to the current time. Audit rows that previously showed every change as starting “now” now carry the correct session boundary.
- Pop-out keeps main tab as WASM owner — Opening a document in a pop-out window no longer transfers WASM ownership; the main tab stays authoritative and the pop-out reads through.
- Sidecar heartbeat tightened — Heartbeat now counts only actual ping responses, not noise from other event types, so disconnect detection is more reliable on slow networks.
PlatformActivitiesScripting
Activity-plan scripts can spawn follow-up Activities
A SCRIPT step’s return value can now include anextActivity block that asks the platform to start another Activity Plan when the current Activity completes. Use this to chain related workflows — intake → extraction, classification → enrichment, validation → posting — without an external orchestrator.-
Return shape: alongside
actionand the existingfeaturesarray, scripts can now return: -
Deferred spawn: the new Activity starts when the current Activity reaches
COMPLETED, not at the moment the script returns. Multiple SCRIPT steps in one plan may each emit their ownnextActivityand they fan out at completion in step insertion order. -
Same-project only for v1. The target plan must already be bound to the current project via
project_resources. Cross-project spawns are rejected. -
Inheritance: when
documentFamilyIdsis omitted, the spawned Activity inherits the source Activity’s document families. Server-controlledtriggerMetadata(sourceActivityId,sourceStepId,sourceActionUuid,sourceProjectId) is always merged in last so the audit trail can’t be spoofed; the spawned Activity’striggerKindisACTIVITY_COMPLETED. -
Soft failure: spawn errors (missing plan, FGAC denial, input validation failure) leave the source Activity completed and record the reason in
script_result.nextActivityError. On success,script_result.nextActivityIdpoints back to the new Activity. -
Feature attachments:
nextActivity.featuresis applied to the named document families immediately, before the spawn fires, so the new plan’s templates and scripts see them via the existing template context.
PlatformActivitiesTask GroupsKnowledgeScriptingAPI
Task Groups, the New Activity experience, and knowledge in scripts
This release rounds out the Activity-centered workflow model. Reviewers can now batch related work into Task Groups; the New Activity surface replaces the old New Task entry points; activity plans get a richer editor; and activity-plan scripts gain a first-classknowledge global.Task Groups (new):- Bundle tasks into a single assignment unit — Select related tasks from any Tasks tab and use Create task group to wrap them under one name, description, priority, status, assignee, and team. Members work the group as a unit instead of picking off individual tasks.
- Groups tab and kiosk Take-Next — Each project and organization now has an always-on Groups tab. Reviewers can claim the next eligible group from the kiosk widget without manual selection, and the workspace opens a guided drawer with auto-advance between member tasks and a completion summary.
- Slide-over detail panel — Clicking a row opens a side panel that manages status, assignee, member tasks, history, and delete in one place. The Tasks grid now shows a clickable group chip on grouped rows so you can jump straight to the panel.
- Permission-gated — The Groups tab and Take-Next kiosk action appear only when the viewer’s role permits them.
- New Activity is the primary CTA — Workflow org-home and project-home Activities tabs now lead with New Activity (replacing the old “New Task” button). Project Home adds a split-button so you can start an Activity or kick off a Job Run from the same control.
- Two-step Activity wizard — Pick a project, pick an activity plan, then fill in title, description, priority, and documents on the same form. Document upload is inline; AI naming proposes a title from document content when enabled.
- Activity Plan editor — Visual editor with tabs, schema-driven properties panel, manual layout with persisted positions, slug shown under each step’s name, action-qualified edges, badges auto-generated from module refs, and per-step Cancel and Reprocess actions. Plans get a delete action consistent with task templates.
- Document Families on Activities — Activities own their document families directly. The new
GET /api/activities/{id}/steps/{stepId}/document-familiesendpoint returns the documents touched by a step. CREATE_TASK steps automatically copy the activity’s document families onto the task they materialize, so review surfaces always have the right context.PATCH /api/activities/{id}/steps/{stepId}is also available for step updates. - Activity steps in the API —
GET /api/activities/{id}now embeds the full step list in the response.
knowledgeglobal in script runtime — Activity-plan SCRIPT steps and routing scripts now expose aknowledgeobject scoped to the script’s permitted document families:knowledge.getFeatures(familyId)andknowledge.getItems(familyId)return the raw feature and item instances on a familyknowledge.featuresByType(familyId, featureTypeRef)andknowledge.itemsByType(familyId, itemTypeRef)filter by type- When the script operates on a single family,
knowledge.features/knowledge.itemsreturn that family’s data directly - Features and items are enriched with their full type definitions and frozen, so scripts can read everything they need without separate lookups and can’t accidentally mutate the source data
- See the Scripting — Knowledge bindings guide for usage patterns.
- Knowledge Sets, Knowledge Items, Knowledge Item Types, Knowledge Feature Types, and Knowledge Features can now be created, updated, and referenced by
orgSlug+slug(oritemTypeRef/featureTypeRef) on every create and PUT path. Manifests no longer need internal UUIDs to round-trip. - The resource resolver supports a new
knowledge-item://URI scheme. /api/knowledge-features?query=now actually searches feature name, type name, and description (previously narrowed the result set incorrectly).
- Step dots on document family cards — Document family cards now show a row of dots indicating each activity step’s state for that document. Hover for the step name and status; click an activity to open it. The same display appears in document grids via the activity cell renderer.
- Per-document activity data is batch-fetched, so even large document lists open without per-row API churn.
- Workstreams tab removed — Project navigation no longer shows the legacy Workstreams tab.
- Resources panel — The Studio resources panel now surfaces task templates and activity plans alongside other project metadata.
- Org-level activities — Activities filter by
lifecycleStateinstead of the removedstatusfield; the old org-home Activities tab variant has been retired in favor of the unified grid.
- Feature palette includes a search box and caps at 30 visible items at a time, so palettes with many feature types stay usable.
- The Applications tab shows a document family + content object inspector so you can drill into the source content behind a knowledge feature.
- Knowledge Features get a reworked card layout and detail overview, with feature properties and
extendedPropertiesexposed to AI naming templates.
- Clearing a SELECT now nulls the value — Clearing a selection-type attribute properly clears its underlying value (not just the display string), so dependent formulas, conditional formats, and validators react correctly.
- “Edited Value” indicator on clears — When you clear an AI-extracted value, the form now marks the cell as user-edited so the next extraction pass won’t silently overwrite it.
- Better grid editing feedback — Invalid Number cells show a focus ring while focused; grid Add no longer freezes the row gate; autocomplete dropdowns match by substring (not just prefix); SELECT popovers stay open across ag-grid cell destroy/recreate.
- Formulas re-evaluate when the data-object cache refreshes, so derived values follow upstream edits without a manual refresh.
- Faster project load — Project resources are now fetched in bulk with deduplicated module wiring, and a no-op assistant-connections endpoint has been removed from the load path.
- Channel-scoped workspace blob store keeps chat attachments and drafts per channel.
- Conversations continue across sessions via SessionStore continuation, so picking a chat back up doesn’t reset context.
- Task-scoped chats now see the task’s document store refs.
- Module refs can use a
{org}placeholder so plans are portable across organizations.
- Activity lifecycle now lives on
Activity.lifecycleState(DRAFT,RUNNING,PAUSED,COMPLETED,CANCELLED,FAILED). The previousActivity.Statusfield has been retired; existing data is migrated automatically. Update integrations that read the old field. - Activity step
kindis nowtypeacross the database, API, and UI (EXECUTION,SCRIPT,BRIDGE_CALL,CREATE_TASK,APPROVAL,LLM,AGENT). Existing rows are migrated; new activity-plan YAML should usetype. /api/planshas been retired now that the UI runs entirely on/api/activities. The OpenAPI spec has been regenerated with 20 previously-undocumented routes added — point external integrations at/api/activitiesand refresh generated SDKs.
PlatformBreakingAPI
Assistant connections removed
Breaking change. Assistant connections and the connection-driven event router in the orchestrator have been removed across the platform./api/assistant-connectionsREST endpoints (GET, POST, PUT, DELETE) have been removed. Clients calling these endpoints will receive 404.- The
AssistantConnectionandProjectAssistantConnectiontypes are gone from the Python SDK and the generated TypeScript models. - Domain events (document family, channel, batch, content) are still emitted by producers, but the orchestrator no longer routes them to assistants via connections — those events are now drained from the SQS queue without action.
- Activity-related events (
PLAN_CREATED,TASK_UPDATED,REPROCESS) continue to flow through the orchestrator’s plan advancer, trigger evaluator, and reprocess handler unchanged. - The
kdxa_assistant_connectionsdatabase table is retained but emptied by migration; nothing reads or writes it. - In the CLI,
kodexa-cli pullno longer writesassistant-connections/directories, andkodexa-cli applywarns and ignores any legacy on-diskassistant-connections/content. - The Studio data-flow editor has been removed; without connections the editor had no edges to render.
task_status_changed; broader trigger-based routing will follow in a subsequent release.PlatformActivitiesData DefinitionsData FormsCLI
Activity-centered workflows, reactive validation, and richer operations tooling
This release continues Kodexa’s move to an Activity-centered model for document-heavy business processes. Activities represent the business process run. Tasks represent the human review, correction, approval, or exception work that happens inside that run.New Features and Improvements:- Activities as first-class workflow runs — Activity Plans, Activity runs, and Activity steps are now the primary model for orchestrating automated work, human review, integrations, and audit history. Activity detail APIs now include step data so user interfaces and integrations can show the run and its materialized work together.
- First-class Service Bridge steps — Activity Plans can call configured Service Bridges as workflow steps. Request method, URL, body, response body, and result details are captured with the step so teams can review and troubleshoot external system calls without leaving the workflow.
- Activity authoring improvements — The flow editor now supports manual layout, persisted node positions, connection labels, context menus, compact step palettes, Service Bridge nodes, schema-driven configuration, and project/intake bindings.
- Per-document execution visibility — Execution details now surface at the document-family and step level, including active or failed steps, logs, execution IDs, and error details for faster operational review.
- Reactive Data Definition validation — Changes to data definitions, selection options, conditional formats, validation rules, and document data now trigger scoped revalidation. Matching exceptions are created, closed, or reopened as the document moves in and out of compliance.
- Richer exception review in Data Forms — Review surfaces now show more complete exception detail, filter open exceptions consistently, support override metadata and support article references, and can scope actions to specific exception paths.
- Knowledge snapshot review — Knowledge Sets now include snapshot panels, feature chips, and visual diffs so teams can review knowledge changes before and after updates.
- CLI and GitOps improvements —
kdx syncnow preserves project-resource links more reliably, records sync state for task templates, annotates manifest project keys with readable names, sorts legacy associations deterministically, and supports pre-packagemetadata.buildhooks. A newkdx secretcommand adds organization secret management from the CLI. - Document handling improvements — Preprocessing can correct document rotation and summarize documents. The document viewer now handles processed PDFs and rotated spatial overlays more consistently.
- Operational observability — Platform errors, upload failures, subscription failures, and Activity/execution status changes now emit sanitized structured events for better monitoring without exposing sensitive request data.
metadata.moduleRuntimeParameters. Update any module YAML still using modelRuntimeParameters.PlatformScriptingCLIBreaking
Script API consolidation, document-resident taxonomies, and shared script helpers
Three related changes ship together. The first is breaking; the rest are additive and unlock smaller, more maintainable scripts.1. Script API consolidation (breaking change)The JavaScript API for intake scripts, planner script steps, taxonomy event subscriptions (browser and Python contexts), and module scripts has been modernized onto a single canonical surface. Scripts written against the legacy API need updating.- Method names are now camelCase.
currentObject.GetFirstAttributeValue("foo")→currentObject.getFirstAttributeValue("foo"). Same rule for every method oncurrentObject,document, attributes, and content nodes. Scripts using PascalCase fail withTypeError: Object has no member 'GetFirstAttributeValue'. bridge.data.*is removed.bridge.data.setAttribute(currentObject.GetID(), name, value)collapses tocurrentObject.setAttribute(name, value).bridge.data.getAttribute(...)collapses tocurrentObject.getFirstAttributeValue(name). Calling the removed surface throwsbridge.data is undefined.log()is now structured. Replacelog("info", "msg: " + x)withlog.info("msg:", x).log.warn,log.error, andlog.debugfollow the same variadic shape (args joined with spaces, likeconsole.log). Callinglog()positionally throwslog is not a function.getType()on content nodes is renamed togetNodeType(). Scripts iterating selector results and reading the node type need a one-word find/replace.serviceBridge.list()is removed. Discovery now lives in the platform admin surface; scripts reference bridges by known"orgSlug/bridgeSlug"refs.
doc.getOrCreate(path)/obj.getOrCreateChild(path)— find-or-create idempotent. Replaces thefindFirst → if null createpattern.obj.setAttribute(name, value)— find-or-create on an attribute and write a typed value in one call.obj.payload({ key: "attrName", ... })— extract a JS object suitable forserviceBridge.callpayloads. Missing attributes default to"".taxon.optionLabel("taxonName", value)— look up the human label for a selection-option value. Replaces hardcoded label maps in event scripts.- Default
pathandownerUri— write methods (addAttribute,copyAttributeFrom, etc.) derivepathfromparent.path + "/" + tagandownerUrifrom the runtime’s script context. Specify only when overriding.
setAttribute(name, value) now resolves the attribute’s type from the document’s cached taxonomies — scripts no longer need to declare type: "SELECTION" (or any other type) on writes. The resolution chain is: VM-supplied resolver → document’s cached taxonomies (loaded by extraction) → fallback inferred from the JS value’s runtime type."orgSlug/moduleSlug" form. Module declarations (loaded as functions/vars) land on the global scope and behave like any other helper in the main script.4. CLI: metadata.scriptPath for module YAMLModule YAML can now reference an external .js file via metadata.scriptPath; kdx sync push reads the file and inlines it into metadata.script at deploy time:contents: block — kdx skips the implementation zip-and-upload path entirely and logs 📜 Module {slug} is inline-script only — skipping implementation upload.Existing modules with inline metadata.script or a populated contents: block are unaffected.PlatformCLISDK
Platform Updates — Mid April 2026
New Features:- Markdown Image Paste — Markdown editors now support Cmd/Ctrl+V image pasting and drag-and-drop. When editing knowledge items, pasted images are automatically uploaded as knowledge set attachments and referenced via portable
attachment://URLs. In standalone contexts, images are base64-encoded inline. - Selection Option Formulas — Data definitions now support formula-driven selection options with a formula mode toggle and extended fields, enabling dynamic dropdown values computed from other attribute values in the document.
- Exception Override — Data forms now support overriding validation exceptions directly from the workspace with WASM persistence and a form-scoped exception details panel.
- Tab Key Grid Navigation — Tab key now navigates between input fields in grid cells for faster data entry.
- Filterable Knowledge Tables — Markdown tables rendered in knowledge sections now include a search bar for filtering rows.
- Service Bridge Status Override —
postReplyScriptcan now override the HTTP response status code returned by a service bridge endpoint. - Intake and Label URI Schemes — The API resolver now supports
intake://andlabel://URI schemes for resource resolution.
- YAML Round-Trip Preservation —
kdx sync pullnow preserves YAML comments and formatting using a newyamlpatchengine. Pushed resources include the original YAML source for lossless round-trips. - Smart Discover Merge —
kdx sync pull --discoverintelligently merges newly discovered resources into existing YAML files, preserving comments and manual edits. - Attachment Download —
kdx sync pullnow downloads knowledge set attachments alongside metadata. - Discover Directory Flag — New
--discover-dirflag sets themetadata_dirin the generated manifest during discovery. - Conflict Detection — Sync state tracking detects when remote resources have changed since the last pull, with a
--forceflag to override conflicts. - Cross-Org Push —
kdx sync pushrewrites organization slug references in YAML values when pushing to a different organization. - Dependency-Aware Push — Resources are pushed in dependency order to avoid reference errors during deployment.
- Parallel Pull/Push — Sync operations now run in parallel for faster execution.
- Legacy Server Compatibility — Improved compatibility with older Kodexa servers including paginated API responses, case-insensitive slug matching, and fallback resource fetching.
- Optimistic Locking — Tasks, document families, and batch updates now use change-sequence-based optimistic locking to prevent concurrent modification conflicts.
- Service Bridge Observability — Service bridge proxy calls now emit Datadog events with request and response body details.
- Selection Formula Scoping — Selection formula evaluation is now scoped to ancestor data objects for more predictable results.
- Auto-Select Single Option — Dropdown fields with a single available option are automatically selected when the field is empty.
- Attribute Editor Consistency — Attribute editors now emit updates on blur rather than on every keystroke, reducing unnecessary saves.
- Readonly Field Styling — Readonly form fields are now visually distinguished with a border and muted background.
- WASM Binary Size — The WASM binary has been reduced by 20.8% by removing unused expression engine dependencies.
- Detached Sidecar Toolbar — The detached sidecar window now includes the full document toolbar and page navigation.
- Fixed service bridge calls not re-firing when dependency values changed.
- Fixed selection options formula toggle not responding to clicks.
- Fixed missing formula attribute references causing errors instead of resolving to nil.
- Fixed document store table view checkbox selection and row click behavior.
- Fixed chat session loading failing when reopening an existing conversation.
- Fixed LLM JSON response preprocessing to handle truncated or malformed responses.
- Fixed task locking and auto-lock behavior in plan advancement.
- Fixed form freeze, dropdown UX glitches, and validation timing issues in data forms.
- Fixed inline grid editing focus loss when attribute data updates arrived.
- Fixed extracted selection values being accepted even when not in the dropdown options list.
- Fixed GoJA script runtime not persisting
AddChild,SetTaxonomy,SetPath, and attribute mutations to the document.
PlatformCLISDK
Platform Updates — Early April 2026
Improvements:- Scoped Document Reprocessing —
POST /api/document-families/{id}/reprocessnow accepts an optionalassistantIdsrequest body. When omitted, the platform auto-detects prior assistant contributions and reprocesses the family asynchronously. - Document Family Feature Filters — Document families can now be filtered through attached knowledge-feature relationships such as
features.id=='...'andfeatures.slug=='...'. - Module Package Selection — Python runtimes now honor
metadata.moduleRuntimeParameters.modulewhen a module archive contains multiple packages, ensuring the intended package is imported before execution. - Completion Event Chaining — Applying an execution’s
completeLabelnow emits a follow-upCONTENT_CREATEDevent so downstream subscriptions can react to the finalized content.
Platform
Platform Updates — Late March 2026
New Features:- Intake API Tokens — Intakes now support scoped API tokens for machine-to-machine authentication. Create tokens on the new API Tokens tab; each token authenticates directly against a specific intake endpoint without requiring user credentials. Tokens are hashed at rest with SHA-256.
- Detachable Sidecar — Pop out the sidecar document viewer into a separate browser window using the detach button. The inline sidecar collapses while the external window is open and automatically restores when the external tab is closed.
- Smart Grid Filters — All grid views (tasks, projects, document families, etc.) now include a unified search bar with recent query history, structured filter mode with metadata-aware autocomplete, and AI-powered natural language filter generation.
- Script Step Log Viewer — Script step logs are now captured in CloudWatch and viewable directly from Activity step details. Each script execution automatically records start/end entries and all
log()calls. See Script Steps. - Knowledge Expression Trees — Knowledge sets now support expression-based feature matching using AND, OR, and NOT operators, replacing the previous clause-based system. This enables more flexible conditional logic for knowledge assessment. See Knowledge System.
- UNO Document Converter — New module runtime for converting Office documents (Word, Excel, PowerPoint) via LibreOffice/UNO. Available as
kodexa/uno-runtime. See Module Runtimes. - LLM Model Manager — New Python SDK
ModelManagerclass provides unified access to all LLM models through the Kodexa AI Gateway. Supports text completion, function calling, streaming, thinking mode, and multimodal input. Replaces direct provider SDKs with a single gateway client. See LLM & Model Manager. - AI Grid Extraction — Data forms using
v2:gridlayout now support AI-powered grid extraction with word-level node tagging for more accurate table data capture. See Data Forms Extraction. - Direct Extract — New
allowDirectExtractoption on data form attribute editors lets users copy values directly from the document text without an AI call. See Data Forms Extraction.
- CLI Re-Authentication — The CLI now automatically prompts for re-authentication when it encounters a 401 response, instead of failing. See CLI Authentication.
- Access Token Security — API access tokens are now hashed at rest using SHA-256. The profile access tokens UI has been redesigned with confirmation dialogs before deletion and tokens scoped to the current user.
- Module Ref Rename — The bridge script parameters
model_storeandmodel_optionshave been renamed tomodule_refandmodule_optionsfor consistency. See Module Runtimes. - Datadog Observability — New instrumentation events for agent runtimes, agent instances, and LLM calls in the AI Gateway for Datadog monitoring.
- Execution Cancel — The execution cancel button in the UI is now wired to the API endpoint.
- Taxonomy → Data Definition — The UI label “Taxonomy” has been renamed to “Data Definition” across resource badges and labels for consistency with the platform terminology.
Platform
Platform Updates — March 2026
New Features:- Agentic Assistants — A new assistant role that delegates processing to AI agents. Configure an agent runtime, module references, and a natural language prompt to let the agent autonomously decide how to process documents. See Assistants.
- Intake Enhancements — Intakes now support JavaScript scripting for file validation and metadata enrichment, task template integration for auto-creating tasks on upload, multi-file uploads, knowledge feature assignment, and processing metadata.
- Activity Workflow Enhancements — New SCRIPT and AGENT step types. SCRIPT steps run inline JavaScript for conditional routing. AGENT steps spawn AI agents in workflow execution. Action-qualified dependencies enable conditional branching (e.g., proceed only on “Approve”). Activity runs display as interactive DAG flow visualizations with automatic deadlock detection.
- AI Task Naming — Task templates can configure LLM-powered naming so tasks receive descriptive titles based on document content.
- Notification Sounds — Toggle audio notifications for error/warning toasts and new channel messages from your profile preferences.
- Document Native Download — New
GET /api/document-families/{id}/nativeendpoint returns the original uploaded file. See Download original native file. - Execution Log Viewer — Restyled with syntax highlighting, auto-scroll, copy and download buttons, and dark theme enforcement.
- Progress Toasts — Consolidated progress notifications for document uploads and batch reprocessing operations.
- Secrets API — Secrets are now managed through organization-scoped endpoints (
/api/organizations/{orgId}/secrets) with secure encrypted storage. - Resource Resolver — Three new project-scoped schemes:
task-status,task-template, andassistant. See Components and Structure. - CLI Resource Resolution —
kdx applynow resolves project-scoped resources (task statuses, task templates, assistants) with thescheme://org/project/slugURI format. - AI Gateway — Extended model metadata with pricing, description, and classification fields. Cloud models now proxy through the AI gateway.
- Store Reprocessing — Reprocess documents within a store with assistant selection and filtering.
- Document Groups — New
hardMaxPagesfield to enforce a hard page count limit at upload time.
CLIPre-release
KDX CLI v2026.3.0 (Pre-release)
This version is currently available as a pre-release. Install via
brew install kodexa-ai/tap/kdx-dev to try it out.- Apply Ordering Fix: Fixed an issue where
kdx applyfor modules would overwrite metadata changes. Implementation uploads now happen before metadata PUT, ensuring inference options and other metadata updates are preserved. - OpenAPI Resource Discovery: Manual resource definitions now properly override OpenAPI-discovered resources, preventing bogus CRUD paths from being generated for document-stores and other hyphenated resource types.
- Hyphenated Resource Types: Fixed resource discovery for
data-store,data-definition, anddocument-storeby normalizing hyphens and underscores in resource lookup. - Deploy Failure Reporting: Deployment failures are now properly surfaced with error counts and non-zero exit codes, instead of silently reporting success.
- ID Stripping on Create: The CLI now strips
idand_idfields from CREATE payloads since IDs are server-generated, preventing conflicts during resource creation.
- E2E Test Suite: Added comprehensive end-to-end tests covering document family reprocessing, knowledge-set CRUD and resolution, module upload/download, and CLI-to-API integration.
- Filter Syntax: Updated to SpringFilter DSL syntax for resource filtering, with syntax reference added to
kdx get --help. - Sort Parameters: Standardized sort parameter format across all commands.
- Module metadata (inference options, configuration) is now reliably preserved during
kdx applyoperations kdx sync deploynow correctly reports failures and returns non-zero exit codes for CI/CD pipelines- Resource operations for hyphenated types (data-store, document-store, data-definition) work reliably
brew install kodexa-ai/tap/kdx-devCLI
KDX CLI v2026.2.1
Bug fixes for project-scoped resource syncing and OpenAPI spec parsing:Bug Fixes:- Project-Scoped Resource Sync: Fixed an issue where syncing project-scoped resources (e.g., task templates) would fail with “TaskTemplate must have a project” errors. The PUT payload now correctly includes the project reference for project-scoped resources, mirroring the existing pattern for organization-scoped resources.
- OpenAPI Schema Parsing: The CLI now gracefully handles missing
$refschema references in the server’s OpenAPI specification. When broken references are encountered (e.g., a missingValidationFailedResponse), the CLI patches in empty object stubs and retries parsing instead of failing.
- Task template and other project-scoped resource syncing now works correctly with
kdx sync pushandkdx sync deploy - CLI operations no longer fail when the platform’s OpenAPI spec contains missing schema definitions
brew upgrade kdxCLI
KDX CLI v2026.2.0
Document Command Overhaul, Knowledge Management & Module Downloads:Version Scheme Change: The CLI version now aligns with the platform release cycle (2026.2.x), replacing the previous 0.x/8.x numbering.Document Command Rewrite: Allkdx document commands have been rewritten to use the native kodexa-document Go library via a new DocumentAdapter, providing significantly improved performance and richer output including node IDs and type information.New Document Subcommands:kdx document stats- Document statistics summarykdx document schema- Display document schemakdx document tags- List and inspect tagskdx document features- List and inspect featureskdx document node- Inspect individual nodeskdx document text- Extract text contentkdx document page- Page-level operationskdx document find- Multi-criteria search across nodeskdx document spatial find- Spatial search by coordinateskdx document spatial bbox- Bounding box querieskdx document data objects- List data objectskdx document data attributes- List data attributeskdx document data exceptions- List data exceptionskdx document audit- View audit trail entries
kdx knowledge attach- Attach files to knowledge setskdx knowledge download- Download knowledge set itemsattachmentPathsupport inkdx syncfor knowledge items
kdx get module <slug> --download- Download module implementation packages directly
- Project-scoped resources with auto-pull for task templates
- Graceful handling of missing files in pull operations
brew upgrade kdxCLI
KDX CLI v0.6.0
Content Object Access & Extended Store Commands:New Features:-
Document Family Content Commands: New
kdx document-family contentsubcommand for direct access to content objects (kddb files) within document families:kdx document-family content list- List all content objects with IDs, timestamps, and labelskdx document-family content download- Download kddb files directly, bypassing DFM export timeouts--latestflag - Automatically select the most recent content object--outputflag - Specify custom output filename
-
Store Upload & Watch Commands: New commands for document upload and processing workflows:
kdx store upload- Upload files (PDF, images, documents) to document storeskdx store watch- Monitor document processing progress with real-time status updates--labelflag - Wait for specific processing labels (PREPARED, FIRST-PASS, LABELED, PROCESSED)--timeoutflag - Configure wait timeout for long-running processing
- Project Create Organization Lookup: Fixed organization resolution when creating projects from templates
- Dynamic API Flags: Improved handling of dynamic flags for
kdx runoperations - Document Family Data Export: Fixed data export to always use latest content object
brew upgrade kdxCLISDKDocumentation
v8 Documentation Preview
Pre-release documentation for upcoming v8 CLI and SDK releases:CLI v8 - Document Commands (Preview):Newkdx document command suite for working with local KDDB files without requiring a platform connection:kdx document info- Display document summary with metadata and statisticskdx document print- Pretty print document structure as an ASCII tree with depth limiting and feature displaykdx document select- Query nodes by type using selector syntaxkdx document natives list/extract- List and extract embedded files (PDFs, images, etc.)kdx document external list/get/set/delete- Manage key-value external data storekdx document metadata get/set- View and modify document metadata
- Native Documents: Store and retrieve binary files within KDDB documents
- External Data: Flexible key-value store for custom data and processing results
- Metadata: Document properties including UUID, version, and custom fields
- Content Nodes: Hierarchical document structure with types and content
- Selectors: XPath-like query syntax for finding nodes
These features are in preview and will be included in the upcoming v8 release of the KDX CLI and Kodexa Document SDKs.
SDKDocumentation
Kodexa Document SDK v8.0.0
New SDK Documentation & Version 8 Libraries:New Documentation:- SDK Documentation Tab: Added a dedicated SDK section to the developer portal with comprehensive guides for Python and TypeScript
- Python Getting Started: Complete guide covering installation, document creation, node manipulation, selectors, features, tags, and saving
- TypeScript Getting Started: Full guide including WASM initialization, async patterns, memory management, and browser/Node.js setup
- High-performance document processing via Go backend with CFFI bindings
- ~100x faster in-memory mode for processing pipelines
- Full support for KDDB format, hierarchical nodes, features, tags, and XPath-like selectors
- Context manager support for automatic resource cleanup
- WebAssembly-powered SDK for Node.js and modern browsers
- ~5x faster than pure JavaScript implementations
- Full async API with TypeScript type safety
- Works with both file-based and in-browser SQLite
- Create, load, and save KDDB documents
- Hierarchical ContentNode tree structure
- XPath-like selector queries
- Features (key-value metadata) and Tags (annotations with confidence)
- JSON and binary export formats
CLI
KDX CLI v0.5.2
Stability & User Experience Improvements:Bug Fixes:- Increased Client Timeout: Extended HTTP client timeout from 60 seconds to 10 minutes (600s) to prevent premature failures during long-running deployment operations, particularly beneficial for large manifest deployments, multiple resource/module deployments, and deployments to slow or distant environments
- Improved Branch Mapping Error Handling: Changed behavior when no branch mapping is found - now displays informational message and exits gracefully instead of returning error, providing better UX when working on unmapped branches
- Better CI/CD Integration: Non-disruptive behavior when working on unmapped branches doesn’t fail pipelines unnecessarily
- Clearer User Feedback: Informational messages clearly distinguish between configuration issues and actual errors
- Enhanced Reliability: Deployments that previously failed due to timeout will now complete successfully
- Prevents deployment timeouts for operations with large manifests or multiple resources
- Better user experience when working with selective branch mapping configurations
- More reliable long-running deployment operations
GitHub Action
Kodexa Sync Action v2.2.0
Built-in Slack Notifications & GitHub Job Summary:New Features:- Slack Notifications: Send rich deployment summaries to Slack automatically with
slack-channel-idandslack-tokeninputs - GitHub Job Summary: Add deployment summary to workflow run with
annotate-summary: true - Renamed Input:
workers→threadsto match kdx-cli flag naming
- 🚀 Deployment status (or 🔍 for dry runs)
- Repository and branch information
- Resource counts (created, updated, unchanged)
- Direct link to the GitHub Actions run
CLI
KDX CLI v0.5.0
Tag-Based Deployments & Enhanced GitOps:Major Features:- Tag-Based Deployment Mappings: Deploy using git tags in addition to branches, enabling release-driven workflows with
tag_mappingsconfiguration supporting semantic versions, release candidates, and preview tags - Manual Deployment Overrides: New
--branchand--tagflags provide explicit control over deployment routing without requiring git operations, perfect for CI/CD, testing, and rollback scenarios - JSON Deployment Reports: Generate structured JSON reports of deployment actions with
--json-report <path>for CI/CD integration - Parallel Resource Deployment: New
--threads <n>flag for configuring parallel threads during resource deployment - significantly faster for large deployments - Resource Filtering: Filter resources during deployment with
--filter <pattern>for selective deployments
- Enhanced Error Messages: Clear, actionable error messages with hints when mappings are not found
- Better User Feedback: Deployment mode indicators showing whether using branch detection, tag detection, or manual override
- Improved Mapping Resolution: Support for multiple overlapping mappings, enabling sophisticated multi-environment deployment strategies
CLI
KDX CLI v0.4.1
Enhanced Debugging, Error Handling & Sync Improvements:New Features:- Enhanced Client Debugging: Added detailed API request/response logging when debug mode is enabled, providing comprehensive information for troubleshooting
- Improved Module Syncing: Updated module syncing to build and display full slugs with organization prefixes, improving clarity in logs and progress reporting
- Enhanced Deployment Output: Deployment command now includes the environment URL in planned deployment messages, providing clearer context for users
- Alternate Extension Support: Added support for both
.yamland.ymlextensions when reading resource files, with improved error hints showing all attempted file paths
- Better Error Messages: Error messages now reference full slugs with organization prefixes, making it easier to identify and debug issues
- Improved Error Handling: Enhanced deployment error handling to capture and report errors without terminating the process immediately
- Robust Payload Handling: New utility function to safely extract string values from interface types
- Debug Mode Formatting: Conditionally display full response bodies based on debug mode for cleaner output in normal operation
- Fixed gofmt formatting in
resource_types_test.go
CLI
KDX CLI v0.3.0
Knowledge Sets & Immutable Resources:New Features:- Knowledge Set Support: New
knowledgesetresource type with full CRUD operations, including example configurations for financial knowledge sets and full support in metadata API and sync operations - Immutable Resource Types: Added support for immutable resources (
featuretypeandfeatureinstance) that cannot be modified once created, maintaining database integrity. Sync operations automatically skip updates for immutable resources with appropriate warnings
