Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1779-digest-count-rollover.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix the team run report printing `1000.0k` for a count between 999,950 and 999,999; it now carries into the next unit and reads `1.0M` ([#1779](https://github.com/ThinkFlowLab/FlyBot/issues/1779)).
2 changes: 1 addition & 1 deletion docs/subsystems/timeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Everything in `plugins/timeline/`: the recorder, the trace file, the read model
| `plugins/timeline/src/store.js` | The trace: single writer, write-tmp + fsync + rename, capped segments. |
| `plugins/timeline/src/window.js` | Pure read model: records to lanes, turns, tools, arrows, edges, utilization, the token spend per Bot (#357), per-Bot outcomes and recent failures (#471), the open requests waiting longest (#469) and the tools each Bot used (#359). |
| `plugins/timeline/src/health.js` | Team health (#357): pure `buildHealth(window)` turning the window into per-Bot signals and flags. |
| `plugins/timeline/src/digest.js` | Team run report (#582): pure `buildDigest(window)` turning the window into short Markdown, with the count and duration formatting (`formatCount`, `formatMs`). A duration rounds once and carries into the next unit (#761), so the text says `1m` and `2h00m`, never `60s`, `60m` or `1h60m`. |
| `plugins/timeline/src/digest.js` | Team run report (#582): pure `buildDigest(window)` turning the window into short Markdown, with the count and duration formatting (`formatCount`, `formatMs`). A duration rounds once and carries into the next unit (#761), so the text says `1m` and `2h00m`, never `60s`, `60m` or `1h60m`. A count carries the same way (#1779): 999,950 reads `1.0M`, never `1000.0k`. |
| `plugins/timeline/src/alerts.js` | Durable alerts (#584): pure `createAlertTracker()` folding a window across reads, so a condition opens one alert and resolves when it clears. |
| `plugins/timeline/client.js` | Browser entry (`@flybot/timeline`): the 2 s poller exposed as `ctx.flybot.timeline`, the geometry functions (`layoutSwimlanes`, `layoutMap`, `waitRows`, `utilizationRows`) and the panel (swimlanes, who waits on whom, utilization, and **Copy report**), opened from its `flybot.sidebar.section` row, the only entry (not in the + menu, #269). |
| `plugins/timeline/src/sanitize.js` | The cleaners for ids, labels, codes, times, counts. |
Expand Down
9 changes: 8 additions & 1 deletion plugins/timeline/src/digest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ const TOP_TOOLS = 5
export function formatCount(value) {
const n = Number.isFinite(value) ? Math.max(0, Math.round(value)) : 0
if (n >= M) return `${(n / M).toFixed(1)}M`
if (n >= K) return `${(n / K).toFixed(1)}k`
if (n >= K) {
// Carry a count that rounds up into the next unit (#1779), the same carry #761 fixed for
// durations: picking the unit from the raw value and rounding inside it printed `1000.0k` for
// 999,950–999,999, a number the k unit cannot hold.
const tenths = Math.round(n / 100)
if (tenths >= 10_000) return `${(tenths / 10_000).toFixed(1)}M`
return `${(n / K).toFixed(1)}k`
}
return String(n)
}

Expand Down
11 changes: 11 additions & 0 deletions plugins/timeline/test/digest.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ test('formatCount and formatMs read at a glance', () => {
assert.equal(formatMs(undefined), '0s')
})

// The unit is picked from the raw value, so the rounding inside it can overflow it (#1779): a count
// one step below a million printed `1000.0k`, which is a number its own unit cannot hold and one a
// reader has to convert back before it means anything. The carry is the same one #761 fixed for
// durations.
test('#1779: a count that rounds up into the next unit prints that unit, not 1000.0k', () => {
assert.equal(formatCount(999_949), '999.9k')
assert.equal(formatCount(999_950), '1.0M')
assert.equal(formatCount(999_999), '1.0M')
assert.equal(formatCount(1_000_000), '1.0M')
})

// The carry must never overflow the unit the text prints (#761): rounding the raw milliseconds
// inside a unit chosen from the raw value printed `60s` for 59.6 s and `1h60m` for 1h59m50s, and a
// person pasting the report read those numbers in a standup.
Expand Down
Loading