Fix scheduled tasks failing to authenticate, and show when one is running #24

Merged
jknapp merged 3 commits from fix/scheduler-home-clobber into main 2026-08-12 13:55:54 +00:00
3 Commits
Author SHA1 Message Date
jknapp 88f2e73474 Merge branch 'main' into fix/scheduler-home-clobber
Build App (Preview) / compute-version (pull_request) Successful in 5s
Build Container / build-container (pull_request) Successful in 34s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-linux (pull_request) Canceled after 0s
Build App (Preview) / prune-previews (pull_request) Canceled after 0s
Build App (Preview) / build-macos (pull_request) Canceled after 21s
Build App (Preview) / build-windows (pull_request) Canceled after 22s
2026-08-12 13:55:37 +00:00
shadow-testandClaude Opus 5 fa4940dd7d Say when a scheduled task is running
Build App (Preview) / compute-version (pull_request) Successful in 7s
Build Container / build-container (pull_request) Successful in 2m53s
Build App (Preview) / create-release (pull_request) Successful in 5s
Build App (Preview) / build-macos (pull_request) Successful in 2m37s
Build App (Preview) / build-windows (pull_request) Successful in 6m2s
Build App (Preview) / build-linux (pull_request) Successful in 6m53s
Build App (Preview) / prune-previews (pull_request) Successful in 2s
A run is detached — cron has no terminal, and the app fires it as a detached
exec — so triggering one and watching the log was indistinguishable from
triggering one that died. Worse, `claude -p` writes its answer in a single
burst at the end, so a healthy run shows nothing but its log header for as
long as it is thinking. The honest reading of the old UI was "it stalled".

triple-c-task-runner now publishes a state file per run (pid, start time, log
path) and removes it from an EXIT trap. flock remains what actually prevents
overlapping runs; this is purely observability, so every reader verifies the
pid rather than trusting the file — a container stopped mid-run cannot fire a
trap, and a task stuck on "running" forever would be a worse lie than no
indicator at all. Stale files are cleared on read.

On top of that:

- `list` grows a status column: "running 4m12s" or "idle".
- `status [--id] [--watch]` answers "is it still going?" directly, with
  elapsed time and the tail of the log when there is any output yet.
- `run` streams the log instead of blocking silently, and refuses to start a
  task that is already running.
- The Automation tab marks a running task, disables its Run now button, and
  polls while anything is in flight — including the second or two between
  firing a run and the runner registering it, which is the exact window that
  used to read as dead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 06:33:49 -07:00
shadow-testandClaude Opus 5 9027fa9ad4 Stop the scheduler handing Claude root's HOME
Build Container / build-container (pull_request) Successful in 1m11s
Every scheduled task failed with "Not logged in · Please run /login" while
the container's OAuth credential sat there, valid, the whole time.

The entrypoint snapshots the environment into ~/.claude/scheduler/.env so
cron jobs get more than cron's minimal env. It runs as root, and HOME was
in the capture list, so the file recorded HOME=/root. The task runner then
sources that file with `set -a`, overwriting the HOME cron gave the job.
`claude -p` looks for its credential under $HOME, finds no /root/.claude,
and exits 1. Logging still worked — SCHEDULER_DIR is expanded before the
sourcing — which is why this presents as a well-formed log of a task that
never authenticated.

Drop HOME from the captured set and write it explicitly instead; cron does
still need one. Then restore HOME across the source in the task runner too:
.env lives on the home volume, so every project created before this ships
keeps a stale copy of it until its container restarts, and the runner is
what has to survive that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 06:13:59 -07:00