mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-13 19:43:27 +02:00
* fix(dashboard): count cancelled runs in usage run time (MUL-5823)
CancelAgentTask accepts a task in 'running', so a cancelled row can carry
both started_at and completed_at — real agent occupancy. The run-time
rollups filtered on `status IN ('completed','failed')`, so every run the
user stopped mid-flight contributed 0 seconds and 0 to the task count.
The cost side has no status filter at all (UpsertTaskUsage and the hourly
rollup ignore status), so Cost/Tokens counted those runs while Time/Tasks
did not — two different task populations on the same dashboard, diverging
further the more runs get stopped.
Widen both run-time queries to include 'cancelled' and report it as a
third outcome alongside failed. The existing `started_at IS NOT NULL`
guard keeps a run cancelled while still queued out: it never occupied an
agent. The failure rollups keep the two-status filter on purpose — a
manual stop is not a failure and must not dilute the error rate.
Migrations 261/262 swap the supporting partial index to a predicate that
covers the third status; without that the widened filter can no longer
use it and the rollups fall back to a full table scan.
Co-authored-by: multica-agent <github@multica.ai>
* fix(migrate): guard the 261/262 index swap against an interrupted build
An interrupted CREATE INDEX CONCURRENTLY leaves an INVALID index behind.
`IF NOT EXISTS` then reports success on retry without rebuilding it, the
runner records 261 as applied, and 262 drops the still-valid v1 — leaving
every dashboard rollup on a full table scan.
Register cleanupInvalidConcurrentIndexHook for 261, the same guard
migration 257 already uses for the same hazard.
The down path needs different handling: hooks only run in the `up`
direction, so 262.down drops IF NOT EXISTS and fails closed instead —
matching what 258.down does for the 257/258 pair.
Adds a regression test covering both. Unlike 257's unique index this one
cannot be failed with a duplicate row, so the build is interrupted the way
a real one is: a concurrent open transaction blocks the wait phase until
statement_timeout cancels it. The test asserts the bare retry is a silent
no-op, that the hook repairs it, and that 262 only drops v1 once v2 is
valid.
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: Bohan-J <bohan@devv.ai>
Co-authored-by: multica-agent <github@multica.ai>