Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
552c024
docs(specs): analyze repository and define scheduled workflows spec
arielshad Mar 20, 2026
353530b
docs(specs): define requirements and product questions for scheduled …
arielshad Mar 20, 2026
4ad8cc4
docs(specs): research technical decisions for scheduled workflows
arielshad Mar 20, 2026
66e482f
docs(specs): create implementation plan and task breakdown for schedu…
arielshad Mar 20, 2026
39276ab
feat(tsp): add scheduled workflow domain models and croner dependency
arielshad Mar 20, 2026
eb5367d
feat(domain): add iclock port interface and workflow notification events
arielshad Mar 20, 2026
4b70b46
feat(domain): add persistence layer for scheduled workflows
arielshad Mar 20, 2026
17ca14d
feat(domain): add application logic and scheduler service for schedul…
arielshad Mar 20, 2026
9ac34b9
feat(cli): add workflow command group and daemon scheduler integration
arielshad Mar 20, 2026
bad62e9
feat(web): add workflow management page and server actions
arielshad Mar 20, 2026
41a0d69
fix(deps): add croner to root package dependencies
arielshad Mar 20, 2026
d33e6be
fix(domain): add workflow scheduler mocks to serve command tests
arielshad Mar 20, 2026
5460953
chore(specs): capture evidence for scheduled workflows feature
arielshad Mar 20, 2026
8432c34
chore(specs): recapture evidence for scheduled workflows feature
arielshad Mar 21, 2026
23c45d8
chore(specs): mark evidence step completed for scheduled workflows fe…
arielshad Mar 21, 2026
28af662
fix(ci): attempt 1/10 — add missing storybook mocks for workflow serv…
arielshad Mar 21, 2026
c5ed856
feat(domain): gate scheduled workflows behind feature flag
arielshad Mar 22, 2026
20f4355
fix(web): add missing scheduledworkflows feature flag to repository d…
arielshad Mar 24, 2026
5b0bf60
style(web): fix prettier formatting in app sidebar import
arielshad Mar 24, 2026
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
5 changes: 5 additions & 0 deletions .storybook/mocks/app/actions/get-workflow-history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function getWorkflowHistory() {
return {
executions: [],
};
}
5 changes: 5 additions & 0 deletions .storybook/mocks/app/actions/list-workflows.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function listWorkflows() {
return {
workflows: [],
};
}
5 changes: 5 additions & 0 deletions .storybook/mocks/app/actions/toggle-workflow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function toggleWorkflow() {
return {
success: true,
};
}
5 changes: 5 additions & 0 deletions .storybook/mocks/app/actions/trigger-workflow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function triggerWorkflow() {
return {
success: true,
};
}
5 changes: 5 additions & 0 deletions apis/json-schema/FeatureFlags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ properties:
type: boolean
default: false
description: Use the built-in React file manager instead of the native OS folder picker
scheduledWorkflows:
type: boolean
default: false
description: Enable scheduled workflows feature — workflow creation, scheduling, and execution
required:
- skills
- envDeploy
Expand All @@ -38,4 +42,5 @@ required:
- adoptBranch
- gitRebaseSync
- reactFileManager
- scheduledWorkflows
description: Feature flag toggles for runtime feature control
15 changes: 15 additions & 0 deletions apis/json-schema/NotificationEventConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ properties:
type: boolean
default: true
description: Notify when feature is ready for merge review
workflowStarted:
type: boolean
default: true
description: Notify when a scheduled workflow execution starts
workflowCompleted:
type: boolean
default: true
description: Notify when a scheduled workflow execution completes successfully
workflowFailed:
type: boolean
default: true
description: Notify when a scheduled workflow execution fails
required:
- agentStarted
- phaseCompleted
Expand All @@ -58,4 +70,7 @@ required:
- prChecksFailed
- prBlocked
- mergeReviewReady
- workflowStarted
- workflowCompleted
- workflowFailed
description: Notification event type filters
3 changes: 3 additions & 0 deletions apis/json-schema/NotificationEventType.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ enum:
- pr_checks_failed
- pr_blocked
- merge_review_ready
- workflow_started
- workflow_completed
- workflow_failed
description: Types of agent lifecycle notification events
46 changes: 46 additions & 0 deletions apis/json-schema/ScheduledWorkflow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: ScheduledWorkflow.yaml
type: object
properties:
name:
type: string
description: Unique human-readable name identifying this workflow within the repository
description:
type: string
description: Optional human-readable description of what this workflow does
prompt:
type: string
description: Agent prompt instruction that the AI agent will execute when the workflow runs
toolConstraints:
type: array
items:
type: string
description: Optional allowlist of tool names the workflow agent is permitted to use
cronExpression:
type: string
description: "Cron expression defining the schedule (5-field format: min hour dom month dow)"
timezone:
type: string
description: IANA timezone for cron evaluation (e.g. America/New_York). Defaults to UTC
enabled:
type: boolean
description: Whether the workflow schedule is active. Disabled workflows retain their cron expression but do not auto-execute
lastRunAt:
type: string
format: date-time
description: Timestamp of the most recent execution (manual or scheduled)
nextRunAt:
type: string
format: date-time
description: Calculated timestamp of the next scheduled execution based on cron expression
repositoryPath:
type: string
description: Absolute file system path to the repository this workflow operates on
required:
- name
- prompt
- enabled
- repositoryPath
allOf:
- $ref: SoftDeletableEntity.yaml
description: User-defined reusable automation that runs on demand or on a cron schedule
38 changes: 38 additions & 0 deletions apis/json-schema/WorkflowExecution.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: WorkflowExecution.yaml
type: object
properties:
workflowId:
$ref: UUID.yaml
description: ID of the ScheduledWorkflow that was executed
triggerType:
$ref: WorkflowTriggerType.yaml
description: How this execution was triggered (manual or scheduled)
status:
$ref: WorkflowExecutionStatus.yaml
description: Current lifecycle status of this execution
startedAt:
type: string
format: date-time
description: Timestamp when the execution started running
completedAt:
type: string
format: date-time
description: Timestamp when the execution completed or failed (null if still running)
durationMs:
type: integer
description: Duration of the execution in milliseconds (null if still running)
outputSummary:
type: string
description: Summary of the execution output (truncated if too long)
errorMessage:
type: string
description: Error message if the execution failed
required:
- workflowId
- triggerType
- status
- startedAt
allOf:
- $ref: BaseEntity.yaml
description: A single execution record for a scheduled workflow run
10 changes: 10 additions & 0 deletions apis/json-schema/WorkflowExecutionStatus.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: WorkflowExecutionStatus.yaml
type: string
enum:
- queued
- running
- completed
- failed
- cancelled
description: Lifecycle status of a workflow execution
7 changes: 7 additions & 0 deletions apis/json-schema/WorkflowTriggerType.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: WorkflowTriggerType.yaml
type: string
enum:
- manual
- scheduled
description: How a workflow execution was triggered
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
"better-sqlite3": "^12.6.2",
"cli-table3": "^0.6.5",
"commander": "^14.0.3",
"croner": "^10.0.0",
"js-yaml": "^4.1.1",
"minimatch": "^7.x",
"next": "16.1.6",
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
},
"dependencies": {
"ajv": "^8.18.0",
"croner": "^10.0.0",
"tree-kill": "^1.2.2",
"which": "^5.0.0"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
export type { IFeatureRepository, FeatureListFilters } from './feature-repository.interface.js';
export type { ISettingsRepository } from './settings.repository.interface.js';
export type { IRepositoryRepository } from './repository-repository.interface.js';
export type { IWorkflowRepository, WorkflowListFilters } from './workflow-repository.interface.js';
export type { IWorkflowExecutionRepository } from './workflow-execution-repository.interface.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Workflow Execution Repository Interface
*
* Output port for WorkflowExecution persistence operations.
* Implementations handle database-specific logic (SQLite, etc.).
*
* Following Clean Architecture:
* - Domain and Application layers depend on this interface
* - Infrastructure layer provides concrete implementations
*/

import type {
WorkflowExecution,
WorkflowExecutionStatus,
} from '../../../../domain/generated/output.js';

/**
* Repository interface for WorkflowExecution entity persistence.
*
* Implementations must:
* - Handle database connection management
* - Provide thread-safe operations
* - Support efficient queries by workflowId and status
* - Support retention cleanup via deleteOlderThan
*/
export interface IWorkflowExecutionRepository {
/**
* Create a new execution record.
*
* @param execution - The execution to persist
*/
create(execution: WorkflowExecution): Promise<void>;

/**
* Find an execution by its unique ID.
*
* @param id - The execution ID
* @returns The execution or null if not found
*/
findById(id: string): Promise<WorkflowExecution | null>;

/**
* Find executions for a specific workflow, ordered by started_at DESC.
*
* @param workflowId - The workflow ID
* @param limit - Optional maximum number of records to return
* @returns Array of executions, most recent first
*/
findByWorkflowId(workflowId: string, limit?: number): Promise<WorkflowExecution[]>;

/**
* Find executions with a specific status.
* Used by the scheduler for crash recovery (finding stale 'running' records).
*
* @param status - The execution status to filter by
* @returns Array of matching executions
*/
findByStatus(status: WorkflowExecutionStatus): Promise<WorkflowExecution[]>;

/**
* Update an existing execution record.
*
* @param execution - The execution with updated fields
*/
update(execution: WorkflowExecution): Promise<void>;

/**
* Delete execution records older than the given date.
* Used for retention cleanup (default: 30 days).
*
* @param date - Delete records with started_at before this date
* @returns The number of records deleted
*/
deleteOlderThan(date: Date): Promise<number>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Workflow Repository Interface
*
* Output port for ScheduledWorkflow persistence operations.
* Implementations handle database-specific logic (SQLite, etc.).
*
* Following Clean Architecture:
* - Domain and Application layers depend on this interface
* - Infrastructure layer provides concrete implementations
*/

import type { ScheduledWorkflow } from '../../../../domain/generated/output.js';

/**
* Filters for listing workflows.
*/
export interface WorkflowListFilters {
repositoryPath?: string;
enabled?: boolean;
/** When true, include soft-deleted workflows in results. Default: false. */
includeDeleted?: boolean;
}

/**
* Repository interface for ScheduledWorkflow entity persistence.
*
* Implementations must:
* - Handle database connection management
* - Provide thread-safe operations
* - Support query by name + repositoryPath for uniqueness
* - Exclude soft-deleted workflows from queries by default
*/
export interface IWorkflowRepository {
/**
* Create a new workflow record.
*
* @param workflow - The workflow to persist
*/
create(workflow: ScheduledWorkflow): Promise<void>;

/**
* Find a workflow by its unique ID (excludes soft-deleted).
*
* @param id - The workflow ID
* @returns The workflow or null if not found
*/
findById(id: string): Promise<ScheduledWorkflow | null>;

/**
* Find a workflow by name within a repository (excludes soft-deleted).
*
* @param name - The workflow name
* @param repositoryPath - The repository path to scope the search
* @returns The workflow or null if not found
*/
findByName(name: string, repositoryPath: string): Promise<ScheduledWorkflow | null>;

/**
* Find all enabled, non-deleted workflows.
* Used by the scheduler service to determine which workflows need evaluation.
*
* @returns Array of enabled workflows
*/
findEnabled(): Promise<ScheduledWorkflow[]>;

/**
* List workflows with optional filters.
* Excludes soft-deleted workflows unless includeDeleted is true.
*
* @param filters - Optional filters for repositoryPath, enabled state, and includeDeleted
* @returns Array of matching workflows
*/
list(filters?: WorkflowListFilters): Promise<ScheduledWorkflow[]>;

/**
* Update an existing workflow.
*
* @param workflow - The workflow with updated fields
*/
update(workflow: ScheduledWorkflow): Promise<void>;

/**
* Soft-delete a workflow by setting deletedAt timestamp.
* Preserves execution history for audit purposes.
*
* @param id - The workflow ID to soft-delete
*/
softDelete(id: string): Promise<void>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Clock Service Interface
*
* Output port for reading the current time. Enables deterministic testing
* of time-dependent logic (cron evaluation, nextRunAt calculation, retention
* cleanup) without relying on system time or Vitest fake timers.
*
* Following Clean Architecture:
* - Application layer depends on this interface
* - Infrastructure layer provides concrete implementation (RealClock)
* - Tests provide MockClock with controllable time
*/

/**
* Port interface for reading the current time.
*
* Implementations must:
* - Return a Date representing the current moment
* - Be safe to call frequently (no side effects)
*/
export interface IClock {
/**
* Get the current date and time.
*
* @returns A Date representing the current moment
*/
now(): Date;
}
Loading
Loading