Files
bvandeusenandClaude Opus 5.5 cbbf186f17
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m16s
CI / unit (push) Successful in 1m31s
CI / publish (push) Successful in 1m3s
feat(metrics): each rolled-up hour keeps the level it held, its p95 (#5738)
plugin_metrics_hourly gains value_p95 beside value_avg and value_max. A
two-minute backup spike raises the hour's peak and leaves its p95 low; an
hour spent at that level raises both. Learned normals (milestone 513) need
that difference to call a recurring spike usual without calling the same
level held at another hour usual too. Hours rolled up before this keep a
NULL p95 and read as their average.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:45:42 -04:00

27 lines
932 B
Python

"""Each hour of the metric rollup keeps the level it held, its 95th percentile,
beside its average and peak (#5738), so a learned normal can tell a brief
spike (a high peak, a low p95) from a level the metric stayed at.
Revision ID: 0044_hourly_p95
Revises: 0043_outbox_alert_words
Create Date: 2026-10-14
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = "0044_hourly_p95"
down_revision: Union[str, None] = "0043_outbox_alert_words"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Hours rolled up before this have no p95 to recover (their raw samples
# are gone), so they stay NULL and read as their average.
op.add_column("plugin_metrics_hourly", sa.Column("value_p95", sa.Float, nullable=True))
def downgrade() -> None:
op.drop_column("plugin_metrics_hourly", "value_p95")