Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { resolveInitialWorkflowStatus } from './initial-workflow-status.util';

describe('resolveInitialWorkflowStatus (EVO-1690 regression)', () => {
it("maps the session-store 'active' status into the loop's 'running'", () => {
expect(resolveInitialWorkflowStatus('active')).toBe('running');
});

it("maps 'waiting' into 'running' so signal-driven resumes re-enter the loop", () => {
expect(resolveInitialWorkflowStatus('waiting')).toBe('running');
});

it("restarts a 'completed' session as 'running' (pre-existing resume semantics)", () => {
expect(resolveInitialWorkflowStatus('completed')).toBe('running');
});

it("defaults to 'running' when the session has no status", () => {
expect(resolveInitialWorkflowStatus(undefined)).toBe('running');
});

it("preserves 'paused' — a paused session must not resume by itself", () => {
expect(resolveInitialWorkflowStatus('paused')).toBe('paused');
});
});
20 changes: 20 additions & 0 deletions src/modules/temporal/workflows/initial-workflow-status.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export type WorkflowExecutionStatus =
| 'running'
| 'completed'
| 'failed'
| 'paused';

/**
* Maps a persisted journey-session status into the workflow loop's vocabulary
* (EVO-1690). The session store speaks 'active'/'waiting'/'completed'; the
* execution loop only advances on 'running' — copying the session status
* verbatim made `while (state.status === 'running')` false on the first
* iteration and every pre-created session (the norm since EVO-1644) completed
* with zero nodes executed. Only 'paused' survives the mapping: a paused
* session must not resume by itself.
*/
export function resolveInitialWorkflowStatus(
sessionStatus: string | undefined,
Comment on lines +16 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Tighten the sessionStatus type to known session-store statuses instead of string.

string | undefined makes it easy for unsupported statuses to slip in unnoticed. If the session store exposes a finite set of statuses, model that as a string union type (e.g. 'active' | 'waiting' | 'completed' | 'paused' | undefined) so TypeScript can enforce exhaustive handling when new statuses are introduced.

Suggested implementation:

/**
 * Maps a persisted journey-session status into the workflow loop's vocabulary
 * (EVO-1690). The session store speaks 'active'/'waiting'/'completed'; the
 * execution loop only advances on 'running' — copying the session status
 * verbatim made `while (state.status === 'running')` false on the first
 * iteration and every pre-created session (the norm since EVO-1644) completed
 * with zero nodes executed. Only 'paused' survives the mapping: a paused
 * session must not resume by itself.
 */
export type JourneySessionStoreStatus =
  | 'active'
  | 'waiting'
  | 'completed'
  | 'paused';

export function resolveInitialWorkflowStatus(
  sessionStatus: JourneySessionStoreStatus | undefined,
): WorkflowExecutionStatus {
  return sessionStatus === 'paused' ? 'paused' : 'running';
}

If there is already a shared type for the journey-session status (for example in a session-store module), you should reuse that instead of introducing JourneySessionStoreStatus here, and import it into this file. Replace the local JourneySessionStoreStatus definition with an import and keep the parameter typed as that union plus undefined.

): WorkflowExecutionStatus {
return sessionStatus === 'paused' ? 'paused' : 'running';
}
9 changes: 5 additions & 4 deletions src/modules/temporal/workflows/journey-execution.workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { ActionNodeActivities } from '../activities/action-nodes.activities
import type { JourneyTrackingActivities } from '../activities/journey-tracking.activities';
import type { JourneyTrackingContext } from '../services/journey-tracking.service';
import type { WaitActivities } from '../activities/wait.activities';
import { resolveInitialWorkflowStatus } from './initial-workflow-status.util';

// Interfaces for workflow input and state
export interface JourneyExecutionInput {
Expand Down Expand Up @@ -141,10 +142,10 @@ export async function JourneyExecutionWorkflow(
triggerEvent: input.triggerEvent, // Always use current trigger
},
completedNodes: existingSession.completedNodes || [],
status:
existingSession.status === 'completed'
? 'running'
: existingSession.status || 'running', // Resume if not completed
// Session-store statuses ('active'/'waiting') are a different
// vocabulary from the workflow loop's ('running'/'paused') — copying
// them verbatim kept the loop from ever running (EVO-1690).
status: resolveInitialWorkflowStatus(existingSession.status),
}
: {
currentNodeId: undefined,
Expand Down
Loading