Skip to content

Flag a Bot busy in a long turn instead of idle - #618

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:fix/timeline-health-busy-idle
Oct 21, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:fix/timeline-health-busy-idle

Conversation

@HugoXK

@HugoXK HugoXK commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Flag a Bot busy in a long turn instead of idle
The timeline team-health view (plugins/timeline/src/health.js) derived its flags from utilization.turns, which counts only turns that began inside the window, while busyMs, errors and running cover every turn that overlapped it. Two wrong readings followed, both contradicting timeline.md invariant 5 ("Idle means no running turn"):

  • a Bot in one long turn that started before the window (turns: 0, busyMs = the whole window, running: true) was flagged idle, and never busy, though it was saturated and running;
  • a failed turn that began before the window counted in errors but not in turns, so the rate could exceed 1 and the detail read e.g. "3 of 2 turns failed".
    busy is now the share of the window that was busy (so a long turn counts however it started), idle needs no overlapping turn and nothing running, and the failure rate divides by the larger of the in-window turns and the failed turns.
    plugins/timeline/test/health.test.js adds a case for each (a running Bot at 100% busy must be busy, not idle; an errors rate over more failures than in-window turns must not exceed 1) — both fail without the change. The existing tests are unchanged.
    Ran at the tip: npm run verify (green, TZ=UTC). Not run: smoke (this macOS box has no setsid, so dev/bench.sh cannot start dsh web), Desktop, real-model steward.

The team-health view keyed `busy` on turns that began in the window and
`idle` on there being none, so a Bot in one long turn that started before
the window read idle while it was busy the whole time and running. Base
busy on the busy share of the window and idle on no overlapping turn and
nothing running; count the failure rate against the larger of the turns
that began in the window and the failed turns, so it never reads more
failures than turns.
@Birfy Birfy added bug Something isn't working area:ui Web/desktop UI and copy labels Oct 21, 2026
@Birfy

Birfy commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

Labels applied by Chief on behalf of Triage (Triage had no GitHub write channel this round). Type/area only; no risk:T* is written by hand.

@Birfy

Birfy commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 064fe63

Tip SHA: 064fe638eadbf50159a8a19a512b47d90664140e
CI at tip: 8/8 success (tests-ok, package, plugins social/tree/timeline, ubuntu-latest Node 24,
           reviewed tip, risk tier)
Tier (from the diff): T2 — plugins/** (plugins/timeline/src/health.js); the docs line is T0

Ran at this tip:

$ cd plugins/timeline && TZ=UTC node --import ./test/resolve.mjs --test test/health.test.js
# tests 5 / pass 5 / fail 0

Gate ③, source change reverted (plugins/timeline/src/health.js from base 81c708d0), tests kept:

not ok 3 - health: a long turn that began before the window is busy, not idle
not ok 4 - health: a failed turn that began before the window never reads more than the turns
# tests 5 / pass 3 / fail 2

Exactly the two new tests go red.

The change is right on the merits: utilization.turns counts only turns that began in the window while busyMs, errors and running cover every turn that overlapped it, so the old flags could call a saturated Bot idle and could print "3 of 2 turns failed". Counting the error ratio against Math.max(turns, errors) and requiring running !== true for idle restores timeline.md invariant 5 without widening any window semantics. The busy flag now also fires for a Bot whose single turn started before the window, which is the point of the fix.

Entering the merge queue now.

@Birfy
Birfy added this pull request to the merge queue Oct 21, 2026
Merged via the queue into ThinkFlowLab:main with commit 29fa085 Oct 21, 2026
8 checks passed
@Birfy

Birfy commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

Post-merge record: 29fa085b (squash, via the merge queue). Reviewed tip 064fe638 unchanged.

Birfy added a commit to Birfy/FlyBot that referenced this pull request Oct 23, 2026
…#670)

Fixes ThinkFlowLab#609

Tip SHA: 4c8d98c

## Reproduce

The rule text says `risk:T*` is set by the controller from the diff,
never by hand, and the
mechanism does not hold up: `github-actions[bot]` labelled ThinkFlowLab#574 at
07:28:43Z, while ThinkFlowLab#606, ThinkFlowLab#610,
ThinkFlowLab#611, ThinkFlowLab#612, ThinkFlowLab#616, ThinkFlowLab#617, ThinkFlowLab#618 and ThinkFlowLab#619 carry no `risk:T*` at all even
though `risk tier (report
only)` succeeded on every one of them. The owner decision on the issue
is ②: keep the workflow
read-only, never label — and make the computed tier auditable instead of
remembered.

At this tip, `node .github/scripts/risk-gate.mjs --pr <n>` ends its
report with

    risk-tier: T3; basis: AGENTS.md=T3, plugins/social/src/social.js=T3

so a review can quote the tier the rules produced instead of restating a
number.

## What changed

- `.github/scripts/risk-gate.mjs`: `judge()` records every changed path
(and a rename's old path)
with the tier `tierOf()` gave it, and `summaryLine()` renders the final
line of the report from
that same result — highest tier first, paths capped at 10 with a `+N
more` tail. `render()` ends
the job summary with it, after the prose, so the two can never disagree.
- `.github/scripts/risk-gate.test.mjs`: four cases — the line's exact
shape for a T3 PR, the
ordering and the cap, the no-changed-files case, and "the line is the
last line of the report".
- `AGENTS.md` gains a bullet under the label section: quote the summary
line, the workflow never
writes a label, the controller copies it on its poll, no review
transcribes a tier.
- `.github/labels.json`: every `risk:T*` description keeps `advisory`
and `the merge gate
recomputes it` and now also names the controller as the writer (all ≤
100 characters).
- `docs/controller.md`: the tier stays auditable without the label —
quote the summary line.
- `.github/workflows/risk-tier.yml`: header comment only — no permission
added, no step changed.
It keeps `contents: read` and `pull-requests: read` and still never
labels, comments or merges.

## What I ran

- `TZ=UTC node --test .github/scripts/*.test.mjs` at this tip (with
`git` on `PATH`) →
  **144/144**.
- `node --test .github/scripts/risk-gate.test.mjs` → **18/18**,
including the four new cases.
- `node .github/scripts/risk-gate.mjs` behaviour checked by hand through
the harness result:
`judge([AGENTS.md, plugins/social/src/social.js])` → `risk-tier: T3;
basis: AGENTS.md=T3,
plugins/social/src/social.js=T3`; no files → `risk-tier: T0; basis: none
(no changed files)`.
- `changelog.mjs check` not run: a change users cannot see needs no
fragment (changelog.d/README).

## Red without the source change

- Removing the `basis.push([name, t])` line from `judge()` (the tests
untouched) →
`risk-gate.test.mjs` **16/18, 2 red**, exactly `summaryLine: one
machine-readable line…` and
  `summaryLine: highest tier first…`.
- Removing `summaryLine(result)` from the end of `render()` instead →
**17/18, 1 red**, exactly
`render: the machine-readable line is the last line of the report`.
Restored → 18/18.

## Not run

- The workflow itself (it needs Actions): the change to `risk-tier.yml`
is a comment, and the
  existing `the workflow stays report only` test still passes with it.
- `npm run verify` end to end: no `core/node_modules` in this sandbox
(the same on `main`).
- The `risk-tier` job on this PR runs the base copy of the script, so
its summary will only show
the new line after merge — that is the workflow's normal base-copy
behaviour, not a gap here.

## Risk

T3 by the repository's own rules (`.github/**`, `AGENTS.md`): a
reporting script plus text. It
adds no permission, no network call, no label, comment or merge, and
cannot block anything — the
job is still `continue-on-error: true` and not a required check. The
only behaviour it changes is
the last line of that job's summary. The one thing a reader must not
conclude is that the label
now arrives on time: it does not, and the docs keep saying why (ThinkFlowLab#629).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ui Web/desktop UI and copy bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants