feat(cron): add conversational update tool - #1057
Conversation
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9535a45d26
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ExecConfig: args.ExecConfig, | ||
| TimeoutSec: args.TimeoutSec, | ||
| Tags: args.Tags, | ||
| ExpectedUpdatedAt: expectedUpdatedAt, |
There was a problem hiding this comment.
Enforce stale tokens in the embedded adapter
When HARNESS_CRON_URL is unset, buildCronBootstrap supplies embeddedCronAdapter as this client, but that adapter's UpdateJob never examines req.ExpectedUpdatedAt. If a job changes after cron_get, an update carrying the stale timestamp therefore still succeeds and overwrites the newer row in the default embedded deployment; enforce the token with an atomic conditional store update on this path.
Useful? React with 👍 / 👎.
| writeError(w, http.StatusBadRequest, "invalid_json", err.Error()) | ||
| return | ||
| } | ||
| if req.ExpectedUpdatedAt != nil && !job.UpdatedAt.Equal(req.ExpectedUpdatedAt.UTC()) { |
There was a problem hiding this comment.
Compare the version atomically with the write
When two remote PATCH requests read the same job version before either writes, both pass this check, and SQLiteStore.UpdateJob later updates solely by job_id, allowing the second full-row write to silently overwrite the first despite its supplied token. Put the expected timestamp predicate in the update transaction or WHERE clause and return 409 when no row matches.
Useful? React with 👍 / 👎.
| "schedule": map[string]any{"type": "string", "description": "New 5-field UTC cron expression"}, | ||
| "command": map[string]any{"type": "string", "description": "New shell command; encoded as execution_config"}, | ||
| "execution_config": map[string]any{"type": "string", "description": "New execution config JSON"}, | ||
| "timeout_seconds": map[string]any{"type": "integer", "description": "New timeout in seconds"}, |
There was a problem hiding this comment.
Reject nonpositive timeout updates
When the model supplies timeout_seconds as zero or a negative number, the schema and handler accept it and both update paths persist it unchanged. ShellExecutor derives its execution deadline from this value, so subsequent runs time out immediately; require a minimum of one second in the schema and runtime validation.
Useful? React with 👍 / 👎.
| - Full-gate result: `./scripts/test-regression.sh` reaches all packages but | ||
| fails only in the pre-existing real-Keychain tests | ||
| `TestKeychainRoundTripAgainstRealKeychain` and | ||
| `TestSavingAKeyForAnExistingProviderMakesItUsable`; the child `security` | ||
| helper prompts and is killed after 15 seconds. Running those two tests in | ||
| isolation passes. Do not report the full regression gate as green until | ||
| that environment/test-harness failure is resolved. |
There was a problem hiding this comment.
Keep this change blocked until regression is green
The added verification evidence explicitly records that ./scripts/test-regression.sh remains red, so this commit lacks the repository-required full-gate proof. The repository treats any failing regression, including a pre-existing failure, as a blocker; resolve the Keychain failure and rerun the complete gate before accepting this change.
AGENTS.md reference: AGENTS.md:L32-L35
Useful? React with 👍 / 👎.
179e395 to
9e20e53
Compare
|
@codex review Please review exact post-rebase head Do not treat #1054 or #1064 as waived: both upstream fixes must land, be rebased here, and hosted checks must pass before merge. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e20e53c0b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if job.Status == StatusPaused { | ||
| s.scheduler.RemoveJob(job.ID) | ||
| } else if err := s.scheduler.UpdateJobSchedule(job); err != nil { | ||
| writeError(w, http.StatusInternalServerError, "scheduler_error", err.Error()) |
There was a problem hiding this comment.
Serialize scheduler reconciliation with the CAS write
When two updates overlap, writer A can commit version A, writer B can then read and commit version B and reconcile the scheduler, and writer A can finally execute this call with its stale snapshot. If either update changes the schedule—or if a delete occurs between A's CAS and reconciliation—the live scheduler is left on the older schedule or re-registers a deleted job even though persistence contains the newer state. The mirrored embedded path has the same ordering; serialize reconciliation per job or verify/reload the committed version before applying it.
AGENTS.md reference: AGENTS.md:L57-L61
Useful? React with 👍 / 👎.
| if job.Status == StatusPaused { | ||
| s.scheduler.RemoveJob(job.ID) | ||
| } else if err := s.scheduler.UpdateJobSchedule(job); err != nil { | ||
| writeError(w, http.StatusInternalServerError, "scheduler_error", err.Error()) |
There was a problem hiding this comment.
Avoid rescheduling jobs for non-schedule edits
When an active job receives only a prompt, command, timeout, or tag update, this branch still calls UpdateJobSchedule, which removes and re-adds the cron entry. NextRunAt is not recomputed for those requests, so the returned and persisted next-run value can describe the old entry while the live scheduler computes from the re-registration time, potentially skipping an imminent occurrence. Since fireJob already reloads the current record, only schedule or status transitions should replace the live entry.
AGENTS.md reference: AGENTS.md:L57-L61
Useful? React with 👍 / 👎.
9e20e53 to
2709fa1
Compare
Closes #1002
Outcome
Completes model-facing conversational cron CREATE, READ, UPDATE, PAUSE, RESUME, HISTORY, and DELETE while preserving legacy shell jobs. Harness jobs are immutably bound to the active tenant, conversation, and agent, and scheduled fires start new runs in that same persisted conversation.
This promotion candidate is
2709fa1a, based onorigin/main3506e01c997231c46920b45bf8947c50087dd863.Scope and architecture
NewDefaultRegistryWithOptions, covering embedded and remote adapters without changing raw operator access.GET /v1/jobs/by-name?name=...and returns a typed ambiguity error.updated_atversion returned bycron_getfor update, pause, resume, and delete.Persistence and scheduler correctness
(tenant_id, conversation_id, agent_id, name).Prepare-> durable CAS -> infallibleCommit, retaining the old live/durable state when prepare or CAS fails.Validation and provider compatibility
cron_createexposes a provider-compatible object-root schema; runtime validation retains the shell/harness exclusivity contract without top-leveloneOf/anyOf/allOf.Test-first evidence
Deterministic red tests covered cross-scope access, same-name isolation, stale update/pause/resume/delete, duplicate scheduler entries, registration failure, scheduler/store divergence, update/delete races, stale callback admission, semantic migration detection, ambiguous operator lookup, history unavailability, and provider schema rejection. Each red was observed before its repair.
Focused normal and race packages pass:
The authoritative logged-in foreground gate passed on exact candidate
2709fa1a:Real provider and conversation proof
A real
harnessdcanary using OpenAIgpt-4.1-miniexercised all eight model-facing cron tools in one persisted conversation:assistant.messageinto conversation SSE and appeared in the same transcript.cron_get/history returned both executions with linked run IDs.jobs: []and HTTP 404 for the former ID.This proves the harness/API conversation path for #1002. Real PTY TUI, rendered macOS GUI, remote cronsd authentication/dispatch, terminal run linkage/no-overlap, and callback behavior remain separate epic children and are not claimed here.
Rollout and rollback
Back up the stopped SQLite database before first startup. The migration is atomic and idempotent; verify row counts, execution references,
integrity_check, andforeign_key_checkbefore accepting traffic. Roll back with a compatibility-capable prior binary while retaining the migrated schema. Do not downgrade to global name uniqueness when cross-scope duplicates exist.Promotion checklist
main