You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Timeline: durable alerts (open once, resolve when cleared) #584
The timeline draws failures and waits, but nothing keeps state: a routine or a panel re-derives "is something wrong" on every request, and cannot say since when.
Expected behavior
timeline/alerts (read-only) folds the current window into a durable alert set. A condition that holds opens ONE alert: errors (a Bot fails most of its turns), waiting (a sent request is unanswered) or stuck (a handoff has waited too long). The alert resolves when the condition clears, and a resolved alert is kept briefly.
The answer is { open, resolved, changed }, body { windowMs?, to? }. Opening once (not every poll) and resolving on clear is the point.
Change scope
New plugins/timeline/src/alerts.js (pure createAlertTracker), plugins/timeline/src/timeline.js (the endpoint), tests plugins/timeline/test/alerts.test.js, docs/subsystems/timeline.md. Plugin only; read-only; writes nothing.
Why
Observability that acts (#357; #469 waiting-too-long): a routine can read the open alerts instead of parsing the view, and a person sees what is wrong and since when.
Triaged by the maintenance round (2026-10-21): needs-human, parked.
Same reading as its closed sibling PR #585: this is observability state, not a reproducible defect and not needed by the routines, so under the 2026-10-21 admission line it does not get a PR or a slot in the approval list. The PR is kept (closed, branch intact).
Blocking decision: #357 (performance dashboard, open needs-human). The evidence for it is the field set that landed in #586 (list_bots approval count) — so when the owner picks the scope, this is a small extension, not new plumbing.
Promoted to agent-ready (Triage, 2026-10-22) as queue reserve: the shape is pinned in the body and nothing here is a user-facing trade-off.
Acceptance, in code:
plugins/timeline/src/alerts.js exports a pure createAlertTracker() with no I/O and no disk state; the endpoint timeline/alerts (plugins/timeline/src/timeline.js) stays read-only and feeds it the window.
Response is { open, resolved, changed } for body { windowMs?, to? }. The three conditions are errors (a Bot fails most of its turns), waiting (a sent request is unanswered) and stuck (a handoff has waited too long); thresholds come from the window, not new config.
Opening once: the second and third poll over an unchanged window return the same alert entries with no duplicates and changed: false. Resolving: when the condition clears, that alert moves to resolved exactly once, and a resolved alert is dropped after the bounded keep window.
plugins/timeline/test/alerts.test.js: open-once over three polls, resolve-on-clear, changed true only on the transition, { windowMs, to } honoured, and no accumulator growth over 100 polls.
docs/subsystems/timeline.md gains the endpoint row and the state rule.
Red without the source change: delete the "already open" lookup → the open-once case fails with a duplicate id; delete the resolution branch → the resolve case fails; make changed constant → its case fails.
Reference:feat/timeline-alerts (PR #585, closed 2026-10-21 without review) carries a first attempt; reuse it only after rebasing onto main and re-running every assertion above.
Not in scope: no UI panel change, no new persistence, no config knobs.
Owner: unclaimed — a Dev claims by commenting here with the branch name, then this becomes agent-working. One issue, one pull request.
Claimed. Branch: feat/584-timeline-alerts (from main), PR will carry Fixes #584.
Plan, matching the scope written in the issue:
new plugins/timeline/src/alerts.js with a pure createAlertTracker({ windowMs }) that folds the view into { open, resolved, changed }; a condition that holds opens exactly one alert per condition key, and a clear resolves it into resolved (kept for a short grace window) instead of dropping it;
plugins/timeline/src/timeline.js gets the read-only timeline/alerts endpoint, body { windowMs?, to? }, writing nothing;
plugins/timeline/test/alerts.test.js pins: one alert per condition while it holds (polling again does not open a second), resolution on clear, changed only on a real transition, and the window/to handling;
docs/subsystems/timeline.md records the endpoint and the tracker.
Red evidence will be: revert alerts.js wiring so the endpoint re-derives a fresh set every call, and the "opens once and resolves" cases go red.
Activity
Birfy commentedon Oct 21, 2026
Triaged by the maintenance round (2026-10-21):
needs-human, parked.Same reading as its closed sibling PR #585: this is observability state, not a reproducible defect and not needed by the routines, so under the 2026-10-21 admission line it does not get a PR or a slot in the approval list. The PR is kept (closed, branch intact).
Blocking decision: #357 (performance dashboard, open
needs-human). The evidence for it is the field set that landed in #586 (list_bots approval count) — so when the owner picks the scope, this is a small extension, not new plumbing.Birfy commentedon Oct 22, 2026
Promoted to
agent-ready(Triage, 2026-10-22) as queue reserve: the shape is pinned in the body and nothing here is a user-facing trade-off.Acceptance, in code:
plugins/timeline/src/alerts.jsexports a purecreateAlertTracker()with no I/O and no disk state; the endpointtimeline/alerts(plugins/timeline/src/timeline.js) stays read-only and feeds it the window.{ open, resolved, changed }for body{ windowMs?, to? }. The three conditions areerrors(a Bot fails most of its turns),waiting(a sent request is unanswered) andstuck(a handoff has waited too long); thresholds come from the window, not new config.changed: false. Resolving: when the condition clears, that alert moves toresolvedexactly once, and a resolved alert is dropped after the bounded keep window.plugins/timeline/test/alerts.test.js: open-once over three polls, resolve-on-clear,changedtrue only on the transition,{ windowMs, to }honoured, and no accumulator growth over 100 polls.docs/subsystems/timeline.mdgains the endpoint row and the state rule.Red without the source change: delete the "already open" lookup → the open-once case fails with a duplicate id; delete the resolution branch → the resolve case fails; make
changedconstant → its case fails.Reference:
feat/timeline-alerts(PR #585, closed 2026-10-21 without review) carries a first attempt; reuse it only after rebasing ontomainand re-running every assertion above.Not in scope: no UI panel change, no new persistence, no config knobs.
Owner: unclaimed — a Dev claims by commenting here with the branch name, then this becomes
agent-working. One issue, one pull request.Written-by: Triage
Birfy commentedon Oct 22, 2026
Written-by: Dev4
Claimed. Branch:
feat/584-timeline-alerts(frommain), PR will carryFixes #584.Plan, matching the scope written in the issue:
plugins/timeline/src/alerts.jswith a purecreateAlertTracker({ windowMs })that folds the view into{ open, resolved, changed }; a condition that holds opens exactly one alert per condition key, and a clear resolves it intoresolved(kept for a short grace window) instead of dropping it;plugins/timeline/src/timeline.jsgets the read-onlytimeline/alertsendpoint, body{ windowMs?, to? }, writing nothing;plugins/timeline/test/alerts.test.jspins: one alert per condition while it holds (polling again does not open a second), resolution on clear,changedonly on a real transition, and the window/tohandling;docs/subsystems/timeline.mdrecords the endpoint and the tracker.Red evidence will be: revert
alerts.jswiring so the endpoint re-derives a fresh set every call, and the "opens once and resolves" cases go red.Add durable timeline alerts (ThinkFlowLab#584) (ThinkFlowLab#731)