Skip to content

feat: add cron workflow trigger and workflow runs table#385

Merged
marccampbell merged 16 commits into
mainfrom
feat/cron-workflow-trigger
Jun 9, 2026
Merged

feat: add cron workflow trigger and workflow runs table#385
marccampbell merged 16 commits into
mainfrom
feat/cron-workflow-trigger

Conversation

@elasticclaw-factory

Copy link
Copy Markdown
Contributor

Adds cron-based workflow triggers and execution history tracking.

Changes:

  • New CronWorkflowTrigger type with schedule, timezone, overlap_policy, timeout
  • New WorkflowRun type for tracking workflow executions
  • New workflow_runs SQLite table with indexes for efficient querying
  • Cron trigger validation (exactly one trigger source, valid overlap policy)
  • Supported overlap policies: skip, queue, parallel (default: skip)

Schema additions:

  • workflow_runs table with status tracking (pending, running, completed, failed, skipped, timed_out, canceled)
  • Indexes on workflow+workspace, status, and claw_id for dashboard queries

- Add CronWorkflowTrigger type with schedule, timezone, overlap_policy, timeout
- Add WorkflowRun type for execution history tracking
- Add workflow_runs DB table with indexes
- Validate cron triggers in workflow validation (exactly one trigger source)
- Support overlap policies: skip, queue, parallel
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat: add cron workflow trigger and work..." | Re-trigger Greptile

Comment thread pkg/types/validation.go
Comment thread pkg/types/validation.go
Comment thread pkg/types/workspace.go Outdated
Comment thread pkg/hub/db.go
Claw added 3 commits June 9, 2026 14:28
- Fix workflow_normalize.go syntax error (extra closing brace)
- Fix cron_scheduler.go to use pointer iteration over ws.Workflows
- Fix cron_scheduler.go to use cs.cron.Schedule() correctly (returns EntryID only)
- Remove unused context import from cron_scheduler.go
- Remove unused types import from cron_api.go
- Wire cron scheduler start/stop into server lifecycle
- ws.Workflows is []*WorkflowConfig, so iteration by value gives *WorkflowConfig
- Remove incorrect &ws.Workflows[i] which created **WorkflowConfig
- Same fix for both reloadWorkflows() and manualTrigger()
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Comments Outside Diff (2)

  1. pkg/hub/cron_scheduler.go, line 521-537 (link)

    P1 RunContext scan will always fail at runtime

    types.WorkflowRun.RunContext is map[string]interface{}, but the database column run_context is TEXT storing a JSON string. database/sql's Scan does not support assigning a string/[]byte column value directly into map[string]interface{} — it will return "unsupported Scan, storing driver.Value type []uint8 into type *map[string]interface{}" for every row. Any call to getRunHistory against a non-empty table will return an error rather than results.

    The fix is to scan run_context into a string (or []byte) variable and then json.Unmarshal it into r.RunContext after the scan.

  2. pkg/hub/cron_scheduler.go, line 437-447 (link)

    P1 finishRun is defined but never called

    finishRun sets status = 'completed' and writes finished_at, but there is no call site in the codebase — runWorkflow never invokes it after the claw completes (or fails). All runs started by the scheduler will remain in status = 'running' indefinitely, making the workflow_runs table's history unreliable and the completed/failed statuses effectively unreachable.

Reviews (2): Last reviewed commit: "fix: correct pointer iteration in cron s..." | Re-trigger Greptile

@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Reviews (3): Last reviewed commit: "fix: update cron test to expect timezone..." | Re-trigger Greptile

Comment thread pkg/hub/cron_scheduler.go
- go mod tidy promoted robfig/cron from indirect to direct
- Removed stale testcontainers dependencies no longer used in codebase
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Reviews (4): Last reviewed commit: "fix: run go mod tidy to make robfig/cron..." | Re-trigger Greptile

- Fix RunContext scan: scan into string then json.Unmarshal into map[string]interface{}
- Add finishRunByClawID to mark runs completed/failed/canceled by claw ID
- Wire finishRunByClawID into all claw terminal status paths:
  * idle (handleClawDoneSignal) -> completed
  * manual kill (server.go) -> canceled
  * error (stopAgentWithReason) -> failed
  * issue left trigger (github_issues.go, linear.go) -> canceled
  * PR merged (pr_watcher.go) -> completed
  * pipeline terminal (pipeline_runner.go) -> completed
  * factory trigger failures (github_issues, linear, shortcut, external) -> failed
  * factory trigger reclaimed (factory_triggers.go) -> canceled
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Reviews (5): Last reviewed commit: "fix: address Greptile review comments on..." | Re-trigger Greptile

- recordRun now accepts runID as first parameter instead of generating a new one
- Fixes updateRun always matching zero rows because runID was never in DB
- Fixes error path leaving 'running' row stuck forever while inserting new 'failed' row
- Skipped runs still generate their own runID (no updateRun needed for them)
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Comments Outside Diff (1)

  1. pkg/hub/cron_api.go, line 329-333 (link)

    P1 All manualTrigger errors returned as 404

    manualTrigger can fail for two distinct reasons: (1) the workflow is genuinely not found/not cron-triggered (a 404), or (2) loadAllWorkspaces() hits a database error (which should be a 500). Today both cases return http.StatusNotFound, so an API client that receives a 404 cannot tell whether the workflow doesn't exist or the database is unavailable. The correct fix is to distinguish the two error classes — returning 500 for infrastructure errors and 404 only for genuine "not found" cases.

Reviews (6): Last reviewed commit: "fix: recordRun must use caller-provided ..." | Re-trigger Greptile

- Add cronTriggerNotFoundError type for 'not found' / 'not cron-triggered' cases
- manualTrigger returns typed error for 404 cases, plain error for DB failures
- API handler returns 404 for not-found, 500 for infrastructure errors
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Reviews (7): Last reviewed commit: "fix: distinguish 404 vs 500 errors in ma..." | Re-trigger Greptile

Comment thread pkg/hub/cron_scheduler.go Outdated
- Change running map from bool to int (counter)
- Increment counter when run starts, decrement when it finishes
- Check > 0 instead of true for active runs
- Prevents parallel run defer from corrupting tracker when another run is still active
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Reviews (8): Last reviewed commit: "fix: use reference counter for running m..." | Re-trigger Greptile

Comment thread pkg/hub/cron_scheduler.go
Addresses Greptile review comment: the error path was calling recordRun
again with the same runID, which caused a primary key conflict and left
the run stuck in 'running' status forever.

Now uses failRun() which UPDATEs the existing row with status='failed',
result message, and finished_at timestamp.
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Reviews (9): Last reviewed commit: "fix: use failRun instead of recordRun on..." | Re-trigger Greptile

Comment thread pkg/hub/cron_scheduler.go
Claw added 2 commits June 9, 2026 19:30
The running counter was decremented immediately after claw creation,
making skip/queue overlap policies ineffective since the claw was still
running when the next cron firing occurred.

Now the counter stays elevated until finishRunByClawID is called when the
claw reaches a terminal status (idle, deleted, error). A clawWorkflow map
tracks clawID -> workflow key for this purpose.

Addresses Greptile review comment about overlap enforcement being a no-op.
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Reviews (10): Last reviewed commit: "merge: resolve conflicts with main" | Re-trigger Greptile

Comment thread pkg/hub/cron_scheduler.go Outdated
…olicy

runWorkflow now returns a bool indicating whether a run was actually started.
When overlap policy causes a skip, manualTrigger returns a cronTriggerSkippedError
which the API handler translates to HTTP 409 Conflict instead of 200 OK.

Addresses Greptile review comment about manualTrigger silently returning success
when the run is skipped.
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Reviews (11): Last reviewed commit: "fix: manualTrigger returns error when ru..." | Re-trigger Greptile

Comment thread pkg/hub/cron_scheduler.go Outdated
Comment thread pkg/hub/cron_scheduler.go
@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Comments Outside Diff (1)

  1. pkg/hub/pipeline_runner.go, line 1372-1373 (link)

    P1 Pipeline terminal stage always reported as "completed"

    transitionPipelineStageWithContext marks a workflow run as "completed" whenever a pipeline reaches any terminal stage, even error terminals. If stopAgentWithReason is subsequently called for the same claw it will attempt to set status = 'failed', but the WHERE status = 'running' guard will prevent the update since the row is already "completed". A pipeline claw that fails via an error terminal stage will therefore be permanently recorded as "completed" in workflow_runs. Consider passing the terminal stage's exit status so the finishRunByClawID call can distinguish success from failure.

Reviews (12): Last reviewed commit: "Address cron workflow review feedback" | Re-trigger Greptile

@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Reviews (13): Last reviewed commit: "Mark failed pipeline terminals in workfl..." | Re-trigger Greptile

@marccampbell marccampbell merged commit 499bc21 into main Jun 9, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants