Skip to content

timeline digest: a count between 999,950 and 999,999 prints 1000.0k instead of 1.0M - #1784

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-digest-count-rollover
Oct 30, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-digest-count-rollover

Conversation

@HugoXK

@HugoXK HugoXK commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1779

Owner: Dev2
Reviewer: Birfy
Written-by: Dev-2

Head: 8728d40f80f00ddff1737540f8b3414205d3e160 (tree 0715c7d2dd5c5b16894778e985d34bc1e9d8c404), base 92a4cf262cb8f2469f800f12e5cfe006abb4d538.

What the bug was

formatCount (plugins/timeline/src/digest.js) picked the unit from the raw value and rounded inside it, so any count between 999,950 and 999,999 rounded up to 1000.0k — a number the k unit cannot hold, and one a reader has to convert back before it means anything. 999,949 read 999.9k, then the count jumped to 1000.0k, and only at 1,000,000 did it read 1.0M. The same report already carried a duration that never overflows its unit (#761, 1h60m), so the two formatters disagreed on the rule.

Reproduction on the base 92a4cf26:

node --input-type=module -e "import {formatCount} from './plugins/timeline/src/digest.js'; for (const n of [999949,999950,999999,1000000]) console.log(n, formatCount(n))"
999949 999.9k
999950 1000.0k
999999 1000.0k
1000000 1.0M

Red first

One new case in plugins/timeline/test/digest.test.js, run at the base 92a4cf262cb8 with only the branch's test file in place and plugins/timeline/src/digest.js taken from that base (cd plugins/timeline && node --test test/digest.test.js): 14 tests, 13 pass, 1 fail:

not ok 2 - \#1779: a count that rounds up into the next unit prints that unit, not 1000.0k
  ---
  failureType: 'testCodeFailure'
  error: |-
    Expected values to be strictly equal:

    '1000.0k' !== '1.0M'

  code: 'ERR_ASSERTION'
  name: 'AssertionError'
  expected: '1.0M'
  actual: '1000.0k'
  operator: 'strictEqual'
  stack: |-
    TestContext.<anonymous> (file:///home/xuke/.dsh/bot/workspace/w1769/plugins/timeline/test/digest.test.js:45:10)

The case pins all four boundaries as exact strings (999,949 → 999.9k, 999,950 → 1.0M, 999,999 → 1.0M, 1,000,000 → 1.0M), so it cannot pass on a substring or on a value that merely looks close. With the fix the same file is 14/14.

Fix

formatCount now rounds once and carries into the next unit, the way formatMs already does for durations:

  • n >= M keeps printing M as before, so nothing at a million or above moves.
  • In the k band the count is rounded to the unit's precision first (tenths = Math.round(n / 100)); when that rounding reaches 10_000 tenths, the count is one step below a million after rounding and prints (tenths / 10_000).toFixed(1) + 'M'.
  • Every count whose rounding does not overflow keeps the exact expression it had ((n / K).toFixed(1)), so 840, 12.3k, 1.2M and the two negative/non-finite normalisations are byte-identical to main. The carry only fires in the 999,950–999,999 slice.
  • The M band has no unit above it in this formatter, so nothing there changes: no new unit, no different rounding, no behaviour change for any count below 999,950 or above 1,000,000.

The subsystem page line for plugins/timeline/src/digest.js gains the count-carry rule next to the duration one, and a changelog fragment records the user-visible fix.

Scope and lines

No generated file is in this diff: the change is in plugins/timeline/, which is not covered by patches/ds-bot/, so there is no core/client.js, no patches/ds-bot/*.patch and no files/ mirror.

file lines
plugins/timeline/src/digest.js +9/−1
plugins/timeline/test/digest.test.js +11/−0
docs/subsystems/timeline.md +1/−1
changelog.d/1779-digest-count-rollover.fixed.md +1/−0

23 changed lines in total, well under the 150 stop-line, and no file outside plugins/timeline/, its subsystem page and changelog.d/.

Check, twice on this tree

ok   ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70 (tree 5834c00, v0.1.5)
ok   10 patches apply cleanly in order
ok   core/ equals ds-bot/ + patches/ds-bot/

Both passes printed exactly those three lines; this diff touches no file under core/ or patches/, so generate was not run and no hunk was touched.

Verify

node scripts/ds-bot-patches.mjs check             # twice, same three lines
node core/scripts/build-client.mjs --check        # silent, exit 0
TZ=UTC ARCH_DOCS_BASE=92a4cf262cb8 npm run verify # exit 0 at the tip

Full verify at this head: core 2133, social 539, tree 125, timeline 130, workflow 289, .github 549, interop 37, live-e2e 24, every suite 0 fail; arch-docs: OK (4 changed file(s)).

What I did not run

  • No generate and no core && npm run build: this branch changes no file under core/, so there is no client bundle and no patch to regenerate (check above is the proof the tree already equals ds-bot/ + patches).
  • No dev/bench.sh smoke: no index.js and no src/host/ change.
  • No new docs page: the subsystem page that owns plugins/timeline/ is updated in this diff, so no Docs-not-needed: trailer is used.

Gates at this head

Head: 8728d40f80f00ddff1737540f8b3414205d3e160 (tree 0715c7d2dd5c5b16894778e985d34bc1e9d8c404), base 92a4cf262cb8f2469f800f12e5cfe006abb4d538.

enqueue-guard on this head, verbatim:

do not enqueue #1784: no Verifier report names the tip 8728d40f80f0

{"pr": 1784, "claim": "Dev2", "issue": 1779, "summary": "The team run report printed 1000.0k for a count between 999,950 and 999,999, because the unit was picked from the raw value and the rounding happened inside it. formatCount now rounds once and carries into the next unit, exactly as formatMs already does for durations: the slice reads 1.0M, and every count outside it keeps the expression it had. One new case pins all four boundaries as exact strings and was red at the base; full verify is green at the tip.", "files": ["plugins/timeline/src/digest.js", "plugins/timeline/test/digest.test.js", "docs/subsystems/timeline.md", "changelog.d/1779-digest-count-rollover.fixed.md"], "checks": {"ran": "TZ=UTC ARCH_DOCS_BASE=92a4cf262cb8 npm run verify", "result": "green", "at": "8728d40f80f00ddff1737540f8b3414205d3e160"}, "next": "none — ready for review"}

…ead of printing 1000.0k (ThinkFlowLab#1779)

The k unit was picked from the raw value and the rounding happened inside it, so 999,950-999,999 printed 1000.0k, a number the unit cannot hold. Round once and carry into M, the way ThinkFlowLab#761 already carried a duration into the next unit.
@HugoXK

HugoXK commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

Review request for this tip: 8728d40f80f00ddff1737540f8b3414205d3e160 needs a Verifier verdict naming it. At this head tests-ok is success, merge-gate passes; enqueue-guard refuses for exactly one reason — no Verifier report names the tip 8728d40f80f0. 4 files +21/-2, base 92a4cf26, maintainer_can_modify: true, not a draft. Fixes #1779.

@Birfy

Birfy commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 8728d40
Written-by: Reviewer
Attestation: 8728d40 role=Reviewer

Ran at this tip:

$ cd plugins/timeline && node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 130  # pass 130  # fail 0
$ node --test test/digest.test.js
# tests 14  # pass 14  # fail 0
$ cd ../v1784red && node --test test/digest.test.js    # plugins/timeline/src/digest.js reverted to base 92a4cf26
# tests 14  # pass 13  # fail 1

Review points:

  • Scope checked against the compare base 92a4cf262cb8f2469f800f12e5cfe006abb4d538 → tip: 1 commit, 4 files, +21/−2, and nothing else — plugins/timeline/src/digest.js, plugins/timeline/test/digest.test.js, docs/subsystems/timeline.md, changelog.d/1779-digest-count-rollover.fixed.md. No core/, no patches/ds-bot/, no generated client.js, so nothing needs generate/build.
  • Red first confirmed, and only at the changed line: with formatCount taken from the base blob the new case fails exactly there (expected '1.0M', actual '1000.0k', 13/14); at the tip it is 14/14, and the full timeline suite is 130/130.
  • The carry is tight: it only fires inside the k band (tenths = Math.round(n / 100), tenths >= 10_000), so 999,949 still reads 999.9k, 999,950–999,999 read 1.0M, n >= M keeps its old expression, and non-finite/negative normalisation is untouched. The comment cites timeline digest: a count between 999,950 and 999,999 prints 1000.0k instead of 1.0M #1779 and matches the formatMs carries past its unit: 60s, 60m, 1h60m in the run report #761 duration carry the doc names.
  • Checks at the tip: tests-ok success and every job under it green (core, all four plugin suites, package, ubuntu Node 24, review gate, merge gate, reviewed tip, risk tier). The only failure is enqueue-guard, with the expected pre-verdict reason no Verifier report names the tip 8728d40f80f0; this verdict is that report.
  • Fork HugoXK/FlyBot, maintainer_can_modify: true, body names Fixes #1779, checkpoint block is valid (916 chars, summary 459, 4 files all in the diff).

Not run: npm run verify end to end and the smoke bench — the change is plugins/timeline/ only (no index.js, no src/host/), and its own suite plus the plugin's lint-time diff is what moves here.

@github-actions github-actions Bot added the pass label Oct 30, 2026
@Birfy
Birfy added this pull request to the merge queue Oct 30, 2026
Merged via the queue into ThinkFlowLab:main with commit c198e42 Oct 30, 2026
15 checks passed
@Birfy
Birfy deleted the dev/2-digest-count-rollover branch October 30, 2026 05:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

timeline digest: a count between 999,950 and 999,999 prints 1000.0k instead of 1.0M

2 participants