feat(profiler/behave_shell): emit temporal.session_duration
Bucket ctx.duration_s against SESSION_DURATION_SHORT_MAX (60s) / MEDIUM_MAX (600s) / LONG_MAX (3600s); else marathon. Direct measurement, confidence 0.85. Skip emission only when no commands and zero duration. New _features/temporal.py module opens Phase E.
This commit is contained in:
@@ -24,6 +24,9 @@ from decnet.profiler.behave_shell._features.cognitive import (
|
||||
inter_command_consistency,
|
||||
inter_command_latency_class,
|
||||
)
|
||||
from decnet.profiler.behave_shell._features.temporal import (
|
||||
session_duration,
|
||||
)
|
||||
from decnet.profiler.behave_shell._features.motor import (
|
||||
command_chunking,
|
||||
error_correction,
|
||||
@@ -59,4 +62,5 @@ FEATURES: tuple[FeatureFn, ...] = (
|
||||
error_resilience_retry_tactic,
|
||||
error_resilience_frustration_typing,
|
||||
error_resilience_fallback_to_man,
|
||||
session_duration,
|
||||
)
|
||||
|
||||
50
decnet/profiler/behave_shell/_features/temporal.py
Normal file
50
decnet/profiler/behave_shell/_features/temporal.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""``temporal.*`` feature functions — per-session subset.
|
||||
|
||||
Phase E ships the four ``temporal.*`` primitives that don't need
|
||||
observation history. The other three (``session_timing``,
|
||||
``persistence``, ``lifecycle_markers.idle_periodicity``) are Tier B
|
||||
and computed by the attribution engine, not the extractor.
|
||||
|
||||
Step E.1: ``temporal.session_duration``.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Iterator
|
||||
|
||||
from decnet_behave_core.spec.envelope import Observation
|
||||
|
||||
from decnet.profiler.behave_shell._ctx import SessionContext
|
||||
from decnet.profiler.behave_shell._features._emit import make_observation
|
||||
from decnet.profiler.behave_shell._thresholds import (
|
||||
SESSION_DURATION_LONG_MAX,
|
||||
SESSION_DURATION_MEDIUM_MAX,
|
||||
SESSION_DURATION_SHORT_MAX,
|
||||
)
|
||||
|
||||
|
||||
def session_duration(ctx: SessionContext) -> Iterator[Observation]:
|
||||
"""Emit ``temporal.session_duration`` ∈ {short, medium, long, marathon}.
|
||||
|
||||
Direct measurement off ``ctx.duration_s``. Skip emission only when
|
||||
the session has neither commands nor any duration to speak of —
|
||||
a one-event session with ``duration_s == 0`` and no commands has
|
||||
nothing honest to bucket. Confidence is high — duration is a fact,
|
||||
not an inference.
|
||||
"""
|
||||
if ctx.duration_s <= 0.0 and not ctx.commands:
|
||||
return
|
||||
d = ctx.duration_s
|
||||
if d < SESSION_DURATION_SHORT_MAX:
|
||||
value = "short"
|
||||
elif d < SESSION_DURATION_MEDIUM_MAX:
|
||||
value = "medium"
|
||||
elif d < SESSION_DURATION_LONG_MAX:
|
||||
value = "long"
|
||||
else:
|
||||
value = "marathon"
|
||||
yield make_observation(
|
||||
ctx,
|
||||
primitive="temporal.session_duration",
|
||||
value=value,
|
||||
confidence=0.85,
|
||||
)
|
||||
Reference in New Issue
Block a user