feat(legacy-meter): scale retirement quiet period to call volume
A clean review window only proves an interface was silent for that window. For a heavily used interface that is weak evidence -- an infrequent caller can fall outside the window entirely, so the interface reads as retirable while still having consumers. _retirement_state now also requires silence since last_seen_at, scaled by all-time volume (RETIREMENT_QUIET_LADDER): 7d under 100 calls, 30d under 10k, 60d above. Existing guards (retired / manual hold / replacement ref / replacement verified / in-window traffic) still take precedence. On the 2026-08-20 capture this makes 15 of 19 legacy interfaces retirable and holds 4: the three six-figure /workstreams/ read paths, and GET /tasks/?workstream_id which still has live traffic. Unblocks the bulk of Wave E2 in the cutover slice plan. 7 new tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
be7f2632c3
commit
93e95f93e7
4 changed files with 229 additions and 19 deletions
|
|
@ -305,7 +305,13 @@ def _summarize_interface(
|
|||
]
|
||||
window = _counters(window_buckets)
|
||||
last_seen = max((bucket.last_seen_at for bucket in buckets), default=None)
|
||||
retirement_candidate, reason = _retirement_state(interface, window.calls)
|
||||
retirement_candidate, reason = _retirement_state(
|
||||
interface,
|
||||
window.calls,
|
||||
all_time_calls=all_time.calls,
|
||||
last_seen_at=last_seen,
|
||||
window_end=window_end,
|
||||
)
|
||||
return LegacyInterfaceSummary(
|
||||
interface=LegacyInterfaceRead.model_validate(interface),
|
||||
all_time=all_time,
|
||||
|
|
@ -340,7 +346,34 @@ def _bucket_counts(buckets: list[LegacyInterfaceUsageBucket], bucket_kind: str)
|
|||
return counts
|
||||
|
||||
|
||||
def _retirement_state(interface: LegacyInterface, window_calls: int) -> tuple[bool, str]:
|
||||
# Minimum quiet period before an interface may be retired, scaled to how much
|
||||
# traffic it ever carried. A review window only proves the interface was silent
|
||||
# for that window; for a high-volume interface that is weak evidence, since a
|
||||
# monthly or quarterly caller can easily fall outside it. Thresholds are
|
||||
# (all-time calls, minimum days since last_seen_at).
|
||||
RETIREMENT_QUIET_LADDER: tuple[tuple[int, int], ...] = (
|
||||
(10_000, 60),
|
||||
(100, 30),
|
||||
(1, 7),
|
||||
)
|
||||
|
||||
|
||||
def _required_quiet_days(all_time_calls: int) -> int:
|
||||
"""Days of silence required before retirement, by all-time call volume."""
|
||||
for threshold, days in RETIREMENT_QUIET_LADDER:
|
||||
if all_time_calls >= threshold:
|
||||
return days
|
||||
return 0
|
||||
|
||||
|
||||
def _retirement_state(
|
||||
interface: LegacyInterface,
|
||||
window_calls: int,
|
||||
*,
|
||||
all_time_calls: int = 0,
|
||||
last_seen_at: datetime | None = None,
|
||||
window_end: datetime | None = None,
|
||||
) -> tuple[bool, str]:
|
||||
if interface.status == "retired":
|
||||
return False, "already retired"
|
||||
if interface.manual_hold:
|
||||
|
|
@ -351,6 +384,19 @@ def _retirement_state(interface: LegacyInterface, window_calls: int) -> tuple[bo
|
|||
return False, "replacement not verified"
|
||||
if window_calls > 0:
|
||||
return False, f"{window_calls} call(s) in review window"
|
||||
|
||||
required_days = _required_quiet_days(all_time_calls)
|
||||
if required_days and last_seen_at is not None and window_end is not None:
|
||||
quiet_days = (window_end - _ensure_datetime(last_seen_at)).days
|
||||
if quiet_days < required_days:
|
||||
return False, (
|
||||
f"quiet {quiet_days}d of {required_days}d required "
|
||||
f"for {all_time_calls} all-time call(s)"
|
||||
)
|
||||
return True, (
|
||||
f"no usage in review window; quiet {quiet_days}d "
|
||||
f"(>= {required_days}d required for {all_time_calls} all-time call(s))"
|
||||
)
|
||||
return True, "no measured usage in review window"
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue