Add scheduled task creation, and stop a bad cron unscheduling everything
Build App / compute-version (pull_request) Successful in 4s
Build Container / build-container (pull_request) Successful in 9m35s
Build App / build-linux (pull_request) Successful in 5m35s
Build App / build-windows (pull_request) Failing after 2m26s
Build App / build-macos (pull_request) Successful in 2m49s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
Build App / compute-version (pull_request) Successful in 4s
Build Container / build-container (pull_request) Successful in 9m35s
Build App / build-linux (pull_request) Successful in 5m35s
Build App / build-windows (pull_request) Failing after 2m26s
Build App / build-macos (pull_request) Successful in 2m49s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
Completes the Automation tab: it could list, toggle, run, log and remove tasks but not create them, so task creation still meant dropping to the CLI. Adds add_scheduled_task and update_scheduled_task, plus a task editor with cron presets and a plain-English reading of the expression. Every field is free user text, so all of it goes to the scheduler as a bare argv vector through bollard — no shell, no quoting. Validation is shape-only rather than metacharacter scrubbing: length caps, no control characters in single-line fields, no leading-dash name, absolute working_dir. Verified by round-tripping a prompt containing `; rm -rf /`, `$(id)`, backticks and newlines: it landed byte-for-byte in the task JSON with nothing executed. The scheduler CLI has no `edit`, so update is add-then-remove with the add first — a rejected edit leaves the original intact. The new id is surfaced in the editor rather than hidden. Root-cause fix, and the more serious half of this commit: triple-c-scheduler never validated --schedule, and rebuild_crontab regenerates the entire crontab and pipes it to `crontab`, which rejects the whole file if any line is malformed — with the error thrown away by `2>/dev/null || true`. A single bad schedule therefore silently unscheduled every other task in the container while reporting success. Reproduced directly. It matters because the global CLAUDE.md tells Claude to drive this CLI, so Claude could trigger it unprompted. `add` now validates the expression and exits non-zero, and rebuild_crontab reports a rejected crontab instead of swallowing it, keeping the offending file for inspection. Verified against the real CLI in this container: a bad schedule is refused without disturbing an existing task's crontab entry, and `0 9 * * 1-5`, `*/30 * * * *`, `0,30 8-17 * * *` and `0 0 1 1 *` are all still accepted. The Rust layer validates independently, agreeing with vixie cron on 23 probed expressions including `1/2` and `*/0` being invalid. 121 frontend tests, 44 Rust tests, both builds clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+27
-4
@@ -197,16 +197,39 @@ the config volume by the entrypoint. Generalizes the pattern the MCP tab was rea
|
||||
(holding `~/.claude`). README.md, HOW-TO-USE.md, and CLAUDE.md all still state that
|
||||
OAuth tokens survive a Reset. Pre-existing; not yet corrected.
|
||||
|
||||
3. **Stale model placeholders** — see "Not yet scheduled" above.
|
||||
3. **An invalid cron expression silently unscheduled every task.** Found while adding
|
||||
task creation to the Automation tab, and the most serious bug in this review.
|
||||
`triple-c-scheduler` never validated `--schedule`, and `rebuild_crontab` regenerates the
|
||||
*entire* crontab and pipes it to `crontab`, which rejects the whole file if any single
|
||||
line is malformed — with the error discarded by `2>/dev/null || true`. So one bad
|
||||
schedule silently unscheduled every other task in the container, reporting success.
|
||||
Reproduced directly. This mattered because the global CLAUDE.md instructs Claude to use
|
||||
this CLI, so Claude itself could trigger it. Fixed at the root: `add` now validates the
|
||||
expression and exits non-zero, and `rebuild_crontab` reports a rejected crontab instead
|
||||
of swallowing it. The Rust `add_scheduled_task` command validates independently.
|
||||
|
||||
4. **Silent save failures.** Project config saves on blur; failures go only to
|
||||
`console.error`. No user-visible indication. Fixed in Phase 3 — `useProjectSave`
|
||||
now renders a Saved / Saving / Save failed indicator and raises a toast.
|
||||
4. **Reset was destructive with no confirmation.** It deletes both volumes — the login,
|
||||
installed skills, all session transcripts — from a single unconfirmed click, while the
|
||||
comparably destructive Remove already confirmed. Now gated by a dialog that names each
|
||||
loss. Fixed.
|
||||
|
||||
5. **Cancelling authentication did not cancel.** Fixed — see the handoff section above.
|
||||
|
||||
6. **Stale model placeholders** — see "Not yet scheduled" above.
|
||||
|
||||
7. **Silent save failures.** Project config saves on blur; failures went only to
|
||||
`console.error`. Fixed in Phase 3 — `useProjectSave` now renders a
|
||||
Saved / Saving / Save failed indicator and raises a toast.
|
||||
|
||||
---
|
||||
|
||||
## Known gaps left by Phase 2–3
|
||||
|
||||
- **Editing a scheduled task changes its id.** `triple-c-scheduler` has no `edit`
|
||||
subcommand, and hand-editing its JSON behind its back would desync the crontab, so edit is
|
||||
implemented as add-then-remove. The add runs first, so a rejected edit leaves the original
|
||||
intact. The task gets a new id and its older logs stay under the old one; the editor says
|
||||
so before saving.
|
||||
- **`open_terminal_session` takes no command argument.** "Resume session" and
|
||||
"Manage in terminal" therefore open a bash tab and *type* the command after a
|
||||
fixed prompt delay. It works, but it is timing-dependent and will misfire on a
|
||||
|
||||
Reference in New Issue
Block a user