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
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>
This commit is contained in:
@@ -26,6 +26,21 @@ export function formatElapsed(ms: number): string {
|
||||
return `${days}d ago`;
|
||||
}
|
||||
|
||||
/** "for 42s" / "for 4m" / "for 1h 12m" — elapsed phrasing for a run in flight.
|
||||
* Seconds are kept below a minute because the first thing anyone wants from a
|
||||
* freshly triggered run is evidence that it started at all. */
|
||||
export function formatRunningFor(iso: string | null | undefined): string | null {
|
||||
if (!iso) return null;
|
||||
const started = Date.parse(iso);
|
||||
if (Number.isNaN(started)) return null;
|
||||
const seconds = Math.max(0, Math.floor((Date.now() - started) / 1000));
|
||||
if (seconds < 60) return `for ${seconds}s`;
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
if (minutes < 60) return `for ${minutes}m`;
|
||||
const hours = Math.floor(minutes / 60);
|
||||
return `for ${hours}h ${minutes % 60}m`;
|
||||
}
|
||||
|
||||
/** Uptime phrasing for a known start timestamp. */
|
||||
export function formatUptime(startedAtMs: number | undefined): string | null {
|
||||
if (startedAtMs === undefined) return null;
|
||||
|
||||
Reference in New Issue
Block a user