feat(schedule): add Temporal schedule for periodic scanning#4
feat(schedule): add Temporal schedule for periodic scanning#4bakayolo merged 7 commits intoblock:mainfrom
Conversation
🤖 Automated Expert ReviewSummaryAdds Temporal Schedule API support for periodic orchestrator scanning with a solid create-or-update pattern. Well-structured code with good test coverage, but has a potential nil pointer panic in the update path and missing startup timeout. Findings🚫 BLOCKER: Nil pointer dereference in DoUpdate closureFile: if input.Description.Schedule.Spec == nil {
input.Description.Schedule.Spec = &client.ScheduleSpec{}
}
|
| Check | Status | Notes |
|---|---|---|
| 🔒 Security | ✅ | No secrets, credentials, or injection vectors |
| 🏗️ Reliability | Nil pointer risk in DoUpdate; missing startup timeout | |
| 🚀 Staging before Prod | N/A | Feature flag gated (SCHEDULE_ENABLED=false default) |
| 📦 Atlantis Plan | N/A | Not an infrastructure PR |
| 🧪 Tests | 6 tests cover happy paths well; missing nil Spec edge case | |
| ⚙️ CI Checks | 🚫 | "Test" check is failing (1m49s) |
| 💰 Cost/Blast Radius | ✅ | Disabled by default, low risk |
Verdict: REQUEST CHANGES
The BLOCKER (nil pointer panic in DoUpdate) must be fixed before merge. The CI test failure also needs investigation. The two WARNINGs (partial update propagation, missing startup timeout) should ideally be addressed but are not strictly blocking.
🤖 Review by Amp agent · not a substitute for human review
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
Adds Temporal Schedule API support so the OrchestratorWorkflow runs automatically on a configurable cron (default: every 6 hours). The schedule manager uses a create-or-update pattern to handle restarts gracefully. Disabled by default via SCHEDULE_ENABLED=false. New files: - pkg/schedule/schedule.go: Schedule manager with create-or-update logic - pkg/schedule/schedule_test.go: 6 unit tests covering all paths Modified: - cmd/server/main.go: Added SCHEDULE_ENABLED, SCHEDULE_CRON, SCHEDULE_ID, SCHEDULE_JITTER config; wiring with graceful failure - pkg/workflow/orchestrator/workflow.go: ScanID fallback from workflow execution ID for scheduled runs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Aligns with the design doc which specifies daily at 06:00 UTC, not every 6 hours. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add nil guard for Spec in DoUpdate closure to prevent panic - Propagate TaskQueue changes in schedule updates via Action assertion - Add 10s timeout on EnsureSchedule during startup - Add TestEnsureSchedule_AlreadyExists_NilSpec test case Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename ScheduleConfig -> Config, ScheduleCreator -> Creator (revive) - Reorder mockScheduleHandle fields for alignment (govet) - Add nolint for hugeParam on mock Create (matches SDK interface) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Handle Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
84254e0 to
bd5d234
Compare
All findings from this review have been addressed:
Follow-up lint fixes in Branch has been rebased onto latest main with clean linear history (no merge commits). All tests and lints pass. |
* block/main: feat: retrieve all Wiz tags instead of subset (block#20) feat: local endoflife.date override for pending upstream PRs (block#19) feat: add OpenSearch resource type (block#9) feat: implement generic config-driven approach for managing resources (block#18) # Conflicts: # cmd/server/main.go
Temporal parses CronExpressions into Calendars server-side on create. On subsequent describes, the cron lives in Calendars and CronExpressions comes back empty. Mutating only CronExpressions on update left the stale Calendars in place, causing the schedule to fire on both the old and new cadences after every restart with a changed cron. Fix by replacing the entire Spec on update instead of mutating fields. Also wires SCHEDULE_* env vars through docker-compose so the feature can be exercised locally end-to-end. Adds a regression test that simulates the real Temporal describe response (cron parsed into Calendars, CronExpressions empty) and asserts Calendars is cleared on update. Verified end-to-end against temporal dev server: after create then restart with a different cron, the schedule now has a single calendar entry matching the new cron (previously had both old and new stacked).
Document SCHEDULE_* env vars, usage examples, and the create-or-update pattern for the Temporal Schedule API integration added in #4. Amp-Thread-ID: https://ampcode.com/threads/T-019d98f0-cc82-75bf-b664-e3a63eef6ee9 Co-authored-by: Amp <amp@ampcode.com>
The Temporal schedule feature was merged in #4 but the README had no documentation for it. Adds: - `SCHEDULE_*` env vars to the configuration table - Usage examples for enabling and customizing the cron schedule - Verification commands (`temporal schedule list/describe`) - Note about the create-or-update pattern for safe restarts Co-authored-by: Amp <amp@ampcode.com>
Summary
SCHEDULE_ENABLED=falsefor backwards compatibilitySCHEDULE_*env vars throughdocker-compose.yamlso the feature can be toggled for local end-to-end testingConfiguration
SCHEDULE_ENABLEDfalseSCHEDULE_CRON0 6 * * *SCHEDULE_IDversion-guard-scanSCHEDULE_JITTER5mChanges
New:
pkg/schedule/schedule.go— Schedule manager with create-or-update logicpkg/schedule/schedule_test.go— 8 unit tests covering all pathsModified:
cmd/server/main.go— Config fields, schedule wiring with graceful failurepkg/workflow/orchestrator/workflow.go— ScanID fallback from workflow execution ID for scheduled runsdocker-compose.yaml—SCHEDULE_*env var passthroughBug fixed during review
Initial implementation mutated only
CronExpressionson the update path. Temporal parsesCronExpressionsinto structuredCalendarsserver-side on create, so subsequent describes return the cron insideCalendarswithCronExpressionsempty. Mutating onlyCronExpressionsleft the stale calendar in place, causing the schedule to fire on both the old and new crons after every restart with a changed cron.Fixed by replacing the entire
Specon update. Regression testTestEnsureSchedule_Update_ReplacesStaleCalendarssimulates the real Temporal describe response and assertsCalendarsis cleared.Test plan
go build ./...compilesgo test ./pkg/schedule/...— 8/8 tests passgo test ./...— full suite passesgolangci-lint run ./pkg/schedule/...cleantemporal server start-dev:SCHEDULE_ENABLED=true SCHEDULE_CRON="0 6 * * *"→temporal schedule describeshows one calendar entry for 06:00SCHEDULE_CRON="*/30 * * * *"→ server logsSchedule updated,temporal schedule describeshows a single calendar entry matching*/30(stale entry correctly cleared)temporal workflow list --query 'WorkflowType = "OrchestratorWorkflow"'confirms scheduled runs fire on cron boundariesReproducing locally
Note:
docker-compose upworks too onceSCHEDULE_ENABLEDis exported, but the bundled Temporal image currently needs a fix onmainbefore compose boots a healthy server — out of scope for this PR.🤖 Generated with Claude Code