feat(freshness): Point 別 expected interval による stale 判定 (#183)#210
Conversation
|
機能上 blocking です。新しく
そのため管理者が multiplier を 5 に変更して UI 上は保存成功しても、60秒周期の Point は期待される 300秒ではなく引き続き 180秒で stale になります。設定 UI があるのに挙動が変わらない false affordance です。ドキュメントも「N = telemetry.staleIntervalMultiplier」と記載しており、利用者には runtime 設定として見えます。 このスライスで全 role 向け設定 read surface を用意しない方針なら、少なくとも未接続の setting を editable registry へ公開せず、現段階は固定既定値として明記してください。設定として公開するなら、権限を漏らさず全 role が必要な telemetry 閾値だけ取得できる endpoint / app config を用意し、home と point detail の両方へ同じ値を配線してください。 回帰テストは multiplier を 3→5 に変えた時、同じ expected interval / age が stale→fresh に変わることを、実際の runtime 設定供給経路から確認してください。 なお、それ以外は local で frontend 382件、.NET unit 686件、OxiGraph integration 6件、最新 main 統合後の 47 targeted tests/build、Chromium で 60秒周期=stale・3600秒周期=fresh を確認済みです。 |
Replace the fixed 300s stale threshold with a per-point threshold derived
from each point's expected telemetry interval:
threshold = expectedInterval × N (N = telemetry.staleIntervalMultiplier, default 3)
Expected interval is resolved through a hierarchy (point → device → gateway),
falling back to the system-default threshold (telemetry.staleThresholdSeconds,
300s) when no interval is known — preserving today's behaviour for points with
no configured interval.
Frontend
- New pure resolver lib/telemetry/freshness-threshold.ts
(resolveExpectedIntervalSeconds / resolveStaleThresholdSeconds).
- classifyPointFreshness honors an optional per-point thresholdSeconds;
loadPointsFreshness computes each point's threshold from an expected-interval
map + multiplier. Both remain backward compatible.
- Thread expectedIntervalSeconds through PointResource mapping, the home
loaders/operator-home, and the point-detail freshness badge.
Backend
- Wire the already-declared-but-dead Point.Interval end to end: add the
sbco:interval ontology constant, project it in the point SPARQL selects, and
map it in MapPoint (the seed already writes sbco:interval; the read path never
surfaced it, so Point.Interval was always null).
- Add telemetry.staleIntervalMultiplier (default 3) to SettingsRegistry.
Docs: new §6 in docs/oss-sla-freshness.md documenting the model + follow-ups
(live settings fetch for non-admins, device/gateway default intervals).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XEG6csNA9GDkQzT37CwAUp
…ble setting (#210 review) `telemetry.staleIntervalMultiplier` was added to the editable SettingsRegistry, but the freshness classifier never reads it — home and point detail both use the frontend constant DEFAULT_STALE_INTERVAL_MULTIPLIER (3). An admin could change it to 5, see "saved", and stale classification would not budge: a false affordance. For this slice, keep the multiplier a fixed default (3) and remove it from the editable registry, so nothing claims to be runtime-configurable when it isn't. The per-point expected-interval mechanism is unchanged (threshold = interval × 3); resolveStaleThresholdSeconds still takes a multiplier param so a future all-role telemetry-threshold read surface can supply it without an API change. Registry test now asserts the key is absent; docs state the multiplier is a fixed constant this slice and spell out the exact follow-up that makes it (and the pre-existing staleThresholdSeconds) live for all roles. telemetry.staleThresholdSeconds is pre-existing (registered before this PR) and is left as-is; its frontend-constant mirroring predates #210. Signed-off-by: Claude <noreply@anthropic.com>
3c0e73d to
f0cf63c
Compare
|
ご指摘のとおり false affordance でした。修正しました( このスライスでは全 role 向けの設定 read サーフェスを用意しない方針なので、ご提示いただいた選択肢のうち 「未接続の setting を editable registry へ公開しない」 を採りました。 変更:
ご要望の「倍率 3→5 で stale→fresh が変わる回帰を runtime 設定供給経路で確認」は、その供給経路(非管理者可読の read サーフェス)自体が本スライス対象外なので、上記フォローアップで供給経路と同時に追加します。現段階では editable な設定サーフェス自体を出さないことで false affordance を解消しています。 検証: Generated by Claude Code |
…tiplier is live (#183) (#215) Closes the #210-review follow-up: the per-point stale multiplier is now read at runtime by every role, so an admin editing it actually changes stale classification (it was previously a fixed constant to avoid a false affordance). Backend - Re-add `telemetry.staleIntervalMultiplier` to the editable SettingsRegistry (now that it is read at runtime). - `ISystemSettingsService.GetTelemetryThresholdsAsync` returns the effective `{ staleThresholdSeconds, staleIntervalMultiplier }` (default + admin override). - New `GET /api/telemetry/config` (`TelemetryConfigController`, `[AuthorizeFilter]` = authenticated, any role) returns only those two numbers — no other settings leak through the non-admin surface. Editing stays admin-only on `/api/system/settings`. Frontend - `lib/telemetry/repository.ts` `getTelemetryConfig()` fetches the surface (session-cached, falls back to the defaults on failure so freshness never breaks). Bespoke fetch as the endpoint is not yet in aspida (regen is a follow-up). - Home loaders and the point-detail freshness badge (`TelemetryHotData`) now use the fetched threshold + multiplier instead of the frontend constants. Tests - Service: thresholds fall back to registry defaults, and an admin override of the multiplier is served. - Frontend: `getTelemetryConfig` parses/caches/falls back, and — the regression the review asked for — the same interval+age flips stale→fresh when the served multiplier goes 3→5, through the actual fetch → config → threshold → classify path. Docs: `docs/oss-sla-freshness.md` §6 updated — the multiplier is now runtime-live. Signed-off-by: Claude <noreply@anthropic.com> Co-authored-by: Claude <noreply@anthropic.com>
Summary
Closes #183 の第一スライス。固定 300 秒の stale 閾値を、Point ごとの期待周期(expected interval)ベースの閾値に置き換えます。
期待周期は階層フォールバックで解決(most specific first):
期待周期が無い場合は倍率を掛けず 300s をそのまま用いるため、未設定ポイントの挙動は従来どおりです。
主な発見
Point.Interval(entity → Swagger → aspidainterval)は既に end-to-end で存在し、Twin seed もsbco:intervalを書き込んでいたが、OxiGraph の読み取り経路がsbco:intervalを投影しておらず常に null でした。本 PR で読み取り経路を配線して有効化しています(aspida 再生成は不要)。Frontend
lib/telemetry/freshness-threshold.ts(resolveExpectedIntervalSeconds/resolveStaleThresholdSeconds/DEFAULT_STALE_INTERVAL_MULTIPLIER)。classifyPointFreshnessがPointLastSeen.thresholdSecondsによる per-point 上書きに対応(後方互換)。loadPointsFreshnessが期待周期マップ + 倍率から各点の閾値を算出。PointResource/NamedPoint/ home loaders / operator-home / ポイント詳細の鮮度バッジ(TelemetryHotData)へexpectedIntervalSecondsを配線。Backend
sbco:intervalオントロジ定数を追加し、Point 系 SPARQL SELECT に?ptIntervalを投影、MapPointでPoint.Intervalにマップ(invariant float parse)。DEFAULT_STALE_INTERVAL_MULTIPLIER)。editable 設定としては公開しません — 鮮度判定が runtime でその値を読むまでは「保存できるが挙動が変わらない」false affordance になるため(レビュー対応、下記)。Docs
docs/oss-sla-freshness.mdに §6 を追加(閾値モデル、実装、フォローアップ)。レビュー対応(
f0cf63c)当初は
telemetry.staleIntervalMultiplierを editable なSettingsRegistryに追加していましたが、鮮度判定がその値を一度も読まず(home / ポイント詳細は共にフロント定数 3 を使用)、管理者が保存しても挙動が変わらない false affordance でした。全ロール向けの設定 read サーフェスは本スライス対象外のため、当該キーを registry から削除し固定既定値としました。resolveStaleThresholdSecondsはmultiplier引数を残しており、将来 read サーフェスを用意する時に (a) キー再追加 (b) home / 詳細 両配線 (c) 倍率 3→5 で同一 interval/age が stale⇄fresh に変わる回帰を runtime 供給経路で固定、までまとめて行います。telemetry.staleThresholdSecondsは本 PR 以前から存在するため据え置き。スコープ外(フォローアップ)
GET /api/system/settingsは admin 限定)。倍率・既定閾値の live 反映はここに集約。GatewayPointDto)への interval 追加は不要につき見送り。Test plan
yarn typecheck && yarn lint(web-client, 0 errors)yarn test(382 passed)dotnet build(Shared, 0 errors)dotnet test(Shared.Test Configuration フィルタ, 29 passed)OxiGraphImportTest(Point.Interval == 60 のアサーションを追加)は Docker/Testcontainers 必須のため本環境では未実行Checklist
docs/oss-sla-freshness.md)🤖 Generated with Claude Code