Files
multica/server/migrations/081_runtime_timezone.up.sql
Multica Eve d6349c16ec feat(runtime): per-runtime timezone for token-usage aggregation (MUL-1950) (#2394)
* feat: per-runtime timezone for token usage aggregation

The runtime token-usage charts (daily and hourly tabs on the
runtime-detail page) bucketed every event by the Postgres session
timezone, which is UTC in production. For an operator in UTC+8 that
meant a Tuesday afternoon's tasks landed in Tuesday early-morning's
bar — the chart was always one off.

Fix: store an IANA timezone on agent_runtime and aggregate under it.

* migrations 081 / 082 add agent_runtime.timezone (TEXT NOT NULL
  DEFAULT 'UTC') and rebuild the rollup pipeline (window function
  and both trigger functions) to compute bucket_date with
  AT TIME ZONE rt.timezone instead of bare DATE().
* No historical backfill — task_usage_daily rows already on disk
  keep their UTC bucket_date; only future writes / re-touches
  recompute under the new tz. (Product call from MUL-1950: 'guarantee
  future correctness'.)
* runtime_usage.sql gains a @tz parameter on ListRuntimeUsage and
  GetRuntimeUsageByHour and threads tz through GetRuntimeTaskHourly  Activity. ListRuntimeUsageDaily reads bucket_date as-is since the
  rollup already wrote it in tz.
* parseSinceParamInTZ replaces the raw N×24h cutoff with start-of-
  day-N in the runtime's tz so 'last 7 days' lines up with bucket
  boundaries.
* Daemon registration sends the host's IANA tz (TZ env, then
  time.Local), and UpsertAgentRuntime preserves any user override
  via a CASE-on-existing-value pattern so a daemon reconnect can't
  silently revert the operator's setting.
* New PATCH /api/runtimes/:id endpoint (UpdateAgentRuntime) lets
  the runtime detail page edit the tz; the editor seeds with the
  browser tz on first interaction.

Refs: MUL-1950

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>

* fix: harden runtime timezone rollups

Co-authored-by: multica-agent <github@multica.ai>

* fix: address runtime timezone review nits

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Eve <eve@multica.ai>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: Eve <eve@multica-ai.local>
2026-05-11 14:39:35 +08:00

20 lines
1.1 KiB
SQL

-- Per-runtime IANA timezone, used as the bucket boundary for daily and
-- hourly token-usage aggregation (see runtime_usage.sql + the rollup
-- function in 082_rollup_runtime_timezone.up.sql).
--
-- Defaults to 'UTC' so the migration is non-disruptive: pre-existing
-- runtimes keep their current UTC-bucketed semantics until an operator
-- (web UI) or a daemon (system zoneinfo on registration) overrides it.
-- All historical task_usage_daily rows stay UTC-cut; only buckets that
-- get re-touched by new task_usage events after this migration ships
-- get rebuilt under the runtime's tz. This is intentional — the product
-- decision was "guarantee future correctness, do not backfill history".
ALTER TABLE agent_runtime
ADD COLUMN timezone TEXT NOT NULL DEFAULT 'UTC';
COMMENT ON COLUMN agent_runtime.timezone IS
'IANA timezone (e.g. ''Asia/Shanghai''). Bucket boundary for per-day '
'and per-hour token usage aggregation. Defaults to UTC for runtimes '
'that existed before MUL-1950; the daemon registration / web UI '
'overwrites this with an operator-detected value going forward.';