Skip to content

August 2026

  • PlaidCloud now hosts Plotly Dash apps, at parity with server-side Panel apps. Publish a Dash app from a Git repository the same way you publish a server Panel app: PlaidCloud builds your code into a container that idles at zero replicas, wakes on the next request, and rebuilds automatically on every push to the connected branch. Each viewer signs in with the platform’s single sign-on, and every read your app makes runs as that viewer — the same per-user, row-level-secured data access a server Panel app gets, using Dash’s own helper API. My Panel Apps lists Dash apps alongside your Panel apps, with build status, logs, locking, and rebuild all working the same way. See Deploy a Dash App From PlaidCloud Git.

  • Execution containers — run a block of related steps as one sealed unit. On the Advanced workflow canvas, right-click a group and toggle Execution on: the group becomes a self-contained sub-workflow with its own entry and exit ports and behaves as a single node in the parent. Nothing inside starts until the group’s inputs arrive and nothing downstream starts until every member finishes, while independent branches inside still run in parallel; disabling a container skips it — and everything downstream of it — as a unit.

    Run and monitor it as a unit. Run Group and Run Group + Downstream rerun the container in place without rebuilding its upstream, the container carries a rolled-up status — a dot, an N of M done count, and a colored border — and Retry from Failure re-runs only the failed members and what depends on them. The Run Monitor shows each step’s container and its rolled-up status alongside the canvas.

    Gate it, or iterate it, from Container Settings… Right-click and choose Container Settings… for two tabs. Conditions offers the same variable, table, document, and time checks a step’s Conditions tab does, deciding whether the container runs at all: they are checked once, when the container is reached — not per member and not per iteration — and a container that fails them is skipped as a unit, taking everything downstream with it. Unlike a step, a container has no “go to step” on a failed condition; a failed condition always means skip and move on, and a genuine error while evaluating one fails the run rather than silently skipping. A disabled container is skipped regardless of its conditions, and resuming a run that skipped a container this way leaves it skipped rather than re-checking. Tick Iterate this execution container to run the whole sealed group once per value in a set instead, using the same loop-variable model as the Workflow Loop step — an explicit list or range, or the unique combinations of columns in a table — so a member’s filter, expression, or output name changes each pass, up to 1,000 iterations. See Execution Containers and Container Conditions.

  • A conditional step now shows it on the canvas, and its conditions are one click away. A step’s run conditions were only visible by opening the step, so tracing a flow on the Advanced workflow canvas gave no indication which hop was gated. The connector leading into a conditional step is now dashed and carries a small shield at its arrowhead — the dash being what you read once you are zoomed out far enough that the shield is too small to pick out. Click the shield, or right-click the connector and choose Edit Conditions…, and the step opens with its Condition Checks already scrolled into view rather than on the Alias field with the checks off-screen. The shield on the step’s own tile still marks it as before, and a step with several incoming connectors gets one shield, not one per arrow. A step with no incoming connector, and one folded into a collapsed group, show only the tile shield — there is no arrow to mark in the first case, and in the second the arrow belongs to the group rather than the step. See Seeing Conditions on the Visual Canvas.

    Hover the shield to see what the gate would cost. The conditional step and everything downstream of it outline in a dashed highlight — the steps that would not run if that condition blocked — and clear again when you move away. Nothing changes on the workflow; steps upstream are never highlighted, because they run either way. It reads like the preview a disabled group’s chip already gives you, with the wording deliberately different: a disabled group is off, so its chip says how many steps will skip, while a condition is only evaluated when the run reaches it, so the shield says how many would not run if it blocks.

    The shield also reports the last run. A plain shield means the step is conditional and either hasn’t run yet or is running now; a shield with a + means it ran on the last run; a shield with a means it did not. The three differ by shape rather than colour, the tooltip says the state in words, and it all updates live while a workflow runs — so you can watch a run reach a gate instead of reloading the canvas, and answer “why didn’t this step run?” while tracing the flow. The is an observation, not a diagnosis: a step is also recorded as not run when it is disabled, when a step upstream of it failed, or when it fell outside a partial run’s scope — the toolbar’s View Logs names the reason for each step a run skipped. A step held at a breakpoint keeps the plain shield — pausing there was your decision, not an outcome of the run.

  • Copy and paste steps on the Advanced workflow canvas, links and groups included. Select steps on the canvas — or a whole group, which brings its steps with it — press Ctrl/Cmd+C, and paste them into any workflow, in this project or another, with Ctrl/Cmd+V. The connections among the copied steps come across, the group boxes come across, and the copies land where you point and stay selected so you can drag them into place. A link to a step you didn’t copy is dropped — there is nothing on the other end to attach it to — and the paste tells you when that has happened rather than dropping it silently. A large paste runs in batches, showing progress you can cancel, and never caps or silently drops steps; Ctrl/Cmd+Z right after a paste removes what it added.

    One clipboard, everywhere. The canvas, the current steps table, and the older steps table you reach by holding Ctrl (or Cmd) all share it, so it no longer matters which screen either workflow is open in — copying in one and pasting in another used to report There is nothing to paste on the clipboard for steps you had copied seconds earlier. The clipboard is held in your browser, so it survives a reload and is shared by every PlaidCloud tab you have open; switching workspace clears it, and it is never shared between browsers, machines, or people. Two smaller notes: Paste → Add Linked Steps (Advanced) declines a paste into a different project and points you at Append or Insert, since a linked step is a second placement of a step its own project owns; and Copy Selected with no rows selected says so rather than quietly emptying the clipboard. See Copy & Paste steps.

  • A project can now be copied into a project that already exists, not only into a brand-new one, and re-synced against the one it came from. The Duplicate Project action is now Copy Project, and its dialog asks where the copy should go: a new project, as before, or one you pick from the list. Copying into an existing project adds to it — the target keeps its own name, ID, data connection environment, storage defaults, document account, access type, access lists and row-access grants, and only the contents you selected are brought in. Where the copy carries an item the target already has, you choose whether the incoming version replaces it or lands alongside it with copy appended.

    This is for taking part of one project into another — a few workflows and the dimensions they use — without exporting an archive and importing it back. You do not have to work out what those workflows depend on: anything you tick brings its tables, dimensions, user-defined functions, sub-workflows and macros with it. A copy into a project you hold no role on, or one locked to prevent changes, is refused before anything is read or written, and a project cannot be copied into itself. See Copying a Project.

    Re-sync Into…, on a project’s actions menu, then promotes a later change forward without an export or an archive. PlaidCloud remembers which object came from which, so the comparison runs by that lineage rather than by name: an object an earlier copy renamed still lines up with its original, only what genuinely changed shows as a difference, and you merge the pieces you choose exactly as in an ordinary comparison. This is what lets one tenant hold Dev, QA and Production as separate projects and move changes forward between them. Re-sync a pair that was never connected by a copy and PlaidCloud says so, falling back to an ordinary comparison rather than pretending. See Re-syncing From the Project You Copied From.

  • Row Access — grant security groups the rows they may see, and apply it to your dashboards. Under Identity → Security Group, declare which column on a project governs row access and grant specific values of it to specific security groups. Every project and attribute always shows Granted, Unrestricted, or Denied, so a locked-out group is visible rather than looking unconfigured. See Managing Row Access.

    Apply to dashboards then takes a project’s grants and rewrites its dashboard restrictions to match. It matches rather than adds to: a grant you removed has its restriction removed, and a column you stopped governing has its restrictions taken away — so re-applying after a change is how you publish that change, and applying twice does no more than applying once. It needs the same access as editing the grants: Architect on that project, with the project unlocked. PlaidCloud tells you what it applied and what it deliberately left alone — a column drawing its values from a dimension or a typed list is reported as not yet applied rather than silently dropped, as is a table whose governed columns all let ungranted groups see everything.

    The first apply on a workspace needs a one-time approval, and there is no undo for it. It takes ownership of the dashboard roles PlaidCloud manages, and the dashboard service keeps no history of role membership to roll back to. So the first time you click Apply to dashboards, a confirmation appears describing it as a one-time, permanent, workspace-wide authorization; a workspace administrator confirms it once, and it records whoever is signed in, so it cannot be given on someone else’s behalf. Until it is given, applying a project is refused and nothing is written to your dashboards. (These actions are also available through the API for automation — POST /rest/v1/analyze/rls/reconcile, plus GET /rest/v1/analyze/rls/first-write-diff to preview the first-apply role changes and POST /rest/v1/analyze/rls/first-write-approval to record the approval.) See Applying Row Access to Dashboards.

    A query you write yourself is declined, and now says why. Row Access filters the table it is defined against, so a query the caller wrote has no single table to attach that filter to, and PlaidCloud declines to run it rather than return rows nothing has vetted. The message names the project, names the ways of reading the same data that do apply the grants — reading a table by name, and the column and dimension value readers — and says a project Architect can run the query as written, which an Architect is allowed to do because they can already grant themselves every value of every governed column. This covers exporting the results of your own SQL to a file or another database, and typing a row condition into a fan-out step’s “Test row 1” preview. Nothing changes on a project that has not turned Row Access on. See Row Access and Queries You Write Yourself.

    Importing a spreadsheet into a Row Access project now works. File imports — Excel, CSV and the like — used to be refused at the very first step, because the same safeguard that declines a query you write yourself was also declining the import’s own table-creation and data-load statements, even though an import reads nothing governed: it only writes the project’s own new table from the file you uploaded. An ordinary file import now runs cleanly on a governed project. The one thing still held back applies only on the interactive import screen: an import whose column mapping you supply yourself — a per-column expression, or a dynamic Excel mapping — is treated the same as free-form SQL, because such a mapping could read another table, and needs a project Architect. Imports run from a workflow are unaffected, since they run under the project’s own service identity. See Row Access and Queries You Write Yourself.

    Publishing a table governed by row access is declined. Row Access filters a table by restricting the reporting view it is published as, so a table has to be published for reporting before its columns are governed. Once a column governs row access, PlaidCloud declines to publish — or republish — that table: a published reporting view is read in full by anyone with reporting access, so an unfiltered view of a governed table would hand out every row and bypass the grants. A table that carries no governed column publishes as before, and one already published stays published. Publish the table first, then declare row access on it. See Publishing Tables.

  • Resume picks a workflow back up exactly where it stopped. A run that failed several levels deep — a workflow calling a workflow, or a loop step working through its iterations — used to restart the first called workflow from its opening step, repeating everything in between; on parallel and Advanced workflows Resume restarted the whole workflow, re-running every step that had already succeeded and every sub-workflow they called. Resume now returns to the step or node that failed, at whatever depth it sits, and re-runs only that and whatever was still waiting on it. A loop step resumes the interrupted iteration and skips the ones that completed, a run scoped with Run Selected keeps its selection, and a step that failed is re-run rather than mistaken for one that finished. Re-running a single step on its own no longer changes where Resume goes next. If you had been using Resume on a parallel workflow as a way to re-run everything, use Run All instead.

    Resume from top of chain handles the nested case: a workflow that ran as a child of another, invoked by a Run Model step, cannot continue its parent’s orchestration by resuming on its own, so this option resumes the top-level parent and continues the whole chain from where it stopped. The top of the chain is resolved within the same project — a chain that crosses into another project resumes from the top of the in-project portion. See Run a Workflow.

  • A PlaidLink agent can now be assigned to an environment, so switching a project’s environment switches its agents too. A connection already holds a different configuration per environment, so moving a project from your default environment to QA re-points every connection it uses in one action — but the agents that run its remote steps stayed put, and pointing a project at a different machine meant editing every step that named one. Each agent now carries an Agent Group, naming the agents that stand in for each other, and an Environment, saying which one it serves. A step still names a specific agent; the run picks whichever member of that agent’s group serves the project’s current environment. Set the two fields once per agent — there is no per-step change, and no table to keep in step across machines.

    Nothing changes until you set a group. An agent with no group serves every environment exactly as today — which is every agent you have until you change one — so a workspace running a single agent is unaffected. Setting a group makes the match exact for every agent in it: a project running in an environment none of the group serves stops with a message naming the group and the environment, rather than falling back to whichever agent the step happened to name. Running production work on a test machine is the failure this prevents, so add an agent for each environment your projects run in, and leave the group blank on an agent that should keep serving everything.

    The machine has to agree with the record. PlaidLink listens on one queue for the environment named in its own configuration file, so setting an agent’s Environment to QA in PlaidCloud while its machine is still configured for the default environment leaves it listening where no work is sent — the step waits and then reports that the agent is not picking up the job. Set the environment name in the environments block of that machine’s PlaidLink configuration at the same time, and restart the service. See Agent Environments.

  • Published Table Performance begins its first phase. Open Published Table Performance from a published table in Table Optimizer to see whether its current activity points to columns worth considering for a future layout improvement. Phase 1 passively monitors and recommends; it shows when no change is needed or more activity is required, while your team remains in control of every change. A future release is planned to add automatic optimization for supported tables. See Review Published Table Performance.

  • You can now see how far along a new workspace is, instead of waiting to find out — and its service links appear only once there is something to open. Creating a workspace takes several minutes, and the list used to show a status you could not act on: a workspace that had stalled part-way looked the same as one still working.

    The workspace list gains Created and Provisioning columns, and the workspace details dialog gains a Provisioning tab carrying the same information plus the service addresses currently published for that workspace. Provisioning reads provisioning, running, failed, paused, trashed, or deleting. A workspace sitting on the same phase for more than about half an hour has stalled, and one reading failed has stopped — now you can tell, with the phase to quote to support. (One stall is gone entirely: a workspace created without the optional backup feature could stop part-way through setup and sit on Activating forever. It now completes normally.)

    The Home, Dashboards, SQL and Logs & Metrics links follow the same readiness. They used to show on every workspace and were built from a fixed address, so a workspace hosted in a different data center got a link that went nowhere, and a workspace still being created got one that could never have worked. Each link is now the address PlaidCloud actually publishes for that workspace, and the icon is present only when the workspace is running and that service is switched on for it. A paused workspace shows none of them, and neither does one whose setup has not finished.

    One thing worth knowing: a workspace can look healthy in deployment tooling while still not being usable, because deployment finishing is not the same as the workspace finishing its own setup. What you see here reports the workspace’s own setup, which is the one that decides whether you can sign in. See Provisioning Status.

  • The step form now opens read-only when you cannot change steps, rather than letting you edit and failing at Save. If your role lets you open a workflow but not modify it, the form used to open fully editable — you could work through a step’s settings and only discover the problem when Save was refused, losing the edit. It now opens with its fields disabled and its write buttons hidden, so a step’s configuration is still there to read, but nothing invites an edit that cannot be kept. This applies wherever the form opens: the workflow table’s edit (pencil) icon, the Visual Canvas, the project Steps grid, the New Step menu, and after converting a step. Nothing changes for anyone who can already edit steps, and what the server accepts is unchanged — this closes the gap between what the screen offered and what it would accept. See Why a Workflow Might Not Be Visible.

  • The Visual Workflow Designer gets a folded step palette, panes you can collapse and resize, and a dark canvas.

    The step palette is organized into folders. Every step type used to sit in one long list you scrolled through; steps are now filed in ten folders that start closed, with your favorites still pinned above them. Folders are shaded by depth and draw a guide line down everything inside them — click a guide to close that folder without scrolling back to its heading — they open and close with the same +/− box the dimension screens use, the heading of the folder you are reading stays pinned to the top as you scroll, and Expand all folders / Collapse all folders sit in the palette heading. Filter… searches everything at once, open or closed, and lists each match under the folder it came from. Clicking a step adds it: it appears near the top left of the canvas, already selected and ready to drag, the row flashes so you can see the add landed, and Delete removes it if you picked the wrong one. Dragging from the palette still drops a step exactly where you let go.

    The whole palette is keyboard operable and navigable with a screen reader. Tab into the list and the arrow keys move through it, Left and Right close and open folders, Enter adds the step you are on, and Esc returns you to the filter box. Each row reports whether it is a folder, whether that folder is open, and how deep it sits. See The Step Palette.

    The palette and the Inspector fold out of the way, and both can be resized. Click the chevron in either header, double-click the divider beside either one, or press [ for the palette and ] for the Inspector, and the pane folds to a narrow rail with its title running down the side; the collapsed Inspector rail keeps showing the name of the selected step, and clicking anywhere on the rail opens the pane again. Either pane can also be dragged wider or narrower by its divider. Widths and collapsed states are remembered per browser and shared across workflows rather than saved per workflow — each pane separately, so a change to one does not disturb the other, and canvas windows already open keep their layout until you close and reopen them. See Collapse and Resize the Palette and Inspector Panes.

    The canvas follows the Dark theme. Working in Dark used to leave the canvas and everything on it — step tiles, connectors, notes, highlights, containers, the minimap, and the palette, Inspector and Data Preview drawer around it — drawn light against an otherwise dark application. They are now drawn for a dark background, with run-status colors and label text chosen to stay legible against it. Diagrams you export stay light whichever theme you use, because a dark diagram is right on screen and wrong on paper, so Export as PNG and Export as PDF read cleanly when printed or pasted into a document. See Dark Theme.

  • Dashboards drop their cached results the moment a table is republished, and cache for an hour by default. A dashboard caches each chart’s results so repeat views don’t re-query the warehouse, and those results were meant to be discarded as soon as a workflow run republished the underlying table — but the discard never took effect, so a chart could keep serving pre-refresh numbers until its cached result expired on its own. Republishing now clears them straight away, and the next person to open the dashboard sees the new data without forcing a refresh.

    Because of that, the cache no longer has to expire quickly to keep a dashboard current: the default lifetime is raised from five minutes to an hour, and you can set it higher still to match how often the data actually changes — in seconds on a database, under Advanced → Performance → Chart cache timeout, or on a single dataset under Settings → Cache timeout. On a table that reloads overnight, an hour or a day means a morning’s dashboard views are served from cache instead of re-querying the warehouse, which is where most of a slow dashboard’s time goes. One case is worth checking: if a table is written to by something outside PlaidCloud, nothing tells its dashboards to drop their cache, so the lifetime is all that refreshes them — those can now be up to an hour behind rather than five minutes, so set a shorter lifetime on those datasets. See Dashboard Caching.

  • The dashboard list now respects per-dashboard restrictions set in the dashboard service. A dashboard can be restricted there to particular roles. Where one has been, PlaidCloud now lists it only for people holding one of those roles, along with its owners and dashboard-service administrators — previously the restriction applied when you opened the dashboard but not to the list, so a restricted dashboard stayed visible to everyone who could reach its project. A dashboard with no such restriction is listed exactly as before, to exactly the same people, and no dashboard is restricted this way today, so nothing disappears from your list as a result of this change. One thing to know: your dashboard account has to exist before a restricted dashboard can reach you — until then restricted dashboards stay hidden while unrestricted ones are listed as normal, so open any dashboard once from PlaidCloud and the restricted ones you hold a role for appear. See Which Dashboards You See.

  • Dashboard accounts follow your PlaidCloud membership, and are managed from PlaidCloud. Inside the dashboard service, adding or editing a user account directly is now limited to administrators, and the screens for creating and editing pending registrations have been removed. Nothing changes about how people get to a dashboard: your dashboard account is set up for you the first time you open one from PlaidCloud, and what you can see there follows from your workspace membership and security groups. Accounts made directly in the dashboard service are not connected to your workspace membership and are easy to leave behind. See Managing Workspace Members.

    Three ways the two used to drift apart are fixed. Changing a member’s email address now updates their dashboard account too — the change saved in PlaidCloud but never carried across, and nothing reported an error, so anything looking a person up by email stopped finding them, leaving their reports account orphaned under an address they no longer used. Re-creating a member no longer resets their reports and dashboards access: removing a member has never deleted their dashboard account, so removing someone and adding them back — a rehire, a corrected email address, undoing a mistaken removal — used to reset that leftover account to the most basic level, discarding administrator access, elevated report-building roles, and any role controlling which rows a report shows them. And a member with two dashboard accounts differing only in capitalization can sign in again: sign-in matched both, could not tell which was meant, treated the person as a first-time arrival, and left the dashboard sign-in page flickering about once a second without ever getting in. Sign-in now settles on a single account — active first, then exact capitalization, then oldest — and two such accounts can no longer be created. If a member’s email was changed, or they were removed and re-added, before this release, the two records are still out of step and must be reconciled by hand; the capitalization case needs nothing cleaned up.

  • Excel exports are around twice as fast, hold their memory flat, and tell you up front when the file cannot be written. A large export previously held the whole result in memory while it built the file, so the biggest exports were also the ones most likely to run out of room and fail; the file is now built as the data is read, so the memory needed stays flat whatever the row count and a typical large export finishes in roughly half the time. The resulting workbook is unchanged, apart from one fix: a date column shows as a date again rather than as a number like 44355 — date columns were being written without their date formatting, so Excel displayed the underlying number. Columns holding both a date and a time were not affected; re-run the export to correct an existing file.

    An export too large for a worksheet now stops immediately and says why. A worksheet holds at most 1,048,576 rows and 16,384 columns, and a step asking for more used to spend the whole export producing a file Excel then refused to open. It now checks before it starts and names CSV and Parquet as the alternatives; very large exports that exceed what the file format allows are reported the same way. See Export to Excel.

  • AI agents connected over MCP read your workspace with far less of their capacity spent on the reading. Listing and lookup tools return the first 25 matches per page rather than 50, an agent can name the columns it wants with fields=[...] instead of taking the whole record, and where every row of a result carries the same fields it comes back column-oriented — the field names once rather than once per row, around 30% fewer characters on typical listing traffic. So a routine “list the tables in this project” costs a fraction of an agent’s limited reading capacity, leaving more room for your actual question. A page that was capped now says so explicitly, so an agent asks for the next page instead of treating the first 25 as the complete answer, and asking for a column that doesn’t exist returns a clear error naming the columns that do. Nothing is out of reach: raise limit, or page with the cursor. Connected agents read the column-oriented shape without any prompting from you and no value changes on the way; if you read these responses from your own code, handle both shapes. See Keeping Results Small and Column-Oriented Results.

    The AI assistant carries a leaner tool catalog too. The list of available tools it loads on every turn is about a third smaller with no loss of capability, and a batch of tool descriptions that told it the wrong thing — naming a tool that doesn’t exist, describing an optional field as required, quoting an error the platform never returns — has been corrected, so it wastes fewer turns on requests that were never going to work.

    A query an agent runs now returns at most 10,000 rows, and says when it stopped there. There was no limit before, and an agent asking for a very large result made everyone else on the same server wait while the answer was prepared to send — reading the rows was never the slow part, packaging them was, and that grew with the size of the result. Results are capped, the reply marks itself as cut short so the agent narrows its query rather than assuming it saw everything, and the packaging step is far cheaper at every size. This applies to raw SQL an agent runs and to query plans it asks for. Nothing changes for an ordinary request, which is comfortably under the cap; if you genuinely need more rows than that, an export is the right tool rather than an agent conversation. See Keeping Results Small.

    And one agent can no longer crowd everyone else out by asking for a great many things at once. An agent can bundle several requests into a single call to save round trips, and there was no limit on how many it could bundle, nor on how many of them ran at the same time — so one bundle could occupy the machinery every other person’s work queues behind. A bundle now holds at most 100 requests and runs a bounded number of them at a time; the rest wait their turn rather than being refused, so a legitimate bundle still completes without crowding out the workspace. A bundle over the limit is refused with a message saying so rather than quietly running part of it. Bundles can no longer be nested inside one another, which never saved anything a flat list does not, and the recipe that pages through a long listing now stops after a set number of pages and says it stopped early. See Batching Requests.

  • Allocation and cost-trace answers say more, claim less, and repeat. Asking an AI agent what drove a change in an allocated cost returns a far more usable answer, across every part of it. Nothing about your allocations, and almost no figure in these answers, has changed — what changed is what the answer states, what it admits it cannot state, and how consistently it does both. See Understanding the Answer, Honest Answers and Tracing Allocations.

    It states the figures behind the percentage. An answer leading on a single figure now gives the amounts it moved between — “net_contribution fell 23.5% (from -$12,917,012.01 to -$15,957,089.93)” — so a direction word can be read against the figures rather than on its own, which matters most where the value is negative and “fell” reads as a profit shrinking when it is a loss getting bigger. A useful side effect: consecutive questions about the same slice join up, one answer’s closing figure being the next one’s opening figure. An opening line reporting offsetting rises and falls carries the pair too — “the net barely moved (3.7%, from $85,472,090.25 to $88,668,890.26), but that hides large offsetting movements” — because those two figures are the pool’s rather than the net’s, so they make that line’s point more plainly rather than softening it: $3.2M of movement inside an $85.5M pool is visibly nothing, where “barely” alone was a judgement about a base the answer never showed. It still sends you on to a single member. An answer reporting no material change gains the same pair. Only a figure that is genuinely unavailable is left unstated — a question the agent declines to attribute, or a combined total that could not be read — where nothing is given rather than a placeholder.

    A table recombining several allocation branches states its combined total. It was the one answer opening with no money at all — “across 4 recombined branches, all 4 fell” — on the reasoning that a recombined table has no single total to state. It has one: it is a real table, and the agent reads its total straight off it, so the line now opens “the combined total fell 58.0% (from $34,621,164.20 to $14,535,705.89)” and the year-on-year comparison resolves for these answers as it does for every other shape — which on one model surfaced a reversal it could not previously state at all, this year’s fall being 89% the size of last year’s rise. The total is read from the table rather than by adding the branches up, because a branch reaching the recombination by more than one route is listed once and counted more than once in the table. Where the two do not agree the answer says so and names both — “the branch figures shown ($12,231,623.56) do not account for the combined change of $25,082,057.48” — and where the total genuinely cannot be read, the opening line reads as it did before and the answer states which condition stopped it. Each branch’s line also states the two figures its change moved between, and an answer covering a single branch reads “1 allocation branch” rather than “1 branches”.

    It names the driver behind its commonest explanation. “A slice’s share of the pool shifted” — of what, driven by what — is now “the shift in this slice’s share of the pool (split on the revenue driver; this slice’s pool share went 23.2% to 29.3%)”. That is the difference between a fact and a lever. The driver is named without figures where the share is not a sensible percentage, and a step with no driver has none to name.

    It grades confidence on the column you are reading, not just on average. The closing confidence line was an average across value columns weighted by how much each moved — and a summary column, being the difference between the others, is almost always the smallest mover and the one you act on. The weaker column is now named first: “medium for the net_contribution column (mostly clean); high overall across 4 columns”. A table recombining several allocation branches is graded the same way, where a branch’s own confidence previously appeared nowhere at all: the weaker branch is named — “medium for the Allocate Support Costs branch; high overall across 2 branches” — and its line in the per-branch breakdown carries its level too. A level marked against a column or a branch now always means weaker than the answer overall, never stronger: on a model whose overall level was medium the mark used to land on the largest faller, labelling it “high”, while the column the answer led on carried nothing. Where the columns or branches agree, the line now states how many it is about — see the next entry.

    It says what the confidence is of. That closing line named a level and, often, a reason, and said nothing about what either was about. Three things follow from that, all fixed. It now names the breakdown it rated — “Confidence in this breakdown by lob” — which matters most when you follow an answer’s own suggested next step: on a banking model the same figure cut by line of business rated medium and, cut by branch as that answer recommended, rated high. Both ratings were right, because cutting differently changes which members offset each other, but with neither line naming its cut the pair read as one number rated twice, the second reading as “we looked again and it’s fine”. It now states its scope where the columns agree — “medium across 3 columns” rather than a bare “medium”, which on a table with several value columns was a rating of no stated number of anything. And it now gives a reason only where every column gives the same one: on an automotive model the line read “medium (mixed residual)” where that residual belonged to cost, a column that had moved 0.2%, while the figure the answer led on was a 38.0% fall in margin — so a reader discounted the headline on the strength of a qualification that was not about it. The per-column reasons were always in Honest limits, each against its own column, and still are. Where a group of columns is both the weakest and shares a reason, that group is named. A single-column answer is unchanged.

    A confidence word inside the trace never outranks the answer’s own. The step-by-step account at the end of an answer stamped each step’s own level on its caveats, in the same vocabulary the answer uses for itself. So a recombined-branch answer opened “low (partial attribution)” and then said “Caveats (high confidence)” twice, eight lines further down, in the block where the figures are — one word, two scopes, nothing on the page saying so, and a reader scanning for the numbers left with the wrong one. A step now states a level only where it is weaker than the answer overall, which is the rule the per-column and per-branch marks already follow; otherwise the line reads “Caveats: …” and the caveat itself is untouched. Separately, where two things hold a confidence down, the line now names both: an answer held at medium by unevenly populated periods and by part of the upstream chain it could not attribute printed only the first, so a reader who skimmed to the label never saw the more serious half, though the answer’s own opening summary said both. It now reads “medium (uneven period coverage; partial attribution)”.

    A complete list of movers no longer calls itself the top of a longer one. A breakdown by a dimension was headed “Top movers”, which tells a reader there are more they cannot see — and usually there are not: where a dimension has a handful of members the list is all of them and the figures add up to the whole change. So a reader either hedged a conclusion that was in fact complete, or went looking for rows that do not exist. The heading reads “All movers” where the list is complete and stays “Top movers” where it genuinely was cut short — which the answer already said, in a note that appears only on lists that had been cut, so the complete case was marked by nothing at all. Where the answer was never told whether the list is complete, it stays “Top movers”: vague is the right answer to not knowing.

    A lowered confidence says what was less than clean. “Mostly clean” is a label, not a reason, and on an answer whose only other note is withheld it was the whole of what a reader got — confidence lowered, nothing to act on. It now names the overlap that caused it: “mostly clean — 16% of the change is interaction between the value coming in and the driver total”.

    The whole-pool warning appears only where there is no breakdown, and the pool-conservation note is separate from it. The warning telling you to scope to a member was still printing above breakdowns the answer had just produced — on a tax model, five lines above a four-entity breakdown. It now appears only where it is true; the columns and stages that genuinely have no breakdown of their own keep it, because for them it is the useful half. That warning also used to bundle a second statement — that the pool total always credits the incoming value rather than the driver, which is a property of how allocation conserves the pool and not a finding about any member — and dropping the pair together left “the value coming in explains 100% of the move” printed next to “account=LAE drove the move”, with nothing in the answer to settle which to believe. They are two separate notes now, so picking a breakdown drops only the one it disproves.

    A search that found nothing still names the member behind the figure it quotes. It read “the strongest candidate was branch, its largest member accounts for a share shift worth ~45% of that column’s change” without ever saying which member — the one place the answer withholds a name being the one place the finding is uncertain and you most need to judge it. It also left the answer’s own warning that a follow-up may lead with a different member impossible to check.

    A label describing how a figure was measured no longer reads as a cause. Where a member’s own change is larger than the whole net movement, the answer said it “drove the move by its own change” — words that named which of two measurements had been used, and which a reader took as the reason the money moved. On a tax model the answer’s own recommended next step said otherwise: 85% of that same member’s change came from its share of the cost pool shifting under it, which for an intercompany charge is the difference between an entity’s own conduct explaining the charge and the allocation basis moving beneath it. Both answers were right; only the first read as an explanation when it was a description. That line now states what the figures show — “its own change is larger than the whole net move” — and reads with, rather than against, the note beside it that other members partly offset. The wording for the other measurement is untouched and no figure changes.

    Its year-on-year note says how big this move was against last year’s, and what last year’s actually was. The note gave one word — normal, elevated, extreme — where a year of large rises and falls that nearly cancelled leaves a tiny net, and an ordinary year after it comes out as a very large multiple. It now reads “1325% the size of last year’s move in absolute terms, in the opposite direction; last year moved +$1,792,719.72”, says that the comparison is between amounts rather than rates, and states outright when the two periods moved in opposite directions — including where they are too far apart for a ratio to be worth quoting, in which case you get the direction, and last year’s move, without a multiple. Where there is no prior year at all, the answer says there is no baseline and now leaves the amount beside it empty, instead of filling in the whole of the current year — which read on one insurance model as last year having moved $380.9M against this year’s $1.79M, a collapse that never happened.

    It says which measurement each number is. The figure an answer opens with is a shift in share of the pool; the breakdown it suggests you run next reports each member’s own change. Both used to be described as a percentage “of delta”, so one model gave 71% and 98% for the same programme, same column, same period, one click apart. Each measurement is now named where it appears, including where the search found nothing and quoted only the closest field, and the suggested next step warns you to expect a different number for the same member. Neither figure changed — only what they are called.

    A “one member drove this” line now names its measurement too, so it can be read on its own. Naming the measurement used to happen only in the answer you started from, which is enough while you read that answer and its follow-up together and not enough for an answer reached on its own — a bookmark, a link someone sent you, a colleague pasting the text, or an assistant that ran only the second question and has no memory of the first. Such a line read “branch=DIGITAL = 63% of delta”, where the only clue to which of the two measurements it was were the words “of delta”. Both now say: “63% of the delta, its own change” against “71% of the move by its shift in share of the pool”. Four words on a line that appears throughout an answer, which is about 1% of a typical answer’s length — a separate note on every stage would have cost far more for the same sentence.

    And where it cannot sensibly print that percentage, it no longer promises a comparison you cannot make. When one member’s shift is larger than the whole net change — routine where large rises and falls cancel out — printing “1166%” would be absurd, so the answer described it in words. But the sentence after it still said the breakdown it suggested “will not match this figure”, and no figure had been printed: following the advice showed a list identical to the one already on screen, so a reader who checked the warning found nothing to check and learned to distrust these notes generally. The size is now given as a multiple — “by more than the whole net change in share-shift terms (11.7×)” — so there is something to compare against. Where even that cannot be stated, the sentence keeps its explanation of what the two measurements are and drops the promise that they will visibly differ.

    It says how much the chosen breakdown won by, and warns when the next step answers on a different column. Where you do not name a field, the agent picks one and says it is “the strongest cut among the dimensions searched” — without ever saying how much strongest, so the field it beat disappeared. On one insurance model the winner scored 11.66 and the runner-up 5.86: half as much, a genuine second story, and visible nowhere in the answer. It now reads “On that measure account scored about 2.0× the next candidate, line” — a comparison rather than a second percentage beside the first, since two percentages measured differently side by side is the confusion these notes exist to prevent. A search that could only score one field says so instead of referring to a runner-up that does not exist. Separately, on a table with several value columns the suggested breakdown answers on the column the answer leads with, which is not always the column the agent searched: one model found its story in cost, offered “the breakdown the auto-discovered cut is based on”, and returned a list about margin led by a different programme with a top figure an order of magnitude smaller. Both answers were right, and someone following the suggestion would reasonably conclude one of them was wrong. The suggestion now names both columns and says the leading member can differ too, not just the percentage. Where the answer covers a single column the two are the same and the wording is unchanged.

    It describes the table and column you asked about. A trace through several allocation stages printed the member breakdown from the deepest stage rather than the table you named — a headline reporting a $9.3M fall followed by movers adding to $16.2M. On a table built from several branches, the row-count note reporting how comparable your two periods are was taken from the largest branch alone, and an answer covering several allocations named only the first while counting each value column as its own branch. Branches are counted as branches now, and a branch whose own columns move in different directions is reported as having moved both ways rather than being assigned one. A result with several value columns now always opens on the same column, and every note about the automatic search names the column it ran against. All of these now describe the table in the headline, at the same scope, adding up to the headline figure.

    It declines instead of answering anyway. Naming a breakdown field the table doesn’t have is refused outright, listing the fields it does have, rather than returning a confident answer with the breakdown quietly missing; naming a period column it doesn’t have is refused the same way, rather than going ahead and reporting only that it couldn’t total the figures, which read as a passing glitch worth retrying when retrying could never help. Driver what-ifs are checked the same way, and you can still leave the period column unset and have it detected for you. Further up the chain the rule is deliberately different: a field added by an allocation only exists downstream of it, so where a field is genuinely absent from an earlier stage the trace groups by the fields that stage does have, telling you which it left out, rather than losing that stage’s breakdown. And where a breakdown is missing for any other reason, including a passing warehouse fault, the answer says so and offers a retry — the totals are measured and unaffected; it is the per-member detail that is absent, not wrong.

    A field that is blank on every row is reported as unfilled instead of as a change perfectly concentrated in one member, and where the best explanation of a change is its unfilled rows, the answer says how often that field carries a value at all — “cost_centre carries a value on 12% of the rows in the periods compared” — because that finding means something quite different on a field that is nearly always filled in. Very sparse and very complete fields read as fewer than 1% and more than 99% rather than as 0% or 100%, neither of which can be true in that case. A total that crossed zero names the sign change and drops the meaningless percentage. Where the check that bounds the automatic search fails, the answer says the search was not run — a limit on the search, not a finding about the data — rather than searching every field on the table in silence, and tells you to name the field you want examined, which works whatever the cause. Where a knock-on estimate could not be worked out for a step, that result carries no figure at all instead of being reported as receiving the whole change, and everything below it is left out of the totals and named as missing. A result measured from only part of what writes it says so and names the step that could not be read: the figure is a real measurement of what could be read, so it stands, but it is not the whole result and should not be read as a floor.

    Its confidence and its warnings hold up. A trace with unattributed stages in the middle is held to medium at best rather than reading “high — clean attribution” off the final stage. A warning raised by an earlier step in the chain reaches Honest limits rather than sitting as a fragment inside the trace. An answer that picked its own breakdown field now carries the offsetting-movement warning that the same question already carried when you named the field yourself. The note saying a list of movers was cut short travels with the list, so asking for a shorter answer no longer changes what it claims. A note raised by several of your value columns names every one it applies to — “Columns revenue, margin: …”, or “Column revenue (also raised by margin): …” where the figures shown belong to one of them — rather than opening with a single column’s name and leaving the others reading as exempt. A change whose pieces overshoot the total by a few pence is no longer described as partly offset by countervailing movements that were not there. And the check that confirms a reworded answer kept its warnings no longer passes a rewrite that dropped one because a column in your own model shared a word with it.

    It picks a breakdown field properly, and offers next steps you can actually run. The score deciding which field best explains a change was capped at 100% before candidates were compared, so on one insurance model three fields scoring 1166%, 586% and 246% all looked identical and the reader was asked to choose. The cap is gone: the field is named, with the movement behind it. Where fields genuinely are close, each is offered as its own suggestion; where nothing cleared the bar, the answer says it searched, says the change reads as a broad move rather than something concentrated, and offers the closest field it found. Where large rises and falls cancel out, the suggestion to narrow to one member now comes first and names the member whose own rise or fall is larger than the headline change — except where naming one would promise more than the answer measured, as when the movement sits in rows that have no value for the field, or is a shift in a member’s share of the pool rather than the member’s own rise or fall; there it still offers to narrow but names no member, and the breakdown stays first. An answer scoped to a single member no longer advises you to scope it to a single member. The two situations that used to offer no next step at all — a total that crossed zero, and a stage that could not be aggregated — now carry one, the second naming the table to check and the likeliest cause, a period column missing from that stage’s table. One case still offers none: where an independent read of the driver table disagrees with the realised split, the useful move is to inspect that driver table directly, which is a separate request rather than an adjustment to this one. An answer spanning several recombined branches now offers one too, naming a branch and the table to trace to follow it on its own — the agent does not trace back past the point where branches meet, so that is where it hands the thread over. The branch offered is the largest mover among those it can name and did attribute, and is not described as the largest, since the per-branch list already shows every branch’s move.

    It reads as words, not internal codes. Dominant: T_total, 100% now reads the change in the pool total explains 100% of the move, and the other factors as the value coming in, the driver total, the shift in this slice’s share of the pool, and the dimension assignment. A qualification that appeared as offsetting_negatives_ratio — usually the most important one, the one saying a half-percent headline hides large movements cancelling out — is in words in both the headline and the trace summary. Tables are named as your project knows them rather than as long analyzetable_ identifiers, in knock-on estimates and in the explanations given when an answer declines; where a name genuinely cannot be resolved the answer names the step that produced it, or leaves the reference out and still reads normally. A blank value reads as (no value) or (blank) rather than None. An answer can no longer give its own confidence two spellings at once — medium (mostly_clean) in the opening summary and medium (mostly clean) two lines below — and where it declines a whole-pool figure because an earlier allocation narrowed what this one can see, it says what it saw in plain words instead of quoting an internal signal name.

    The same question returns the same answer. Where two members moved by exactly the same amount their order was left to the warehouse, which promises none — so a repeated question could reorder them, or, on the boundary of how many movers are shown, swap one for the other, along with that member’s own before, after and change, and even which field the answer chose to break the change down by. Equal movers are now ordered by the value they were broken down by, so a difference between two runs is a difference in your data.

    Driver what-ifs answer correctly on more models. Asking what a change to a driver would do re-divides the pool under the new weights — but where more than one step writes into the same result table, it re-divided the other steps’ money too, overstating one real model’s $29,000 change as $162,000 with nothing to indicate a problem. Such a result now names every step writing that table and points you at the tool for explaining a change that has already happened, and the check printed alongside says plainly that it confirms the split adds up — not that the amount being split is the right one. A step that carries its driver value through under a different name can be asked about at all now: use the driver table’s own column name, while the members the answer reports are named as the result table spells them. And where the warehouse rejects one of the reads a reslice needs, the answer says which read failed and whether retrying is likely to help, instead of returning an internal error. See Driver What-Ifs.

    And a what-if names the same table every time when it points at the biggest single impact. Asking where a change would land returns the outputs it reaches plus a note naming the largest one — and where two came out equal, which one got named depended on the order the allocation steps happened to be stored in, so the same question could answer differently. Two equal outputs is the ordinary case rather than a rare tie: where two allocations draw on the same pool, each hands its output the same share, and a reader told to scrutinise one of them examined one of two identical exposures with nothing on the page to say the other existed. The named table is now stable, and the answer’s underlying data says how many share the top figure.

    Cost-tracing can search combinations of dimensions. Where your workspace has explanatory-cut discovery switched on, asking what drove a change now also tests combinations — a move confined to one region and one product line, say — rather than one dimension at a time; previously the combination search never ran at all unless it was requested explicitly, which nothing did. The answer stays conservative: where nothing explains the move on its own, it says so rather than picking something. Both searches remain off by default.

  • Busy workspaces do less work for the same result. A running workflow now asks PlaidCloud for far less of the same information while it works, which frees up capacity where many workflows run at once. And deleting tables, or reading a table’s dependencies, is much faster on a large project: before a table can be deleted PlaidCloud works out which workflow steps use it, and deleting a folder or several selected tables asked that question separately for every table against every step — so a folder of fifty tables on a project with hundreds of steps could sit for the best part of a second before the confirmation appeared. It is now answered for the whole selection in one pass, carrying only what is displayed. Which steps are reported as using a table, and everything you see, are unchanged.

  • Deleting a folder of dimensions no longer reports a failure once the folder holds more than about fifteen. The dimensions in a folder were deleted strictly one after another, so how long it took grew with how many there were, and a folder of around sixteen crossed the twenty seconds at which the screen stops waiting and shows an error. The delete had in fact worked, and carried on running after the error appeared — so the folder you had just been told had failed to delete was gone when you looked again, with nothing on screen to say which of the two had happened. A folder’s contents are now deleted several at a time, which brings a folder of that size comfortably inside the limit; the more it holds, the bigger the difference. Deleting a folder of tables has had the same change, having escaped the problem so far only by being quicker per table, as has the action that clears out tables no longer referenced by any step — that one covers a whole project rather than a single folder, so on a large project it had the furthest to fall behind. Several at a time rather than all at once is deliberate: everything in a project shares one index of what lives in which folder, and emptying a large folder all at once would contend on it. A delete that fails partway through still leaves the folder itself in place, as before.

  • A cost-trace answer about a figure that is negative at both ends no longer describes a deepening loss with a word that reads as an improvement. Asking why a line of business moved returned “net_contribution fell 23.5%, from -$12,917,012.01 to -$15,957,089.93” — a loss three million dollars bigger, announced with the one word a reader scanning a loss line takes to mean it got smaller. “Fell” is correct about the signed figure and backwards about the quantity most people have in mind, and this is the sentence that gets pasted into an email, so everything careful further down was being read against an opening line already taken the wrong way round. Where a figure is below zero at both ends the answer now describes the size of the balance instead — “went further below zero — the negative balance grew 23.5%” — with the same two figures after it, and the mirror case reads “moved back toward zero — the negative balance shrank”. No figure changes: on such a pair the percentage already shown is exactly the amount the balance grew by. The answer still does not say whether that is good or bad, because which way is good depends on the column. Figures that cross zero, and the ordinary case of a positive figure, are unchanged. See Analyzing Allocations with AI.

  • A cost-trace answer that warns its total hides large offsetting movements now states the test it actually applied. It explained itself with “the top mover alone exceeds the net delta” — a looser rule than the one the agent uses, which is whether the biggest single movement inside the change is at least a quarter larger than the change itself. A reader who took the stated rule at face value and checked it on the next answer found it apparently broken: an insurance model whose largest line moved $24,124,561.91 against a net of $23,755,719.72 — a 1.6% overshoot, correctly rated “high (clean attribution)” with no warning, because that is a mild and arithmetically ordinary overshoot rather than the ninefold churn the warning exists for. The wording now names the threshold and every measure it can be applied to — one member’s movement, that member’s shift in share of the pool, or one factor’s contribution — so the rule an answer states and the rule it applies are the same rule and can be checked against each other. No confidence rating, and no figure, changes. See Analyzing Allocations with AI.

  • Converting a large Alteryx workflow no longer holds up everyone else on the same server. Reading the file and working out what each Alteryx tool becomes is the slow part of an import, and it ran where nothing else could proceed until it finished — measured at four seconds on a 2,000-tool workflow, during which everyone else’s screens simply waited, with nothing to indicate that somebody’s import was the reason. That work now happens alongside normal traffic: the longest anyone else waits is around a twenty-fifth of a second, whatever the size of the workflow being converted. The conversion itself takes somewhat longer in exchange, and what it produces is unchanged. Note that conversion time is driven by how complex your formulas are rather than by the tool count alone — a workflow of simple tools converts far faster than the same number of tools carrying nested conditional expressions. See Migrate Alteryx Workflows.

  • A connection that signs in with OAuth2 can no longer hold up everyone else while it waits. Before PlaidCloud calls a REST connection’s API it fetches an access token from the address that connection names, and that fetch ran on the same thread serving every other request, with no time limit on it. So a token address that accepted the connection and then never replied could leave the whole workspace unresponsive for as long as it stayed silent — with nothing on screen to point at a connection as the cause, since the connection’s own step was one of the many things left waiting. The fetch now runs alongside other work and gives up after 30 seconds, failing that one call and leaving everything else running. This covers REST and Mulesoft connections, Salesforce and Sage, and connections imported from Postman. Nothing changes for a token address that answers normally. See Create a Connection.

  • A forward-looking allocation answer no longer marks a figure unreliable because of rounding. Where a change passes straight through from one table to the next, the amount arriving is the amount leaving — but the two totals are added up in a different order, so they can differ in the last decimal place, by roughly a billionth of a penny on seven million. The check for “this target holds more than the pool it came from”, there to catch a genuinely shared or rescaled target, was treating that as a real overshoot: one pass-through was marked unreliable while an identical one beside it was not, because the rounding fell the other way. Nothing in the written answer was affected — the mark appears in an answer’s underlying data, which is what a dashboard, an export or a connected assistant reads.

  • A step whose run was interrupted now reports itself as abandoned, instead of appearing to run forever. When the work behind a step went away mid-run — because someone pressed Stop, or because platform maintenance interrupted it — nothing ever corrected the step’s status. The workflow view kept showing it as running with a duration climbing indefinitely, which reads as an extremely slow step: in the case that prompted this, a step that takes three seconds displayed as running for over twenty minutes. Such a step is now marked abandoned, with an explanation in its status tooltip, and its duration stops; an open workflow view refreshes on its own. An abandoned step did not complete and produced no output — it is not a slow step and not a data problem. Either Resume the run, which runs the abandoned step again, or re-run the whole workflow. See Follow a Step’s Status.

  • A screen left open for a while no longer stops working until you reload. Signing in gives your browser a short-lived pass, which PlaidCloud renews for you in the background. The older screens — the project list, the workflow list — renewed it; the newer ones could not, and could only accept or refuse a pass. So once it expired, those screens answered every request with “not authorised” however many times you retried, until you happened to visit one of the older screens or reloaded — at which point the newer screens started working again, having been put right by something you did elsewhere. Every screen now renews the pass the same way, so the wait is a moment rather than a dead end, and recovery no longer depends on which screens you visit or in what order. Two smaller things come with it: while a renewal is under way PlaidCloud asks whoever arrives next to try again, and every screen now does so by itself instead of showing a failure; and if your session really has ended you are taken to the sign-in page and returned to where you were. The in-app Help and support-ticket screens are fixed with them. What each screen is allowed to do is unchanged.

  • A workflow step saved with an output column that has no data type now saves, and runs. Adding an output column by hand used to leave it without a type, so saving a Table Lookup or any of the Inner, Outer, Anti and Cross Join steps failed with Update Failed – Internal Server Error and removing the column again was the only way out. A new row now starts as Text, and you can change the type as usual.

    A step already saved that way also failed when it ran, because it could not build its output table. The type is now worked out as the step starts, from the source column that output column reads; where there is nothing to read it from the column is treated as text — an Extract SQL step’s columns come from the query you wrote, and where several sources carry that column name under types that disagree, text is used rather than guessing one of them, since a wrong guess produces a conversion the warehouse can reject. This reaches every step that builds its output table from configured columns: Table Lookup, all four Joins, both Unions, the Extracts, Append, Upsert, the two geospatial steps and the file imports. Multi-Table Join is the deliberate exception — it stops and names the column, which is more useful than a guess in a step joining several tables. Setting the column’s Type in the step form remains how you say what the column should actually be; it is no longer what stands between the step and running. See Column Data Type.

    Two related cases go with it. Creating a table with a column left without a data type no longer fails outright — the column is created as text, covering tables built through the API and through the AI assistant. And a step whose row condition compares an untyped column now runs: steps that delete, update or reshape rows in place send their row condition to the engine separately, and a recent security change to how that condition is read turned away any comparison involving something the platform had no type for — an untyped column, hand-written SQL used as a column, or the result of a user-defined function — stopping the step with an internal error about a refused query that named nothing you could act on — and it applied to every comparison those can appear in, not only a check for empty. A column given the interval type was refused the same way with nothing untyped in the condition at all. All of these run again, conditions that ran before are unaffected, and the query forms that security change exists to refuse are still refused.

  • Excel imports no longer alter the values they bring in. A code held as a number in the spreadsheet — an account or a product id — arrived in a text column with .0 on the end, so 600100 came in as 600100.0; only the rows actually holding a number were affected, which is why a column mixing numeric codes with lettered ones came in half-changed. Three smaller versions of the same fault went with it: a very large whole number arrived in scientific notation rather than as its digits, a date lost its time part, and a duration was written in a form that changed shape once it passed a day. Separately, a spreadsheet that records its true/false columns as 1 and 0 imports again — such a file used to stop the whole import with an error naming the column — not merely the rows concerned, and not only the flag column. All of these read as they did before July’s change to the way spreadsheets are read; columns imported as a number or a date were never affected. Re-run the import to correct rows already loaded. See Import Excel.

  • Timestamp columns no longer import as empty on Lakehouse v2. Importing a CSV or other delimited text file into a Lakehouse v2 workspace left every timestamp column empty while the import reported success, so nothing pointed at the problem until a later step or a dashboard turned up missing dates. The date format PlaidCloud handed the warehouse was written in the wrong dialect’s format codes, and the two disagree on the most common one in a timestamp: the code sent for minutes means full month name to Lakehouse v2, so asked to read 22 as a month name the warehouse matched nothing and returned an empty value for every row rather than an error.

    The same misreading applied to to_timestamp, to_date and to_char anywhere they appear — a transform expression, a view, or a converted Alteryx workflow — where instead of emptying a column it produced visibly wrong text: a HH24:MI:SS format rendered 14:22:31 as 14:August:31. This affected any workspace on Lakehouse v2. Parquet imports were never affected, because they carry their own column types and never build a date format, and no other warehouse type was affected. Already-imported rows are not corrected retroactively — check any table you loaded from text files with dates or timestamps, and re-run those imports.

    If you imported a file after this fix was first announced and still found empty timestamps, that is why. The correction reached the main service but not the separate, slimmer service that builds most file imports, which was running an older version of the shared code. Both now carry it. Re-run any import you ran in between and check its timestamp columns. One related case is not fixed: a column whose data type is Time still imports as empty on Lakehouse v2, for a different reason — the warehouse function used to parse it cannot produce a bare time-of-day value at all. Import the column as Timestamp or as Text until that is addressed. See Import CSV.

  • Exporting a project archive no longer freezes the window, hides a table it could not read, or reports a failure as a success. Choosing Export Project Archive used to hold the window on a loading spinner for the whole archive, with no progress and no way to cancel or step away, and it gave up with an error after ten minutes even though the archive usually carried on running — well inside how long a real archive takes, since on the workspace this was reported from archives of 42, 45, 54 and 64 minutes all occurred against 15 seconds to 7 minutes for a typical one. The window now closes as soon as the request is accepted, a progress window follows the archive, and a notification reports the result. Scheduled archives are unchanged.

    What that notification says is now true in two more cases. A table listed in the project with no data behind it no longer disappears quietly: the archive is still written, that table’s data is left out, and the export names the tables it skipped — as does restoring it, rather than presenting them as complete. And an export that genuinely failed used to report as finished with no file behind it, so the problem surfaced later, usually when someone came to restore the archive; a failure is now reported as a failure. This covers every export type, not only project archives, and the export window names the project it is archiving. See Archive a Project.

  • Importing a project archive with Create new copy of duplicate items now produces items you can tell apart. A project variable is identified by its name, so an incoming variable the target project already defined was given a fresh identifier — arriving as a variable named something like f47ac10b-58cc-4372-a567-0e02b2c3d479, which nothing referenced, while every {variable} in the imported steps went on resolving to the target project’s own value. Nothing failed and nothing said so; the import simply appeared not to have taken. A variable the target already defines is now left alone, and the import tells you which ones it kept.

    Everything else an archive carries — workflows, steps, tables, dimensions, data editors, views and user-defined functions — got its fresh identifier but kept its original name, so you were left with two workflows called Load Data and two dimensions called Period, indistinguishable in every picker, with anything that looks an object up by name choosing between them arbitrarily. A copy is now named the way a duplicated workflow already is, with copy appended and a counter if that is taken. This applies where the incoming object collides with one already in the project; two objects that merely share a name are unaffected. See Duplicate Items.

  • Where Used on a connection now finds REST v2 steps. Before you edit or delete a connection, Where Used… lists the workflow steps that reference it. A REST v2 step was missing from that list — it picks a connection the same way every other step does, but was never among the step types the search looked at. The risk was in what the list did not say: a connection used only by REST v2 steps looked unused, and deleting it on that basis broke steps the list had just told you were not there. See Clone, Delete, and Trace Usage.

  • A Table Anti Join no longer turns a text column into numbers. The step now offers only source A columns as output, matching what an anti-join returns — the rows of source A with no match in source B. Before, when a join key existed on both sides it could be picked up from source B, and if source B typed that column as a number the output column came out numeric even though source A held text. Source B still supplies the join keys; it just no longer offers output columns, and any that were set are dropped when you open the step. The step’s help note also spells out that it is a left-excluding join — for a left join, use Table Outer Join with its Join Type set to Left. See Table Anti Join.

  • A Macro Concurrent step no longer waits forever on a child invocation that has stopped responding. A child reports its own result when it finishes, so a child whose machine stopped without warning never reported anything — and the step went on waiting. There was no error and no progress to see: one run sat at Completed 0 of 29 loops for eighteen minutes, with nothing to do but stop it by hand. The step now checks that each child it is waiting on is still alive and gives up on one that is not, stopping it, cleaning up after it, and failing at the end naming that invocation, the last state it reported, and when. The other children are unaffected — they finish and their output tables are written as normal. A child working through a single long-running step is left alone for as long as it takes, and a check that cannot be answered is not treated as an answer. See When a Child Invocation Stops Responding.

  • An ML: Train Model step can now save a large model. A random forest left at its default hundred trees could not be saved at all: training succeeded, and the step then spent close to four minutes writing the model away before the warehouse connection dropped, reporting Broken pipe or Lost connection — which says nothing about what went wrong and does not tell you the training had worked. Models are now compressed before they are stored, and one too large for a single value is split across as many rows as it needs. A model still too large — more than 128 MB compressed, around twice what a default hundred-tree forest produces — stops as soon as training finishes and tells you its size and the limit, instead of running for minutes and failing with a connection error.

    Two things to know if you read a model table directly rather than passing it to an ML: Score step: it may now hold more than one row, so read metrics_json from a single row (WHERE chunk_index = 0), and the stored model is compressed, so reassembling it yourself means joining the pieces in chunk_index order and decompressing before you unpickle. ML: Score handles all of this and stops with an error if any piece is missing rather than scoring with an incomplete model. Models trained before this release are still read as they were. See How the Model Is Stored.

  • A report text block now prints the value of a column its text names, instead of printing the reference itself. Text referring to a column was written into the report with the reference still in it, so the finished PDF read [Year:A] © Mapbox where it should have read 2026 © Mapbox. The step reported success and the document looked normal, so there was nothing to notice until someone read the page. Naming a column the text cannot read now stops the step and says which column. Ordinary bracketed prose is untouched: a time like [12:30] and an aside like [Note: continued] still print as written. Re-run a report to correct it. See Report Text.

  • A table whose column information could not be read no longer looks like a table with no columns. Steps treated a failed read as “this table has none” and said so, pointing you at your own step order and at whether an upstream step had populated the table, when the real problem was the read. Those steps now say the column information could not be read, and why. This covers converted Alteryx steps and ordinary Union steps alike; a Union over a genuinely empty source is unchanged. Two report cases were affected quietly and are worth checking: a report snippet whose source could not be read was skipped, and a report table section came out empty — both on runs that reported success, so the report simply arrived with a piece missing. A report that used to come out missing a section may now stop with an explanation instead. See Reports in a Converted Workflow.

  • An Access import that cannot read a table now fails and says which one. Importing a Microsoft Access .mdb or .accdb file whose tables could not be read finished green, leaving a table with no columns and no rows behind it — and nothing surfaced until a later step tried to read that table and failed, pointing the investigation at the wrong place entirely. The import now fails at the import, naming the file and the table. A file whose tables are genuinely empty still imports, and reports that it loaded no records. See Import Access.

  • A member you disabled can be enabled again. Disabling a member from Identity → Members recorded the time in a format the rest of the platform could not read back, and the only screen that can re-enable someone reads it first — so Enable failed with an internal error and there was no way to reverse a disable. The same unreadable value broke the member list itself: with one member disabled this way, Identity → Members and the member pickers that use it stopped loading for everyone in the workspace. Both work again, and members already disabled this way can be enabled normally — nothing needs to be re-done to them, and no data was lost.

  • The dimension-member picker now opens reliably and applies to the right rows. Double-clicking a property cell configured to pick a member from a dimension — in the dimension hierarchy grid or a Data Editor grid — needed a second double-click to open when the cell already had a value, and that second click could land the pick on the wrong row further down the grid. Picking a member in an empty cell and clicking Apply silently discarded it, in some cases Apply cleared the value instead of setting it, and in a Data Editor grid a pick was discarded outright. The picker now opens on the first double-click, and Apply and Clear both work on the row you actually edited, in both grids. With several rows selected, Apply still applies to every selected row — that’s intentional multi-row editing, and it now always includes the row you double-clicked. Separately, a property’s Allow Multiple Selections checkbox, in its Dimension Configuration, can now be turned off; it previously reverted to checked no matter what you saved. See Dimension-Type Properties and the Member Picker.

  • A workspace no longer becomes slow to respond because of requests whose contents never arrived. Where a request reached PlaidCloud announcing itself but its contents did not follow, the platform went on waiting for as long as twenty minutes even though your browser had given up after twenty seconds. Each one held a slot on the process serving the whole workspace, so enough of them accumulating made everything slow, and only restarting the workspace cleared them. Such a request is now given up on after fifteen seconds. Nothing you do legitimately takes that long to send, and file uploads, which genuinely are large, are handled separately.

  • AI agents can now build the text, document, spatial and report steps from their documented shape. Asked to create a Text Sentiment, Topic Model, Word Cloud, PDF text, image OCR, Fuzzy Match, XML or JSON parse, any of the spatial or geospatial steps, or any of the report pieces, an agent read the platform’s own description of the step and built it exactly as described — and the step failed on its first run every time, because the description named the input-column list differently from the way the step reads it. Thirty-six step types were affected; the description now matches. Steps converted from Alteryx were never affected.

  • An AI assistant can now set a step’s run conditions. Asking one to add a condition — run this step only when the source table has rows, or only when a project variable holds a particular value — reported success and stored nothing, so the step went on running unconditionally with nothing to say a condition had been dropped. Checking the change before writing it reported no problem either, and both creating a step and editing an existing one were affected. Conditions set this way are now stored and evaluated exactly as ones you set on the Conditions tab, and the same fix covers a step’s lock, its visibility in the Manager and Explorer views, its retry settings, and the step it goes to on error.

    The condition list is replaced in full on every write, so an assistant changing one condition has to send every other condition you want to keep. A condition that could not be evaluated is now refused rather than stored — such a condition used to save and then skip the step on every run — while one that is merely incomplete is reported back as a warning and still written. Check Financial Close Workday is the one condition type an assistant cannot set usefully yet: it can write the condition, but that check never matches, so the assistant warns you rather than leaving you to discover it on the next run. Set that one on the Conditions tab yourself. See Setting Conditions With an AI Assistant.

  • The AI assistant draws diagrams as pictures, won’t invent the numbers in them, and can send email and SMS again. A requested diagram occasionally arrived as a block of diagram source code instead of an image; it now renders whichever notation the assistant picks. A chart or Sankey needs a figure for every bar or band, and where the assistant hasn’t been given one it now asks you rather than drawing a plausible-looking value, so a picture that reads as authoritative is not quietly made up. Separately, asking it to send a message either failed outright or reported success while sending nothing; sending now works, and leaving the recipient out is reported as an error instead of quietly doing nothing. See Using the AI Assistant and Using Email.

  • Two fixes for connected agents reading your workspace. An agent asking “has this changed since I last looked?” was compared only on the identifiers in the list, so renaming or editing a record — without adding or removing one — still came back as “not modified” and the agent could go on working from a stale copy; it now compares the records themselves, consistently whichever server handled the request. And searching warehouses by name returns the matching warehouses: the search answered with a bare identifier instead of records, so an agent got neither the details nor a count and an empty result was indistinguishable from an error. It now returns records like every other search, and matches on any part of the name regardless of case.

  • The Text Sentiment step’s sentence-level scoring is documented. Find sentiment at sentence level — which scores each sentence in a record and returns the average, rather than scoring the whole body at once — has worked all along, but nothing described it. It is documented now, including the case that makes it look like it does nothing: on records holding a single sentence, the average over sentences is the whole-body score, so the two settings agree. See Text Sentiment.

  • The Import Table Bundle and Export Table Bundle progress windows no longer show <Information Message> above the progress bar. A dialog with no message to display was falling back to a placeholder meant for development, and rendering it as literal text. Dialogs with nothing to say now show nothing there.

  • Editing a security group’s permissions no longer removes the permissions you don’t hold yourself, and can’t grant ones you do not have. Anyone who was not a workspace administrator silently stripped every permission on that group they could not grant themselves — just by opening the permission matrix and saving it, even without changing anything. Workspace administrators were unaffected, which is why this looked like permissions disappearing at random for some people and never for others, and why a permission held by only a few, such as Panel app management, would drop off repeatedly and have to be re-added. Those permissions are now left untouched. Review any security group a non-administrator has edited and confirm its permissions are what you expect — a permission removed this way was removed for everyone in the group.

    The matrix only ever offered the permissions you are allowed to control, but a request naming others was accepted anyway, so someone able to manage users could grant a group — and through it themselves — broader access than they held; permissions being granted are now checked against your own. Saving a member or group assignment form with nothing selected now says nothing changed, rather than reporting success while making no change, or, on the member form, removing every group that member had. And permission and membership changes are recorded in the security log: changing a group’s permissions or members, or a member’s groups, previously left no record unless the Git server sync failed, so a permission that changed could not be attributed to anyone afterwards. Every change is now logged with what was granted and what was revoked. See Managing Security Groups and Assignments and Performing a Security Audit.

  • Importing an archive into an existing project keeps that project’s access restrictions, and checks that you can write to it. A project restricted to named users or to security groups was being switched to open access as a side effect of the import, which made it readable — as an Architect — by everyone in the workspace. The restriction was reset because an archive is imported the way a new project is created from one, and a new project is deliberately opened up, since the access lists inside an archive name people from wherever it was exported. That reasoning does not hold for a project that already exists and already has a working access list, so its access type, its lists and its row-access grants are now left exactly as they were. Worth checking: if you have imported an archive into a restricted project, confirm its access setting on the project’s configuration screen.

    The import also checked that you were entitled to import something, but never that you had any role on the project you were importing into — so anyone able to import could write an archive’s contents into any project in the workspace, including one they had no access to and one locked to prevent changes. The target project is now checked the way every other change to a project is. See Importing Into an Existing Project.

  • A project archive no longer includes table data for someone who cannot read the project’s tables. Reading a project’s tables is a separate permission from reading the project itself, and the archive checked it before writing the tables’ definitions but not before writing their rows — so a member holding project access alone could name any table in the project and download its contents inside an archive. Table data is now withheld from anyone without that permission, and the export says which tables were left out.

    Nothing changes for a member who can read the project’s tables. Nor is anything lost: such an archive already carried no table definitions, so restoring one never recreated any tables — the data in it was download-only. Worth checking: if you keep scheduled archives as backups, confirm the member they run as can read the project’s tables, or those archives hold configuration and no data. See Archive a Project.

  • A query assembled outside the platform, or an expression written inside it, can no longer carry SQL of its own. Several Analyze operations — allocate, melt, pivot, update, delete, table exports and view definitions — accept a query that was assembled elsewhere and sent along with the request. The platform used to reconstruct whatever arrived, which meant a crafted request from anything able to reach those operations could have run code inside the server. Such a request now rebuilds only the database-query pieces a genuine query is made of, and the text those pieces hold is checked too: a literal SQL fragment must be a single value rather than a statement, and a name that asks to skip quoting must be an ordinary identifier. Queries you build in the product are unaffected, as are column names containing spaces, dots or quotation marks — those are quoted for you and always were.

    The same rule applies to an expression you type into a data mapper or a step’s filter, which is built inside the platform and so was never checked, even though it reaches a query all the same: an allocation step’s source filter is turned into SQL and put directly into the query’s WHERE clause. Inside sqlalchemy.literal_column only, four shapes stop working. Text that is a subquery is refused, because that is the shape that reads a table the query never declared — build it with sqlalchemy.select(...), .scalar_subquery() for a single value or .subquery() for a table to select from. Text whose outermost operator is OR is refused, because a condition added after it binds to only half of it — put brackets round it, (a OR b). XOR is refused wherever it appears — write it as (a AND NOT b) OR (NOT a AND b). And text carrying a comment is refused, because a comment silently erases whatever the platform adds after it: -- where it would run to the end of the statement, and # anywhere at all, including inside a quoted string and including Postgres’ #> and #>> JSON path operators, because # opens a comment on MySQL-compatible warehouses such as StarRocks and which warehouse will run the expression is not known when it is written. If you need the character itself, pass it as a value with sqlalchemy.literal('#').

    Everything else is unchanged — literal_column('current_date'), a CASE, a cast, a window function, a warehouse-specific function, a bracketed disjunction, and func.count(), which builds one of these for you. An export that supplies plain SQL rather than a pre-built query works exactly as before, and when an AI assistant checks an expression for you it names the shape it refused and what to write instead. See Reading Another Table From an Expression.

  • Copying workflow steps now checks your access to the project you are copying from. The copy checked only the project you were copying into, so the source project — and the workspace it was named in — were taken on trust. A request built by hand could name any project and have that step’s full setup, including its connections and its SQL, recreated in a project of its own choosing. Both are now checked against the workspace you are signed in to, a copy asked to read out of a workspace you are not in is refused, and what is copied is filtered against your role in the source project rather than the destination. Copying between projects you can already open is unchanged, and the copy and paste buttons only ever worked within your own workspace. See Copy & Paste steps.

  • Editing a workflow with the AI assistant can no longer leave it unable to open. Where the assistant changed a workflow’s steps — the reported case was an Import CSV step — a stray, malformed entry could be written into the workflow’s saved structure, and from then on the workflow could not be opened, added to, or have a step removed or switched on or off. The places that read a workflow’s structure now step past such an entry instead of failing on it, so a workflow already left in this state opens again with nothing to repair, and the assistant’s save refuses a malformed entry with a clear error naming it rather than storing it or dropping it silently.