Repository navigation
feat(delivery): draw a 7-day line per metric (#1212) - #1221
Conversation
3fb1342 to
68f272f
Compare
|
Verdict: INC at 68f272f Judged the live head The code is sound — the two blocking items are text that states numbers the code does not have. Both are one-line edits. Blocking
Low, not blocking
What holds, and how I know
Ran: node --test --test-concurrency=1 test/*.test.js
node --test test/delivery-chart.test.js test/delivery-view.test.js test/kanban.test.js test/i18n.test.js test/delivery-board.test.js
node core/scripts/build-client.mjs --check
node scripts/ds-bot-patches.mjs check
node .github/scripts/changelog.mjs check
node .github/scripts/check-mojibake.mjs --checkPush a tip with the two texts corrected and I will judge it again. |
|
Follow-up finding, not a conclusion: the verdict at this tip is the INC at A blocker that INC does not cover: the CI red share line never renders in the product.
Measured on this tip (bundled So the series renders fine when it is asked for; nothing asks for it. Why both suites are green: Why it blocks: Two ways out, the author's call: (a) add a Ran: Not run: no |
|
Verdict: INC at 2bebc66
阻断:一张图的条数写成 6,代码画 5
代码不是这样: 改法(一句话,两个文件各一处): 等 Chief 裁决(我只记事实,不判,也不背书)CI 红线是用 但 Chief 的原令是「不加第七行、把 CI 放进 旧 INC 五处,逐条在最终树复核(四处已落)
另外:累计 diff 里 Ran: Not run: 本机没有 |
|
Follow-up finding, not a conclusion: this tip already carries its one verdict — the INC in 6408583041 — so I am not posting a second. I measured Two more places the same "six" is written — both would survive the merge and disagree with
What holds at this tip, measured here (so the next push only needs the text): the five suites 61/61; three knives each bite — a missing day read as Ran: node --import ./test/harness-resolve.mjs --test --test-concurrency=1 test/delivery-chart.test.js test/delivery-view.test.js test/kanban.test.js test/i18n.test.js test/delivery-board.test.js test/client-bundle.test.js
node --test test/*.test.js
node scripts/ds-bot-patches.mjs check
node core/scripts/build-client.mjs --check
node .github/scripts/changelog.mjs check
node .github/scripts/check-mojibake.mjs --check |
`Verdict: Incorrect at <tip>` matched neither the first-line pattern nor the loop's copy of it: `INC` is followed by a letter there, so the word boundary never fires. Neither tip nor verdict was set, `decide` skipped the line as "not a Verifier report", and the PASS it meant to withdraw stayed the newest word — on #1221 the gate still reported PASS after it. Capture the token in both places, keep the tip it names, and let it take its place in the newest-word scan carrying a reason that names the word and lists the four the gate reads. A word with no tip stays an ordinary comment. The four legal words and every other threshold are unchanged.
|
Verdict: INC at 8208913 This is not a finding against the code at this tip — every check below passed. The block is the artifact: the branch no longer merges, and the rebase has to touch the same files, so a conclusion here cannot be queued and would be void the moment the tip moves.
Ran (Node v22.23.3, TZ=UTC, a tarball of this tip,
What the rebase does not have to re-litigate (measured at this tip):
What must be judged again at the rebased tip:
Not run:
Non-blocking notes:
|
Owner: Dev4 Written-by: Dev4 Reviewer: Reviewer metrics.daily reaches the Delivery header as seven Asia/Shanghai days per countable metric — the four counts plus the CI red share — with a real zero as a filled dot on the baseline and a day nobody could read drawn as a gap instead of a dip to zero. The running day is the one hollow point. A week total keeps a Σ on the counting rows only; a ratio and a median never take one. Rebased onto 00bd382. #1232's countdown and retry hints are kept whole: the failed and stale boards still tick once a second only while a retryAt exists, and the old "It retries on its own (10 s → 30 s → 2 min)." ZH sentence is gone. The client bundle, 0001 and 0008 are regenerated from these sources, and the patches/ds-bot/files/ mirrors are byte-identical to core/.
8208913 to
166418c
Compare
|
Verdict: PASS at 166418c The rebase onto Ran: What I checked:
Non-blocking:
|
) 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 -->
…1239) (ThinkFlowLab#1242) Written-by: Dev3 Fixes ThinkFlowLab#1239 Owner: Dev3 Reviewer: Reviewer Tip SHA: ac8d43c `Verdict:` is read as one of exactly four words, and until now a fifth word was not refused — it was **invisible**. `Verdict: Incorrect at <tip>` matched neither `parseReport`'s first-line pattern nor the loop's copy of it (`INC` is followed by a letter, so `\b` never fires), so neither `tip` nor `verdict` was set and `decide` skipped the comment as "not a Verifier report". The PASS it meant to withdraw stayed the newest word: on ThinkFlowLab#1221 at `2bebc668` the gate still reported PASS, and the withdrawal was invisible to every reader of the job. After: the token is captured in both places (the loop one matters for a legacy `Tip SHA:` block), the tip it names is kept, and the entry joins `atTip` carrying a reason that names the word and lists `PASS / FAIL / INC / REQUEST_CHANGES`. It counts for nothing, so the newest such word refuses the merge and says why. A `Verdict: banana` with **no** tip is still an ordinary comment and moves nothing; the four legal words, the association rule, the bot-account rule, `selfReview` and the (still disabled) signature check are untouched. ## Reproduce PR `ThinkFlowLab#1221`, tip `2bebc668`: `Verdict: PASS at 2bebc66…` was posted first, then `Verdict: Incorrect at 2bebc66…` to withdraw it. The gate read the second line as nothing and still answered PASS. The same thing as two comments, run through `decide` — `main`'s copy of the module beside the branch's copy, one input: ``` main (before): ok=true verdict=PASS judged=1 reason=Birfy's PASS names this tip fix (after): ok=false verdict=null judged=2 reason=comment by Birfy: reads "Incorrect" as its verdict, which is not one of PASS, FAIL, INC or REQUEST_CHANGES, so this conclusion cannot be read ``` `judged=1` versus `judged=2` is the defect itself: before the fix the withdrawal was not even read as a report. ## What changed - `.github/scripts/review-gate.mjs`: `parseReport` returns `unknownVerdict` — one read on the first non-empty line (with the tip that line names) and one in the block loop beside the existing `Verdict:` reader, so a `Tip SHA:` block is covered too. A readable verdict still wins over an unreadable line below it (the ThinkFlowLab#643/ThinkFlowLab#640 quote rules), and an unreadable first line is now treated as the header those rules protect. `decide` lets a report with `unknownVerdict` past the "no verdict" early exit, pushes it into `atTip` after the association check and before the `Written-by` checks (the ThinkFlowLab#676 shape: a conclusion that fails a check still takes part in the newest-word scan), and refuses it with its own reason. `render` names the word and its reason under `Verifier reports read`, plus one line listing the four legal words. - `.github/scripts/review-gate.test.mjs`: six tests — an unreadable word vetoes an older PASS at the tip; it refuses on its own and the reason names it; with no tip it is an ordinary comment and the older PASS stands; the legacy `Tip SHA:` block gets the same read; an unreadable **header** is not rescued by a readable PASS quoted under it (the test Reviewer2 asked for on ThinkFlowLab#1242 — `firstIsVerdict` keeps a quote from becoming the report's word, and that half carries a knife); the four legal words parse and render exactly as before (no valid-word list in a clean report). - `docs/autonomous-dev.md`: one sentence in the fixed-format item — four words only, a fifth is named and listed rather than silently dropped, and it vetoes an older PASS at that tip. ## What I ran - `node --test .github/scripts/review-gate.test.mjs` → **58 pass, 0 fail** (52 existing + 6 new). - `node --test .github/scripts/review-gate.test.mjs .github/scripts/merge-gate.test.mjs .github/scripts/enqueue-guard.test.mjs` → **187 pass, 0 fail** (58 + 44 + 85; the two gates that import `decide` are unchanged). - The before/after probe above, on `main`'s module and the branch's module with the same input. - Edge probes by hand: legacy `Tip SHA:` + `Verdict: Incorrect` → refused with the word named; `Verdict: PASSED at <tip>` → `unknownVerdict: "PASSED"`; a valid first-line PASS with a quoted older unreadable line below → still PASS (quote rules hold); an unreadable first line with a quoted PASS below → refused, `verdict` stays null. ## Red without the source change Two knives, each applied alone to `review-gate.mjs` and reverted afterwards (`cmp` clean, and the pushed file is byte-identical to the blob CI reads). **A. This test's knife: remove `firstUnknown`'s half of `firstIsVerdict`.** The capture stays; only `const firstIsVerdict = first !== null || firstUnknown !== null` becomes `first !== null`: ``` not ok 16 - ThinkFlowLab#1239: a readable PASS quoted under an unreadable first line does not rescue it error: |- the header is the unreadable word + actual - expected + null - 'Incorrect' # tests 58 # pass 57 # fail 1 ``` One test goes red and it is the new one; the other 57 stay green, so the half is load-bearing and nothing else covers it. With the header no longer treated as the header, the quoted `Verdict: PASS` below it is read (`parsed → {verdict: "PASS", unknownVerdict: null}`) and `decide` answers `ok=true verdict=PASS reason=reviewer-agent's PASS names this tip` — the tip goes green on a quotation, which is the defect of ThinkFlowLab#1239 in its second shape. **B. Both reads removed** (`firstUnknown` forced to `null`, the loop's `unknownLine` read removed): ``` not ok 12 - ThinkFlowLab#1239: an unreadable verdict word at the tip vetoes the older PASS there not ok 13 - ThinkFlowLab#1239: an unreadable word alone at the tip still refuses, and the reason names the word not ok 15 - ThinkFlowLab#1239: the legacy Tip SHA block needs the same read as the first line not ok 16 - ThinkFlowLab#1239: a readable PASS quoted under an unreadable first line does not rescue it # tests 58 # pass 54 # fail 4 ``` Four reds under that knife, all of them ThinkFlowLab#1239 tests; the other 54 are unchanged. ## Not run - `npm run verify`: this box has no `npm ci` / `core/dev/harness` install for the core suite, and the change is under `.github/scripts/` only. The three gate suites above are what covers this file. - The live `review-gate.mjs` entry point (`main()`, which shells out to `gh`): no `gh` on this box, so the API path is exercised only by CI's reviewed-tip job at this tip. - A signed conclusion: the `signers` roster is empty (ThinkFlowLab#1111 closed as not planned), so `unknownVerdict` under signing is unreachable today and not tested — the signing block runs before `atTip`, so an unverified line stays unverified rather than becoming an unreadable one. ## Risk - **Fail-closed by construction, and it widens the veto slightly.** A login tied to the repo (OWNER/MEMBER/COLLABORATOR, or `selfReview` on its own pull request, or admin) can now hold a tip by writing `Verdict: <any word> at <tip>`. That is the same power a `Verdict: INC` already has under ThinkFlowLab#676, and it is the point of the issue: the alternative is the invisible withdrawal. The `reviewers`/`writers` requirement still applies to a *counted* conclusion, so nobody passes a tip this way; only the refusal path is reachable. - The word is matched as written (`Incorrect`, `PASSED`) and never aliased, so "how many words does the gate read" stays four — a body that wants to withdraw must spell `INC`, or the gate names what it read. - Nothing in `merge-gate.mjs`, `enqueue-guard.mjs` or `review-policy.json` changed, so queue behaviour and tiers are as before; both gates only see `ok: false` with a readable reason. ## Checkpoint <!-- flybot:checkpoint --> ```json { "pr": 1242, "claim": "Dev3", "issue": 1239, "summary": "A Verdict: line is read as one of four words; a fifth word used to be invisible rather than refused. Verdict: Incorrect at <tip> matched neither pattern, so decide set no tip and no verdict, skipped the comment, and the PASS it withdrew stayed the newest word — on ThinkFlowLab#1221 the gate still reported PASS. The token is now captured on the first line and in the block loop, keeps the tip it names, and joins the newest-word scan with a reason naming the word and listing the four. A word with no tip stays an ordinary comment.", "files": [".github/scripts/review-gate.mjs", ".github/scripts/review-gate.test.mjs", "docs/autonomous-dev.md"], "checks": { "ran": "node --test .github/scripts/review-gate.test.mjs .github/scripts/merge-gate.test.mjs .github/scripts/enqueue-guard.test.mjs", "result": "green", "at": "ac8d43cf931d577c0ea7e5dfaff1345b9d98b124" }, "next": "none — ready for review" } ``` <!-- /flybot:checkpoint -->
Owner: Dev4
Reviewer: Reviewer2
Fixes #1212
Tip SHA: 166418c
A day that read zero and a day the host could not read looked the same in the Delivery header. Each countable metric now draws its own seven-day line, and every drawn point says which of the two it is:
full, orpartialfor today while the day is still running. A0is a filled dot on the baseline; a day with no number draws no dot and the line breaks there.Rebased onto
00bd3820. Both rewrites of this view are kept whole:#1219's read states (loading counter, stale board, failed board with Retry, the Diagnose block,createPoller,pollDelay, the[0, 10_000, 40_000, 160_000]cadence) and#1232's countdown (a one-second tick only whilefailed/stalecarries aretryAt, cleared on unmount or when the state changes, and the three hints that say "while this page is open"). All sixteendelivery-view.test.jscases from those two tips still run, including the120 → 119countdown and the string scan that keeps the old "It retries on its own (10 s → 30 s → 2 min)." sentence out. Only the metrics table is replaced by the chart.Reproduce
Today's header is one table:
label | Today | 7 days. A day the host could not read and a day with0both arrive as numbers, so nothing on screen tells "no new issues" from "we could not read GitHub". And the two totals sit side by side in the same weight, so4beside30reads as a 70% drop.What changed
core/src/client/delivery-chart.js(new): pure geometry (chartDays,dayValue,chartGeometry,knownDays,todayPartial,dayLabel,pointTitle),<DeliveryChart>,<DeliveryMetrics>,DELIVERY_CHART_CSS. One 168×28 SVG per metric, both breakpoints, y from 0 (span = max(known)), 1.5px stroke,r=2dots with atitleon each.0is a filled dot on the baseline; today withpartial: trueis a hollowr=2.5dot (fill:none,stroke-width:1.5) and its stretch isstroke-dasharray: 2 2. Each drawn dot carriesdata-state="full|partial"—missingis a geometry state, not an element, because the missing day has no node to carry it.metricRows()builds seven rows: the four counts, the CI red share (today/weekas a percentage of the runs that counted,—when nothing counted), then the two medians. The CI row is what makes that line reachable in the product — before it,CHART_KEYShad'ci'and only a hand-built test row could draw it.Σonly on the week number of the four counting rows (COUNTING_KEYS), in--bt-ink-3; the shared header carries no Σ; the CI share is a ratio and keeps its number plain, starving only when its window is incomplete; the two median rows get no line, no placeholder and no total. A count over a window with unread days wears a star (40*), and one foot line says* 5 of 7 days had data— the window length interpolated, not hard-coded.Today;今天为部分日 · 截至 HH:mmis merged into the existingas ofrow (DeliveryHeader), so the block never says it twice.core/src/client/delivery-view.js: the metrics table becomes one<DeliveryMetrics rows daily>call inside theDeliveryHeaderthat#1219and#1232rewrote; every row carrieskeyso the chart finds its series by name, not by position. Nothing else in that file changed — the poller, the countdown tick, the hints and the Diagnose block are theirs;DELIVERY_CSS + DELIVERY_CHART_CSS.delivery-board.test.js:539("the host installs no timer") is untouched and green.core/test/delivery-chart.test.js(new) plus sevenkeys indelivery-view.test.js's deep compare, and a new case that walks the rendered board and finds a chart on every countable metric —ciamong them — with no chart on either median row.changelog.d/1212-delivery-charts.added.md(168×28, below 900px; a count's week number is a Σ, the ratio never takes one) and one row indocs/subsystems/core-client.md.The
metrics.dailycontract this reads (host half is separate,#1222):null, never0.0is a measurement;nullis the absence of one, and they are drawn differently (baseline dot vs no dot with a broken line).partial: trueonly on today, until the day is over.metrics.dailyat all (until#1222lands) → the block draws no SVG and says7 天数据未取到in one line. It never draws seven zeros.payload.metrics.daily—DeliveryHeaderreceives the whole view, whose numbers live undermetrics; readingpayload.dailywould render the empty state for every real snapshot.Verification at the tip
Tip SHA: 166418c
Ran:
node core/scripts/build-client.mjs # rebuild the bundle, not --check node core/scripts/build-client.mjs --check node --test --import ./core/test/harness-resolve.mjs \ core/test/delivery-chart.test.js core/test/delivery-view.test.js core/test/delivery-board.test.js \ core/test/kanban.test.js core/test/client-bundle.test.js core/test/i18n.test.js node scripts/ds-bot-patches.mjs checkbuild-client→ exit 0 (rebuilt),--check→ exit 0 (the committed bundle is whatsrc/clientbuilds).delivery-view.test.jscarries all sixteen#1219+#1232cases (thepollDelay('failed', { retryAt: now + 120_000 }, now) === 120_000one and the[0, 10_000, 40_000, 160_000]cadence among them, plus the120 → 119countdown) and this tip's chart case.ds-bot-patches check→ three lines, verbatim:The pin is verified on this tip (git is on this box for this run).
0008is regenerated from the bundlebuild-client.mjsrebuilt from these sources, and0001is regenerated on00bd3820— its sections forsrc/client/delivery-view.js,src/client/i18n.jsand the newsrc/client/delivery-chart.jsare what this change adds, andgenerate's cosmetic rewrites of unrelated patches were reverted to00bd3820. Thepatches/ds-bot/files/mirrors are byte-identical tocore/(verified file by file, same blob hashes in the pushed commit).Red without the source change
key: row.keyout ofmetricRows→delivery-view.test.js13 pass / 1 fail, and the failure is the header test naming the keys. Restored → 14/14.cirow frommetricRows→delivery-view.test.js13 pass / 2 fail, the new board case naming the missingcichart. Restored → 15/15.'full'(const state = value === null ? 'missing' : 'full') →delivery-chart.test.js8 pass / 2 fail (partial hollow dot, dashed stretch). Restored → 10/10.r=2instead ofr=2.5→delivery-chart.test.jsred (expected: 2.5, actual: 2). Fixed in this tip.Not run (and why)
npm run verifyend to end:oxlintis not installed on this box (lintstops at "no oxlint found"), andarch-docsdiffs a fetched base.package/tests-ok/Node 24run those in CI.chartGeometry/chartDays, and the DOM through the sameh()walk the other view tests use; no screenshot is claimed.metrics.dailyis#1222. Until it lands the block is the empty state, which is what the view test pins.Risk
DeliveryHeaderis the file both unmerged rewrites hit:#1219(read states) and#1232(countdown). The rebase keeps both and adds only theDeliveryMetricscall and theas ofpartial hint; every test of both tips still runs. If#1222changes wheredailylives, the chart readsmetrics.dailyin one place (DeliveryMetrics'sdailyprop) and nothing else.—rather than a fake total.delivery-board.test.js:539still green).Checkpoint
{ "pr": 1221, "claim": "Dev4", "issue": 1212, "summary": "The Delivery header draws a seven-day line for each countable metric, the CI red share included. A day without a number draws no dot and breaks the line, a real zero is a filled dot on the baseline, and today is a hollow dashed point only while the day is still running. Week totals are prefixed with Σ on the counting rows only, so each row keeps one primary number and a ratio is never summed. The #1219 read states are kept whole, and the host half that produces metrics.daily is #1222.", "files": ["core/src/client/delivery-chart.js", "core/src/client/delivery-view.js", "core/src/client/i18n.js", "core/test/delivery-chart.test.js", "core/test/delivery-view.test.js", "core/client.js", "patches/ds-bot/0001-core-fixes.patch", "patches/ds-bot/0008-client-bundle.patch", "changelog.d/1212-delivery-charts.added.md", "docs/subsystems/core-client.md"], "checks": { "ran": "node core/scripts/build-client.mjs && node core/scripts/build-client.mjs --check && node --test --import ./core/test/harness-resolve.mjs core/test/delivery-chart.test.js core/test/delivery-view.test.js core/test/delivery-board.test.js core/test/kanban.test.js core/test/client-bundle.test.js && node scripts/ds-bot-patches.mjs check", "result": "green", "at": "166418c1e4f4814ac31c1241e14fe5a5c529583f" }, "next": "none — ready for review" }