diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/processes/ProcessSummaryFilterLink.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/processes/ProcessSummaryFilterLink.java
new file mode 100644
index 00000000..b13e69e3
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/processes/ProcessSummaryFilterLink.java
@@ -0,0 +1,291 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2022. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.backend.core.model.actions.processes;
+
+
+import com.kingsrook.qqq.backend.core.logging.LogPair;
+import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
+import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
+
+
+/*******************************************************************************
+ ** Simple process summary result object, that lets you give a link to a filter
+ ** on a table. e.g., if your process built such a records, give a link to it.
+ *******************************************************************************/
+public class ProcessSummaryFilterLink implements ProcessSummaryLineInterface
+{
+ private Status status;
+ private String tableName;
+ private QQueryFilter filter;
+ private String linkPreText;
+ private String linkText;
+ private String linkPostText;
+
+
+
+ /*******************************************************************************
+ ** Constructor
+ **
+ *******************************************************************************/
+ public ProcessSummaryFilterLink()
+ {
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Override
+ public LogPair toLogPair()
+ {
+ return (logPair("ProcessSummary", logPair("status", status), logPair("tableName", tableName),
+ logPair("linkPreText", linkPreText), logPair("linkText", linkText), logPair("linkPostText", linkPostText)));
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public ProcessSummaryFilterLink(Status status, String tableName, QQueryFilter filter)
+ {
+ this.status = status;
+ this.tableName = tableName;
+ this.filter = filter;
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public ProcessSummaryFilterLink(Status status, String tableName, QQueryFilter filter, String linkText)
+ {
+ this.status = status;
+ this.tableName = tableName;
+ this.filter = filter;
+ this.linkText = linkText;
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for status
+ **
+ *******************************************************************************/
+ public Status getStatus()
+ {
+ return status;
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for status
+ **
+ *******************************************************************************/
+ public void setStatus(Status status)
+ {
+ this.status = status;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for status
+ **
+ *******************************************************************************/
+ public ProcessSummaryFilterLink withStatus(Status status)
+ {
+ this.status = status;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for tableName
+ **
+ *******************************************************************************/
+ public String getTableName()
+ {
+ return tableName;
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for tableName
+ **
+ *******************************************************************************/
+ public void setTableName(String tableName)
+ {
+ this.tableName = tableName;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for tableName
+ **
+ *******************************************************************************/
+ public ProcessSummaryFilterLink withTableName(String tableName)
+ {
+ this.tableName = tableName;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for linkPreText
+ **
+ *******************************************************************************/
+ public String getLinkPreText()
+ {
+ return linkPreText;
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for linkPreText
+ **
+ *******************************************************************************/
+ public void setLinkPreText(String linkPreText)
+ {
+ this.linkPreText = linkPreText;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for linkPreText
+ **
+ *******************************************************************************/
+ public ProcessSummaryFilterLink withLinkPreText(String linkPreText)
+ {
+ this.linkPreText = linkPreText;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for linkText
+ **
+ *******************************************************************************/
+ public String getLinkText()
+ {
+ return linkText;
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for linkText
+ **
+ *******************************************************************************/
+ public void setLinkText(String linkText)
+ {
+ this.linkText = linkText;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for linkText
+ **
+ *******************************************************************************/
+ public ProcessSummaryFilterLink withLinkText(String linkText)
+ {
+ this.linkText = linkText;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for linkPostText
+ **
+ *******************************************************************************/
+ public String getLinkPostText()
+ {
+ return linkPostText;
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for linkPostText
+ **
+ *******************************************************************************/
+ public void setLinkPostText(String linkPostText)
+ {
+ this.linkPostText = linkPostText;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for linkPostText
+ **
+ *******************************************************************************/
+ public ProcessSummaryFilterLink withLinkPostText(String linkPostText)
+ {
+ this.linkPostText = linkPostText;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for filter
+ *******************************************************************************/
+ public QQueryFilter getFilter()
+ {
+ return (this.filter);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for filter
+ *******************************************************************************/
+ public void setFilter(QQueryFilter filter)
+ {
+ this.filter = filter;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for filter
+ *******************************************************************************/
+ public ProcessSummaryFilterLink withFilter(QQueryFilter filter)
+ {
+ this.filter = filter;
+ return (this);
+ }
+
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/RunRecordScriptTransformStep.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/RunRecordScriptTransformStep.java
new file mode 100644
index 00000000..1eb21e7b
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/scripts/RunRecordScriptTransformStep.java
@@ -0,0 +1,98 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2023. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.backend.core.processes.implementations.scripts;
+
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
+import com.kingsrook.qqq.backend.core.exceptions.QException;
+import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
+import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
+import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryRecordLink;
+import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
+import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
+import com.kingsrook.qqq.backend.core.model.actions.processes.Status;
+import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
+import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
+import com.kingsrook.qqq.backend.core.model.scripts.Script;
+import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.NoopTransformStep;
+import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
+import com.kingsrook.qqq.backend.core.utils.StringUtils;
+
+
+/*******************************************************************************
+ ** Transform step run-record-script process. extends no-op, but main purpose is
+ ** to set FIELD_VALIDATION_SUMMARY in the output, with the name of the script
+ ** you selected.
+ *******************************************************************************/
+public class RunRecordScriptTransformStep extends NoopTransformStep
+{
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Override
+ public ArrayList getProcessSummary(RunBackendStepOutput runBackendStepOutput, boolean isForResultScreen)
+ {
+ ArrayList processSummary = new ArrayList<>();
+
+ try
+ {
+ Serializable scriptId = runBackendStepOutput.getValue("scriptId");
+ GetInput getInput = new GetInput();
+ getInput.setTableName(Script.TABLE_NAME);
+ getInput.setPrimaryKey(scriptId);
+ GetOutput getOutput = new GetAction().execute(getInput);
+ if(getOutput.getRecord() != null)
+ {
+ processSummary.add(new ProcessSummaryRecordLink(Status.OK, Script.TABLE_NAME, scriptId, getOutput.getRecord().getValueString("name"))
+ .withLinkPreText(StringUtils.plural(runBackendStepOutput.getRecords(), "It", "They") + " will have the script ")
+ .withLinkPostText(" ran against " + StringUtils.plural(runBackendStepOutput.getRecords(), "it.", "them.")));
+ }
+ else
+ {
+ processSummary.add(new ProcessSummaryLine(Status.ERROR, null, "The selected script could not be found."));
+ }
+ }
+ catch(Exception e)
+ {
+ processSummary.add(new ProcessSummaryLine(Status.ERROR, null, "Error getting the script: " + e.getMessage()));
+ }
+
+ return (processSummary);
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Override
+ public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
+ {
+ super.run(runBackendStepInput, runBackendStepOutput);
+
+ runBackendStepOutput.addValue(StreamedETLWithFrontendProcess.FIELD_VALIDATION_SUMMARY, doGetProcessSummary(runBackendStepOutput, false));
+ }
+
+}