Repository navigation
feat(host): build metrics.daily for the delivery chart (#1222) - #1240
Conversation
Owner: Dev4 Written-by: Dev4 Reviewer: Reviewer Fixes #1222 Refs #1212 The chart has read snapshot.metrics.daily since #1212 landed its client half, but the host never built it, so the block always said it had no data. The round now keeps one bucket per day of the two windows it already reads — the week and today — and assembles the last seven Asia/Shanghai days from them, today last and marked partial. A bucket is null when its list was truncated or its CI could not be read, and a week that could not be read makes the whole series null, so an unread day is never drawn as a quiet one. Reading the buckets turned up a second defect in the same loop: the seven-day CI windows were walked backwards from the start of the week, so the header share described the week before the one its own row covers.
|
Verdict: INC at 46fd4a9 One blocking item, and it is a coverage hole, not a wrong number: Blocking — the "list hit the page cap →
Proof that nothing bites: replacing the helper with That is the standing-approval condition failing: remove the changed code and no test turns red. The behaviour itself is correct — I drove it through the fixture (a 400-row Fix: extend What I verified and it holds (each with my own run, not the body's numbers):
Ran: $ node --import ./core/test/harness-resolve.mjs --test --test-concurrency=1 core/test/delivery-board.test.js
# tests 34 # pass 34 # fail 0
$ node --import ./core/test/harness-resolve.mjs --test --test-concurrency=1 core/test/delivery-board.test.js core/test/delivery-view.test.js core/test/kanban.test.js core/test/client-bundle.test.js core/test/home-lock.test.js
# tests 68 # pass 68 # fail 0
$ node core/scripts/build-client.mjs --check
build-client exit=0
$ node scripts/ds-bot-patches.mjs check
skipped pin (no git): the tree hash cannot be computed here
ok 10 patches apply cleanly in order
ok core/ equals ds-bot/ + patches/ds-bot/
check exit=0Not run: the ds-bot pin here (no Non-blocking:
|
|
Written-by: Reviewer2 I re-ran the hole that INC names and it reproduces exactly: replacing A second blocker that fires before any verdict at the next tip, so it is worth fixing in the same push: the checkpoint's The block itself is 1448 characters, under the 1500 limit — only |
The list half of "a truncated read is null, not a smaller number" had no test: replacing `capped()` with `() => false` left every case green, so removing the change turned nothing red. Four full pages could also mean the `stop` row was on page four, where the window was read whole. `pages()` keeps its array and the walk returns that answer as `truncated`, so `capped()` reads the flag instead of counting rows. Three cases pin it: a capped window is unknown for the six days it covers while today keeps its numbers, a first page of old rows stays a complete read, and a full page four that stopped on an old row is a complete window too. Refs #1222
ada6e94 to
ec4aaf2
Compare
|
Verdict: PASS at ec4aaf2 The one blocking item in the INC at Ran: What I checked:
Queue — the guard refuses on the checkpoint, not the code: the body's Non-blocking:
|
|
Written-by: Reviewer 1) The flag:
Scratch probe: 42 pass / 0 fail (the 37 repo tests plus five of mine). The file is not in the PR. 2) One knife per new test, both bite.
The older knives still bite at this tip: 3) The seam: not blocking. I reproduced it. With the week's lists capped, It does not belong in this PR's blocking set. The header has always counted what the list returned (no cap-aware clamp existed on 4) The rest of the contract, on this tree.
Ran: $ node --test --import ./core/test/harness-resolve.mjs --test-concurrency=1 core/test/delivery-board.test.js
# tests 37 # pass 37 # fail 0
$ node --test --import ./core/test/harness-resolve.mjs --test-concurrency=1 core/test/delivery-board.test.js core/test/delivery-view.test.js core/test/kanban.test.js core/test/client-bundle.test.js core/test/home-lock.test.js
# tests 71 # pass 71 # fail 0
$ node --test --import ./core/test/harness-resolve.mjs --test-concurrency=1 core/test/zz-probe.test.js
# tests 42 # pass 42 # fail 0 (scratch probe: 37 repo tests + 5 of mine, not in the PR)
$ node core/scripts/build-client.mjs --check
build-client exit=0
$ node scripts/ds-bot-patches.mjs check
skipped pin (no git): the tree hash cannot be computed here
ok 10 patches apply cleanly in order
ok core/ equals ds-bot/ + patches/ds-bot/
$ cmp core/src/host/delivery-board.js patches/ds-bot/files/src/host/delivery-board.js
$ cmp core/test/delivery-board.test.js patches/ds-bot/files/test/delivery-board.test.js
$ node .github/scripts/changelog.mjs check
changelog: 4 fragment(s) in changelog.d/ are well formed
$ node .github/scripts/check-mojibake.mjs --check
mojibake: ok — 0 occurrences of U+FFFD in 1069 text file(s)Not run: the ds-bot pin — there is no Non-blocking
Conclusion: PASS at |
) DEFAULT_CHECK_RATIO 0.8 -> 0.45; the profile checkRatio still overrides it and the hard line stays at 95% of the usable window. Rebased onto 5f1517f, where #1221 had regenerated patches/ds-bot/0001-core-fixes.patch and #1240 edited docs/subsystems/core-host.md: the README hunk keeps both sides and the invariant bullet was added to the current page.
…inkFlowLab#1233) (ThinkFlowLab#1243) Owner: Dev2 Reviewer: Reviewer Written-by: Dev2 Fixes ThinkFlowLab#1233 Tip SHA: 35fb01f Base: 5f1517f A Bot's conversation is condensed once it passes the steward's check line, a share of the model's usable window. `ThinkFlowLab#796` measured what a call carries: the fixed block is re-sent on every call, and on this host the history is most of each prompt. So the check line is a floor on the prompt of a long turn, and 0.8 made that floor too high. This PR moves the default share to **0.45**, keeps the profile's `checkRatio` as the override, and leaves the hard line at 95%. ## Reproduce A 24-hour scan of this host's Session logs (`/data/dsh/sessions`, window `2026-10-25T05:11Z .. 2026-10-26T05:11Z`): **13,634 calls**, **2,038,461,443** prompt tokens, p50 **152,185** per call. The 64 compactions in that window had a last pre-compaction prompt of p50 **233,670** (p10 209,731, p90 249,017) — with `0.8` of the usable window as the check line. The arithmetic this PR changes, in `core/src/host/steward.js` `linesFor()`: `usable = max(window - replyReserve, floor(window / 2))`, `check = min(floor(usable * checkRatio), floor(usable * 0.95))`. With the fallback share at 0.45, a model that reports no window (500,000) checks at 225,000 instead of 400,000, and the 100,000-token window in the test at 45,000 instead of 80,000. ## What changed - `core/src/host/steward.js`: `DEFAULT_CHECK_RATIO` `0.8 → 0.45`, with the reason in a comment; the hard line and the fallback rules are untouched. - `core/test/steward.test.js`: the two cases that lean on the default (invalid or missing `checkRatio`) now expect `45_000` at a 100,000 window; the cases that pass `0.8` or `0.5` explicitly are untouched. - `core/README.md`: the config example and the documented default are 0.45. - `docs/subsystems/core-host.md`: a new invariant bullet — the check line, its share, the trade, and `checkRatio` as the override (`core/src/host/` is owned by that page). - `patches/ds-bot/0001-core-fixes.patch`, `patches/ds-bot/topics.json`: the three upstream files join the 0001 topic. This machine has no `git`, so `generate` cannot run; the hunks are written by hand and `check` proves the applied tree equals `core/` byte for byte. - `changelog.d/1233-early-compaction.changed.md`: the user-visible line. - Rebased onto `5f1517f9` (no `git` on this machine, so the branch was rebuilt through the API): `ThinkFlowLab#1221` had regenerated `patches/ds-bot/0001-core-fixes.patch` and `ThinkFlowLab#1240` had edited `docs/subsystems/core-host.md`. The regenerated 0001 keeps its `ThinkFlowLab#1212` i18n lines and takes this branch's README hunk; the invariant bullet was added to the current page, not to a copy of the old one. `check` proves the result: `core/` equals `ds-bot/` + `patches/ds-bot/`. ## What I ran At `ef72651c`: - `node --test core/test/steward.test.js` → `# tests 9 / # pass 9 / # fail 0`. - `node scripts/ds-bot-patches.mjs check` → `skipped pin (no git)`, `ok 10 patches apply cleanly in order`, `ok core/ equals ds-bot/ + patches/ds-bot/`. - `node .github/scripts/changelog.mjs check` → `5 fragment(s) in changelog.d/ are well formed`. - `node .github/scripts/check-mojibake.mjs --check` → `0 occurrences of U+FFFD in 1,069 text file(s)`. - oxlint (97 rules, `core/.oxlintrc.json`) on `src/host/steward.js test/steward.test.js` → `0 warnings, 0 errors`. - The 24-hour scan above, the same script and window that produced the numbers cited in Reproduce. - `node core/scripts/build-client.mjs --check` → exit 0, no output (esbuild is not installed here; it was installed for this run only, outside the tree). - The pin, which `check` cannot compute without `git`: the git tree hash of `ds-bot/` computed locally is `5834c00d9c21b972e4b32ad90489443da2a10324`, equal to `ds-bot.pin.json`; upstream `Fei2411/DeepSeek-Bot@2b10c70` `ds-bot` is the same tree (GitHub API). So `check`'s first line is `skipped pin (no git)` on this host only — CI runs the git path. - `node --import ./test/harness-resolve.mjs --test test/steward.test.js` → `# tests 9 / # pass 9 / # fail 0`. ## Red without the source change - Put `0.8` back in `core/src/host/steward.js`: `node --test core/test/steward.test.js` → `not ok 2 - a model without a window counts as the default, and odd settings fall back`, `# tests 9 / # pass 8 / # fail 1`; `node scripts/ds-bot-patches.mjs check` → `core/src/host/steward.js: differs from ds-bot/ + patches/ds-bot/`. - Restored → 9/9 and `check` green again. ## Not run - **The 24-hour after-reading, and the live knife** (set the share back to 0.8 and rerun the same window). The running host still has the old share: `/data/dsh/profiles/web/cordis.patch.yml` already carries `checkRatio: 0.45` (done on the host, not in this PR), but DSH reads the profile at boot and nothing watches that file, so the new share takes effect at the host's next start. The same-scan numbers (ThinkFlowLab#1233: p50 110–135k, 1.40–1.55B/day, calls/day not up) need that start plus a 24-hour window; they will be reported on ThinkFlowLab#1233, not here. **On 2026-10-26 the user chose not to restart the host for this change**: the code ships now, and the before/after reading is taken after the next start that happens for another reason (a release, or a host restart that is already needed). Until then this PR has no live reading, and the address is ThinkFlowLab#1233, not this body. - `arch-docs`: cannot run on this machine (no `git`: `cannot diff against origin/main`); CI runs it. The owned page `docs/subsystems/core-host.md` is updated. - **The whole `core` suite, locally.** This host cannot spawn the test workers: `node[178512]: pthread_create: Resource temporarily unavailable`, and the run had to be stopped. The one file I could attribute — `test/session-identity.test.js` — fails the same way on a pristine `5f1517f9` as on this branch: `Cannot find package '@deepseek-ai/dsh-llm' imported from deepseek-harness/package.json`. CI has those peers and ran the suite green on `ef72651c`; the rebase touched no test besides `steward.test.js`. - Desktop and the smoke test: not run here; no client change. ## Risk - **Earlier condensing keeps less detail.** A session that condensed ten times in a day at the old line will condense more often, and each checkpoint is a summary of a summary. - **More steward calls and handoffs.** Each check the steward makes is a separate small call (~6,000 tokens); the extra compactions were estimated in ThinkFlowLab#1233 at ~20M/day, about 4% of the saving. - **It is product-wide.** An install that never sets `checkRatio` condenses at 45% from its next release; a profile that sets it keeps its own share. - **The patch hunks are hand-written.** `check` compares the applied tree with `core/` byte for byte (and CI runs the git path), so a mis-placed hunk fails loudly rather than silently drifting. ## Checkpoint <!-- flybot:checkpoint --> ```json {"pr":1243,"claim":"Dev2","issue":1233,"summary":"The steward's check line drops from 80% to 45% of the model's usable window: the fixed block is re-sent on every call and the history is most of a prompt, so a later line costs a long turn more than the detail it keeps. The profile's checkRatio still overrides it and the hard line stays at 95%. The patch series carries the three upstream files in 0001, hand-written because this machine has no git, and check proves the applied tree equals core/.","files":["changelog.d/1233-early-compaction.changed.md","core/README.md","core/src/host/steward.js","core/test/steward.test.js","docs/subsystems/core-host.md","patches/ds-bot/0001-core-fixes.patch","patches/ds-bot/topics.json"],"checks":{"ran":"node --test core/test/steward.test.js; node scripts/ds-bot-patches.mjs check; node .github/scripts/changelog.mjs check; node .github/scripts/check-mojibake.mjs --check","result":"green","at":"35fb01fddad471167ba15382293a0d169ee77ee2"},"next":"Reviewer judges this tip. The user chose on 2026-10-26 not to restart the host for this change, so there is no live reading yet: the 24h before/after numbers and the live knife land on ThinkFlowLab#1233 after the next start that happens for another reason."} ``` <!-- /flybot:checkpoint -->
Owner: Dev4
Reviewer: Reviewer2
Fixes #1222
Refs #1212
Tip SHA: ec4aaf2
The chart half of #1212 reads
snapshot.metrics.daily, and the host has never built it: every snapshot carriesdaily: undefined, so the block the user sees is the empty state "7 天数据未取到". This builds the series, and fixes the CI window it exposed.Reproduce
Read any delivery board snapshot on
main:metricshasdayStart,today,weekandopenand nodaily.delivery-view.js's<DeliveryMetrics rows daily>therefore receivesundefined,chartDays(undefined)returnsnull, and the block renders one grey line instead of seven days — the feature is on the page but always empty. Second, in the same loop:ciFor(from, ciDays)walked its day windows backwards fromfrom(from - day * DAY_MS), so the seven-day CI share in the header described the seven days before the week its own counts cover.What changed
core/src/host/delivery-board.jsciForkeeps a per-day entry (start,failed,counted,total) beside its aggregate, and walks its windows forward from the window start — the direction every other count in the module already reads. A cancelled run never judged the code, so it is left out of the per-day denominator; the header's aggregate keeps its own numbers untouched.metricsFor(..., { buckets: true })returns one bucket per day of the window, built from rows that round already fetched: no extra request, the round still reads today plus the week it read before.dailySeries(week, today, todayStart)assembles the last seven Asia/Shanghai days, oldest first, today last withpartial: true, from the two windows' buckets. A window that could not be read, or a day with no bucket, makes the whole seriesnull.metrics.dailyand nothing else new: the per-day buckets (andci.days) are stripped, sometrics.today/metrics.week/metrics.today.cikeep exactly their old shape.pages()splits intowalk()(returns{ rows, truncated }) and the old array-returning wrapper, so the list calls know whether the walk ended on its last allowed page with a full page and nostoprow.capped()reads that flag instead ofrows.length >= max * PAGE: four full pages could also mean the stop row was on page four, where the window was read whole.core/test/delivery-board.test.js: the seven days and their dates, today partial and only today, per-day counts against the fixture, the Shanghai/UTC boundary, truncated runs, unreadable CI, the missing-week case, the week's CI windows, and the three list-cap cases (capped, a stale first page, a full page four that stopped on an old row).docs/subsystems/core-host.md(both the table row and the Delivery prose), andchangelog.d/1222-daily-metrics.added.md.patches/ds-bot/files/src/host/delivery-board.jsand.../test/delivery-board.test.js. No.patchfile changes — the file is stored whole infiles/, sogeneraterewrites the mirrors andgitshows no patch hunk for it.The contract the client reads (agreed with
#1212, host side here):null, never0. A day whose issue/pull list hit the page cap, or whose/actions/runswas truncated, carriesnull; a CI window that could not be read at all makes every day'scinull.0— that is a measurement, and the chart draws it as a dot on the baseline.stoprow was on it means the window was read whole, and its days keep their numbers (feat(host): build metrics.daily for the delivery chart (#1222) #1240).daily: null→ the client's empty state. It never draws seven zeros.Verification at the tip
Tip SHA: ec4aaf2
Ran:
node core/scripts/build-client.mjs --check node --test --import ./core/test/harness-resolve.mjs \ core/test/delivery-board.test.js core/test/delivery-view.test.js core/test/kanban.test.js \ core/test/client-bundle.test.js core/test/home-lock.test.js node scripts/ds-bot-patches.mjs generate && node scripts/ds-bot-patches.mjs check node .github/scripts/changelog.mjs check node .github/scripts/check-mojibake.mjs --checkdelivery-board.test.jsalone is 37, nine of them new here).ds-bot-patches check→ three lines, verbatim:build-client.mjs --check→ exit 0 (no client file changed; the bundle still matches).changelog.mjs check→ 4 fragments well formed;check-mojibake.mjs --check→ 0 occurrences of U+FFFD.Red without the source change
ciForwalking backwards again (from - day * DAY_MS) → 2 fail: the seven-day test (every day but today loses its CI) and the window test.daily: nullin the payload → 4 fail (the series, the boundary, the truncation and the unreadable-CI cases).counted: 3instead of2).cappedreplaced with() => false→ 1 fail:a count built from a list that stopped at the page cap is unknown, not smaller (#1222)(36 pass / 1 fail). That is the hole the INC named: before this round the whole file stayed green.listing.rows.length >= 400) put back → 1 fail:a full page four that stopped on an old row is a complete window, not a capped one (#1240), which is why the flag comes from the walk.Not run (and why)
npm run verifyend to end:oxlintis not installed on this box andarch-docsdiffs a fetched base.package/tests-ok/Node 24run those in CI.delivery-boardtest uses; no token was spent and no real repository was read.src/host/delivery-board.js, notindex.jsor the turn path; the bench needs a DSH checkout that this box does not have.Risk
metricsForwindows the round already reads; the only requests are the same 8 day-windows of/actions/runsas before (7 for the week, 1 for today) plus the same two lists.#1221and still unmerged; nothing here depends on it being merged first, and nothing here touchescore/src/client/.metrics.weekis built from, so the series and the header cannot disagree about the same day.Checkpoint
{ "pr": 1240, "claim": "Dev4", "issue": 1222, "summary": "The round now builds metrics.daily: seven Asia/Shanghai days, oldest first, today last with partial, each carrying the four counts and a per-day CI block. It costs no request — the buckets come from the week and today windows the round already reads — and an unread day or a truncation is null, never zero. Reading them exposed a second defect: the 7-day CI windows were walked backwards, so the header share described the week before. A week that cannot be read leaves the series null, and the client keeps its empty state.", "files": ["core/src/host/delivery-board.js", "core/test/delivery-board.test.js", "docs/subsystems/core-host.md", "changelog.d/1222-daily-metrics.added.md", "patches/ds-bot/files/src/host/delivery-board.js", "patches/ds-bot/files/test/delivery-board.test.js"], "checks": { "ran": "node core/scripts/build-client.mjs --check && node --test --import ./core/test/harness-resolve.mjs core/test/delivery-board.test.js core/test/delivery-view.test.js core/test/kanban.test.js core/test/client-bundle.test.js core/test/home-lock.test.js && node scripts/ds-bot-patches.mjs check", "result": "green", "at": "ec4aaf267a7b7ea790262b70cb1c539db6a286f7" }, "next": "none — ready for review" }