|
|
@@ -140,6 +140,14 @@ nlohmann::json ExecutionResult::toJson() const {
|
|
|
j["error"] = error;
|
|
|
j["output"] = truncateLargeValues(final_output);
|
|
|
|
|
|
+ // A finished execution's node outputs are a log, so large strings are
|
|
|
+ // truncated. A Waiting execution's node outputs are the resume state a
|
|
|
+ // later resume seeds itself from - truncating them would hand the resume
|
|
|
+ // a literal "[omitted N bytes]" placeholder in place of real data, so
|
|
|
+ // they are kept whole. Do not "restore consistency" here later; that
|
|
|
+ // would silently reintroduce truncated resume data.
|
|
|
+ const bool keep_full_outputs = (status == ExecutionStatus::Waiting);
|
|
|
+
|
|
|
j["nodeExecutions"] = nlohmann::json::array();
|
|
|
for (const auto& [id, result] : node_results) {
|
|
|
nlohmann::json nr;
|
|
|
@@ -149,7 +157,7 @@ nlohmann::json ExecutionResult::toJson() const {
|
|
|
nr["finishedAt"] = result.finished_at;
|
|
|
// Note: input is intentionally not stored to avoid data duplication
|
|
|
// Each node's input can be reconstructed from upstream node outputs + connections
|
|
|
- nr["output"] = truncateLargeValues(result.output);
|
|
|
+ nr["output"] = keep_full_outputs ? result.output : truncateLargeValues(result.output);
|
|
|
nr["error"] = result.error;
|
|
|
nr["retryCount"] = result.retry_count;
|
|
|
j["nodeExecutions"].push_back(nr);
|
|
|
@@ -1450,13 +1458,20 @@ void WorkflowEngine::storeExecution(const ExecutionResult& result) {
|
|
|
// A finished execution is a log entry and ages out after a week. One that is
|
|
|
// waiting for a person is work still owed an answer, and having it expire
|
|
|
// under them loses the run silently, so it lives until its own deadline
|
|
|
- // plus a day of slack for a late approver.
|
|
|
+ // plus a day of slack for a late approver. An expiresAt of 0 means "no
|
|
|
+ // deadline", not "no need to extend" - it is given the 30-day ceiling the
|
|
|
+ // approval node caps at, so a permanent approval does not fall back to
|
|
|
+ // the ordinary seven-day log TTL and evaporate with no trace.
|
|
|
int64_t ttl_ms = 7 * 24 * 60 * 60 * 1000;
|
|
|
- if (result.status == ExecutionStatus::Waiting && result.pause_expires_at > 0) {
|
|
|
- const int64_t remaining = result.pause_expires_at - TimeUtils::nowMs();
|
|
|
+ if (result.status == ExecutionStatus::Waiting) {
|
|
|
const int64_t grace = 24 * 60 * 60 * 1000;
|
|
|
- if (remaining + grace > ttl_ms) {
|
|
|
- ttl_ms = remaining + grace;
|
|
|
+ const int64_t never_ttl = 30LL * 24 * 60 * 60 * 1000;
|
|
|
+ int64_t wanted = never_ttl;
|
|
|
+ if (result.pause_expires_at > 0) {
|
|
|
+ wanted = result.pause_expires_at - TimeUtils::nowMs() + grace;
|
|
|
+ }
|
|
|
+ if (wanted > ttl_ms) {
|
|
|
+ ttl_ms = wanted;
|
|
|
}
|
|
|
}
|
|
|
|