mirror of
https://github.com/simstudioai/sim.git
synced 2026-11-05 00:02:28 +08:00
* fix(logs): record how long a cancelled run had been going A cancelled run got an end timestamp and no duration. Every other terminal transition writes both — the completion path sets them together, and a paused run already records its elapsed time — but cancellation writes the log row directly rather than through completion, so it had no in-memory duration to store and simply omitted the column. That is not cosmetic. `GET /api/v2/logs` filters on `minDurationMs` and `maxDurationMs`, and a null column drops the row out of every such query, so cancellations are invisible to exactly the searches someone runs when investigating cancellations. The published contract also says the end timestamp is null only while a run is active, which a cancelled run is not. Both cancellation writes now derive the duration in the same statement from the row's own `started_at`, through one shared expression so the two cannot drift apart the way they did from the completion path. The end instant is computed once and reused, so the stamped end and the derived duration describe the same moment rather than two clock reads. The instant is bound as an explicit `timestamp` rather than a `Date`: `started_at` is `timestamp without time zone` holding a UTC wall clock, and a driver-bound date would infer `timestamptz` and make the interval depend on the session zone. The floor of one millisecond matches the completion path, so a run cancelled inside its first millisecond still records that it ran. * fix(logs): saturate the cancelled-run duration at the column ceiling The column is `integer`, so an untimed run cancelled after roughly twenty-five days overflowed the cast. That cost more than the duration it was recording: the direct write is caught and logged, so the row would have stayed `running` with no end timestamp at all, and the workflow-group write would have failed its transaction and taken the whole cancellation with it. Saturating keeps the terminal write. A duration wrong in its last digits is a smaller lie than a run that never ended. * fix(logs): record the duration on the other two cancellation writes Review found the first pass had only covered two of four terminal cancellation writes. The two it missed spell the timestamp `endedAt: now` rather than `endedAt: new Date()`, so the search that found the first pair could never have found them — and one of them is the common case: a workflow-group run with a live cell sidecar takes that branch, and the direct cancel skips its own log update whenever group cancellation handled the run, so it was the only writer for those cancellations. The other is the paused-cancellation write, which the first pass reported as already correct on the strength of that same search. A paused run records its duration when it pauses; cancelling it did not. All four now derive the duration the same way, and the sweep for the remaining ones went over every `status: 'cancelled'` write rather than one spelling of the timestamp beside it. * fix(logs): let a recorded duration outlive a later cancellation A paused run measures its own active duration at the pause checkpoint. The previous commit then had cancellation overwrite that with wall clock from the start, which quietly redefines the column for those runs to include the time the run spent waiting rather than working — filling a gap by discarding an answer someone else had already computed. The duration now coalesces onto whatever the row already carries, so a cancellation only supplies the value when nothing else did. Every other cancellation path leaves the column null, so the change is inert there. * fix(logs): only a paused run keeps the duration it recorded Preserving any duration already on the row was too broad. Resuming flips the log back to running and leaves the pause checkpoint value behind, so a resumed run carries a stale reading while it is accruing time again; cancelling it would have frozen that pre-resume figure and disagreed with the resume completion path, which measures wall clock. What separates the two is the row's status rather than whether the column is populated. A paused run is not accruing, so its recorded active duration stands. A running one recomputes.