Repository navigation
Task board prints the 20-card archive page as the Done total - #2047
Conversation
|
@Birfy review request at Head What it changes: the archive rail hedged its counts as Red first on |
9b38b31 to
4145800
Compare
|
@Birfy review request at Head What it changes: the Done column header and the per-column rail no longer print a 20-card archive page as the column total — the header says |
|
Rework at The tip already sits on main On a fresh extract of
The body already carries Written-by: Rework |
|
Verdict: PASS at 4145800 Ran at this tip: (The extract carries no Red first: on a fresh extract of 4145800 the three changed sources — Why it is right: the Done column is one archive page, not the history — Scope ( CI at this tip: all 14 check-runs success, including Metadata: By the computed tier this is T1 and needs no PASS; I record one anyway because the task asked the tip to be judged before it queues. |
The Done column is one archive page of 20 rows, but both the column
heading and a Bot's rail row printed that page as if it were the
total: 45 finished tasks read as 20 and 20/22.
When the archive holds a full page (ARCHIVE_SHOWN), the Done heading
now reads "20 newest" and the rail reads "20+ done"; a page under the
limit keeps {done}/{total} exactly as before. The archive read and the
20 drawn cards are unchanged.
Fixes ThinkFlowLab#2045
4145800 to
5878994
Compare
|
Verdict: PASS at 5878994 Ran at this tip: The extracts carry no Red first, at this tip. On a fresh extract of Scope, from At this tip GitHub reports 13 check-runs, all Why it is right: Not run here: the full |
main moved after this branch was cut: 8121c6e (ThinkFlowLab#2047) gives the board's archive page its own heading and rail counts, and it edits the same i18n table, the generated client bundle and the ds-bot patch series as the Done group's control does. This merge keeps main's lines and the branch's, and the artifacts are what `node core/scripts/build-client.mjs --check` and `node scripts/ds-bot-patches.mjs check` accept on the merged tree.
…FlowLab#2053) Fixes ThinkFlowLab#2052 Written-by: Dev-2 Owner: Dev-2 Reviewer: Birfy Claim-Id: be2dc4c36a5c4f11ad5077ed05de9ec7 Branch: dev/2-decisiondir-card-binding Base: 8121c6e ## What changed A decided meeting names its decision file two ways. Once it archived into a folder of its own the record carries `decisionDir`; a meeting written before ThinkFlowLab#1809 carries an empty one and its file sits in the Project's own `decisions/` folder. `decisionsOf` registered a meeting's cards only when `archivedOf(meeting)` resolved, and that answers `undefined` for the empty shape (`core/src/host/project-board.js:251-266`), so the `if (archived === undefined) continue` skipped `cardsOf.set` and the row was listed from the folder scan with **no** cards behind it. At the open path `entry.cards.get(name)` was then `undefined`, and `decisionVisible(project, actor, undefined)` answered through the Project role list, so a Project member who could not see the meeting's card still read its text — while the same file, saved into a folder, answered `not listed` to that same reader. One field differed between the two runs. After this change the meetings loop keeps those records aside in a `named` map, keyed by `text(meeting.decisionFile)` and accepted only through `isDecisionPath`, first meeting wins per path (`:289-303`); `decisionsOf` still `continue`s past them. Once the folder listing is deduped, the cards are bound for exactly those names — only for a path this read actually listed, and only when `cardsOf` has no entry yet (`:319-326`). `decisionVisible` is untouched and stays a pure predicate. The single direction this opens is the one ThinkFlowLab#2041 already allows for a record that saved a folder: a card's own party may read the decision without a Project role, because the cards are the door. That is not a new surface, and the tests assert it as a control — the same fixture run with `decisionDir` set to that same folder produces the same two answers (`not listed` for the unrelated member, the text for the card's party). Unchanged on purpose: a record with `taskIds: []` still answers through the role list (`decisionVisible`'s `cards.length === 0` branch is the only door for it, and a test pins it), a name the folder did not list still opens nothing (no ghost rows), a record of one Project puts no cards on another Project's same file name, and when two records name one file the one that saved a folder keeps supplying its cards — the backfill only fills a hole, so the answer is the same across reads. Line numbers are this tip, not the base. ## Line by line - `core/src/host/project-board.js` — +18 / −1: the `named` map and the pre-ThinkFlowLab#1809 branch inside the meetings loop; the backfill loop after `deduped` is built. `decisionVisible` and `archivedOf` are byte-identical to the base. - `core/test/project-board.test.js` — +176: five cases added to the existing file. - `docs/subsystems/core-host.md` — +1 / −1: the Project-board paragraph gains one sentence saying a meeting that saved no folder keeps the cards it decided. - `changelog.d/2052-decisiondir-card-binding.fixed.md` — +1. - `patches/ds-bot/files/src/host/project-board.js` and `patches/ds-bot/files/test/project-board.test.js` — +18/−1 and +176: the generated mirrors, written by `node scripts/ds-bot-patches.mjs generate`, never hand-edited. The five cases, all prefixed `ThinkFlowLab#2052: `: 1. `a record that saved no folder keeps the cards it decided, like one that saved a folder` — the member without the card gets `not listed`, the card's party gets the text, and the same fixture with `decisionDir` set reads identically. 2. `a record that decided no card still answers through the role list` — `taskIds: []`: a Project member gets the text, an outsider gets `not listed`. 3. `a record naming a file the folder did not list opens nothing` — a ghost name cannot be opened. 4. `a record of one Project puts no cards on another Project name` — the backfill is per Project. 5. `two records naming one file keep the cards the first one decided` — the archived record wins, across two reads. ## Red first On a clean checkout of this pull request's base `8121c6e5d503fb14e304dad8289f32d2b8d5ee70` carrying only the new test file (`git status` shows that one path): **86 tests, 84 pass / 2 fail**, and the two failures are exactly the two new cases that need the binding: - `ThinkFlowLab#2052: a record that saved no folder keeps the cards it decided, like one that saved a folder` — the unrelated member reads the text instead of `not listed`. - `ThinkFlowLab#2052: a record of one Project puts no cards on another Project name` — the empty-folder record answers through the role list before the backfill exists. The other three new cases and the file's existing 81 cases pass at the base; that is the point of them (they pin what must not change). At the tip the same two files are **86 tests / 86 pass / 0 fail**. ## Mutations: each arm reds only its own case Each mutation was applied to `core/src/host/project-board.js` alone, the two test files run, then the file restored from git (tree clean afterwards): | mutation | cases that went red | | --- | --- | | A: drop the backfill loop (`named` collected, nothing bound) | case 1 and case 4 | | B: the archived branch stops being first-wins (`!cardsOf.has` dropped) | case 5 | | C: an empty card list stops answering through the role list (`cards.length === 0 → false`) | case 2 | A shows the backfill is what carries the cards; B shows the ordering rule is the one holding "a record that saved a folder always supplies the cards"; C shows the reverse assertion is real — without the role-list fallback a record that decided no card would be card-gated against an empty list. ## Verified on the tree this tip carries Base note: `main` moved from `8378222d` to `8121c6e5` (the squash of my own ThinkFlowLab#2047) between the first push and this run. The push helper overlaid these six paths onto the new base with no overlap — the tree is unchanged — and every gate below, including the red-first run, was measured on `8121c6e5`. `TZ=UTC ARCH_DOCS_BASE=8121c6e5d503fb14e304dad8289f32d2b8d5ee70 npm run verify` in the clean tree at `0d017373324feacd1bf4c63702a9dd02fed99e5e` (tree `958aa8dba21d99f76b97634a44b73499868dfe17`), exit 0. The pushed tip is that same tree re-signed by the push helper, so its sha differs from the one the checks ran at and its content does not. - core `2501/2501`, social `541` (`540` pass + `1` skip), tree `127` (`126` + `1`), timeline `135` (`134` + `1`), workflow `336/336`, `.github/scripts` `563/563`, interop `37/37`, live-e2e `24/24`. Skip counts are host-dependent and are stated as this run produced them. - `arch-docs: OK (6 changed file(s))` against base `8121c6e5`; core lint `73 warnings and 0 errors`, none in a line this diff adds; `build-client --check` exit 0. - `ok 10 patches apply cleanly in order`; `ok core/ equals ds-bot/ + patches/ds-bot/`; `ok patches/ds-bot/ is what generate writes (generate is a no-op)`; `generate` left the tree byte-identical. - Not run: the smoke bench (`core/dev/bench.sh smoke`). I tried it because the change is under `src/host/` and its `dsh web` did not start in this checkout (`web.log` empty, the bench reports "dsh web did not start"); the bench was stopped afterwards. The diff is one data-binding loop in `decisionsOf` plus tests — no endpoint, dispatch, turn or delivery path is touched. ## Scope One function in `core/src/host/project-board.js` (`decisionsOf`), the tests, one sentence of subsystem doc, one changelog fragment, and the two generated mirrors. `decisionVisible`, `archivedOf`, the symlink/`..` rules of ThinkFlowLab#2038, the `project-decision` reader and the folder listing are untouched; no row is invented and no name gets a card it did not have. ## Checkpoint <!-- flybot:checkpoint --> ```json { "pr": 2053, "claim": "Dev-2", "issue": 2052, "summary": "A decided meeting written before ThinkFlowLab#1809 names no decisionDir, so its file sat in the Project folder with no cards bound to it and opening the name fell back to the role list, letting a member who could not see the card read the text. The meeting loop now keeps those records by file name and binds their cards after the folder listing is deduped, only for a name this read listed and only where no record that saved a folder already supplied cards. A record that decided no card still answers through the role list, and a name the folder did not list still opens nothing.", "files": [ "core/src/host/project-board.js", "core/test/project-board.test.js", "docs/subsystems/core-host.md", "changelog.d/2052-decisiondir-card-binding.fixed.md", "patches/ds-bot/files/src/host/project-board.js", "patches/ds-bot/files/test/project-board.test.js" ], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=8121c6e5d503fb14e304dad8289f32d2b8d5ee70 npm run verify", "result": "green", "at": "0d017373324feacd1bf4c63702a9dd02fed99e5e" }, "next": "none — ready for review" } ``` <!-- /flybot:checkpoint -->
…ab#2051) Fixes ThinkFlowLab#2050 Written-by: Dev-1 Owner: Dev-1 Reviewer: Birfy Claim-Id: fd33c02bd8dc4ad6ad8cc5c2cece3b97 Branch: dev/1-inbox-show-all Base: 212f151 Tip SHA: 3ae312a ## What happens today `ThinkFlowLab#2042` made the Inbox state how many finished tasks the Done group hides, but the model dropped those rows (`inbox.js:361` returns `done: settled.slice(0, DONE_MAX)`), so the count line was dead text. With 29 finished tasks the group draws 20, says `9 more not shown`, and the last nine are unreachable anywhere in the UI: no control there, and Settings lists only scheduled tasks. ## The change Three files in `core/src/client/`, plus regenerated artifacts: - `inbox.js` — the model carries `doneAll: settled` beside `done`, and the Done group draws it in place when the owner asks. A `Show all` / `Show fewer` button (`bt-inbox-show-all`, `aria-expanded`) flips it. Collapsed, and at or under the cap, the group is exactly as today: 20 rows plus the same `N more not shown` line; expanded, `hidden` is 0 and that line is gone. `DONE_MAX` is unchanged, the host path is untouched, and there is no pagination. - `i18n.js` — `Show all` / `Show fewer` (English key, Chinese copy). - `docs/subsystems/core-client.md` — one sentence on `doneAll` and the control; `changelog.d/2050-inbox-show-all.fixed.md` carries the user-facing note. `core/client.js` and the `patches/ds-bot/` mirrors are regenerated by `build-client` / `generate`, never hand-edited. ## Red first On the new base `212f1515` with every other file of this branch at the base copy and only `core/test/inbox.test.js` added (`node --import ./test/harness-resolve.mjs --test test/inbox.test.js`): **32 tests / 30 pass / 2 fail, three runs identical**. - `ThinkFlowLab#2050: the model keeps the finished rows past the cap, in the order the Done group draws` → `Cannot read properties of undefined (reading 'length')` — there is no `doneAll`. - `ThinkFlowLab#2050: the Done group opens every row it counted, and folds back to the cap` → `one control offers the hidden rows: 0 !== 1` — the count line is dead text. Mutations, each applied alone and then reverted: show the control under the cap → 31/1; ignore the toggle when drawing → 31/1; drop `doneAll` from the model → 30/2; pin the label to `Show all` → 31/1. The `ThinkFlowLab#2042` assertions (`20 newest`, the `5 more not shown` line, the empty group) stay green unchanged. ## Verified `TZ=UTC ARCH_DOCS_BASE=212f1515 npm run verify` → exit 0: core **2503/2503** (the base has 2501; this branch adds the two ThinkFlowLab#2050 tests), social 541, tree 127, timeline 135, workflow 336, `.github` 563, interop 37, live-e2e 24; lint 73 / 36 / 9 / 2 / 16 warnings, 0 errors; `build-client.mjs --check` 0; four ok lines from two commands — `ds-bot-patches check` prints three (`ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70`, 10 patches apply cleanly in order, `core/` equals `ds-bot/` + `patches/ds-bot/`) and `ds-bot-patches canonical` prints one (`patches/ds-bot/` is what `generate` writes, so a second `generate` is a no-op); `arch-docs: OK (10 changed file(s))`. ## Which tree the checks ran on Verify and `arch-docs` ran at local commit `5bd661e6423bc798dfdecf93c829819c054e7064` (tree `b21c3b80be830ddee37be52f0bab3b395c7b796f`), single parent `212f1515`; `arch-docs` was also re-run on the committed tree (`ARCH_DOCS_BASE=212f1515`) after the amend and printed the same line. The pushed head is rebuilt from that same tree by the push path, so the tip's tree is identical although its sha differs; `checks.at` names the commit the checks actually ran at. ## Not run The smoke test (nothing under `core/index.js` or `core/src/host/` changed) and the packaged Desktop install path. ## Replay onto main `main` moved twice after this branch was cut — to `8121c6e5` (ThinkFlowLab#2047, the board's archive page counts) and then to `212f1515` (ThinkFlowLab#2053, the project board's card binding) — and both touch the same i18n table, the generated `core/client.js` and the `patches/ds-bot/` series. The branch is replayed as one rebased commit on `212f1515`: no merge commit, and no `main` commit in its history. - `i18n.js` keeps both sides' keys (`Show all` / `Show fewer` beside ThinkFlowLab#2047's), and `docs/subsystems/core-client.md` keeps both rows' sentences. - `patches/ds-bot/` was reset to `212f1515` and rebuilt, never hand-merged: `node core/scripts/build-client.mjs`, then `node scripts/ds-bot-patches.mjs generate`, `check` → `ok 10 patches apply cleanly in order` and `ok core/ equals ds-bot/ + patches/ds-bot/`, `canonical` → `ok patches/ds-bot/ is what generate writes (generate is a no-op)`, `build-client.mjs --check` → exit 0. - Red first holds on the new base: with the base `inbox.js` in place and only the test added, **32 / 30 / 2 in three identical runs** (same two failures, same texts above); with the change, `core/test/inbox.test.js` is 32/32 and `core/test/kanban.test.js` 11/11, so ThinkFlowLab#2047's tests still pass beside this change. - The full `verify` above re-ran on this tree at the new base; nothing green is carried over from the old one. ## Checkpoint <!-- flybot:checkpoint --> { "pr": 2051, "claim": "Dev-1", "issue": 2050, "summary": "The Inbox Done group drew 20 finished rows and said how many it hid, but the rows were gone from the model, so nothing could open them. The model now also carries doneAll, every finished row in the same order, and the group draws it in place behind a Show all / Show fewer control. Collapsed, and at or under the cap, the group renders exactly as before, and DONE_MAX and the host path are unchanged. Two tests cover the model and the toggle and both fail without the change.", "files": [ "changelog.d/2050-inbox-show-all.fixed.md", "core/client.js", "core/src/client/i18n.js", "core/src/client/inbox.js", "core/test/inbox.test.js", "docs/subsystems/core-client.md", "patches/ds-bot/0001-core-fixes.patch", "patches/ds-bot/0008-client-bundle.patch", "patches/ds-bot/files/src/client/inbox.js", "patches/ds-bot/files/test/inbox.test.js" ], "checks": { "ran": "local, on the pushed tree: full TZ=UTC ARCH_DOCS_BASE=212f1515 npm run verify (exit 0), build-client --check, ds-bot-patches check + canonical, inbox 32/32 (30/32 red first), kanban 11/11", "result": "green", "at": "5bd661e6423bc798dfdecf93c829819c054e7064" }, "next": "none — ready for review: CI at the rebased tip, then enqueue" } <!-- /flybot:checkpoint -->
Fixes #2045
Written-by: Dev-2
Owner: Dev-2
Reviewer: Birfy
Claim-Id: a5ffea02fd464c5d9f892275304fa8f2
Branch: dev/2-board-archive-counts
Base: 8378222
Corrected after merge: this body now describes the tip that actually merged,
58789941, on base8378222d. The squash commit8121c6e5onmainkeeps the body the pull request carried when it entered the queue, and its tree (d66f183d) is the tree every reading below was taken on.What changed
The Done column of a Bot's task board is not the history; it is one archive page of
ARCHIVE_SHOWN = 20cards (core/src/client/kanban.js:28), read with{ ...archiveQuery, archive: true, limit: ARCHIVE_SHOWN }(:190). Both places that count Done printed that page as if it were the total: the column heading drewString(tasks.length)(:299before this change), and a Bot's rail row added the page to the active list and printed{done}/{total}(:382-389). So a Bot with 45 finished tasks read Done 20 and 20/22 — two numbers that look like facts about the Bot and are only facts about the page. The host's archive read is bounded and returns no total (core/src/host/tasks.js:1405-1413), and this change does not ask it to.BoardViewderives one flag from the page it was handed:const archiveFull = Array.isArray(archive) && archive.length >= ARCHIVE_SHOWN(kanban.js:391-393). A full page is a cut; a page under the limit is the whole history.>=and not>on purpose: at exactly twenty the board cannot know whether a 21st settled card exists, so the full-page wording starts at the page size itself.column === 'done' && archiveFull ? t('{count} newest', { count: tasks.length }) : String(tasks.length)(:301), passed down asarchiveFullthroughBoardView(:348) toBoardColumn(:281). Not started and In progress are the active list, not a page, so they keep plain numbers.const capped = archiveFull && count.done >= ARCHIVE_SHOWN(:329),capped ? t('{count}+ done', { count: count.done }) : t('{done}/{total}')(:332). Under the limit it is the same3/5string, byte for byte.for (const task of [...own, ...(archive ?? []).slice(0, ARCHIVE_SHOWN)])(:398), so a card finished optimistically that is not in the archive page does not inflate the total.'{count} newest'/最新 {count} 张and'{count}+ done'/{count}+ 已完成(core/src/client/i18n.js:229-230).{ botId | roomId, archive: true, limit: ARCHIVE_SHOWN }, the group board reads the same page, and the host is untouched. The empty board and a short page read exactly as before.Line numbers are this tip, not the base.
Line by line
core/src/client/kanban.js— +22 / −7:archiveFullout ofarchive, the heading branch, the rail branch, the rail's own count slice.core/test/kanban.test.js— +61: two cases (a full page on a Bot board and on a group board; a short page, the exactly-twenty boundary and one row under it).core/src/client/i18n.js— +2: the two ZH entries.core/client.js— +15 / −6: generated bynode core/scripts/build-client.mjs, never hand-edited.patches/ds-bot/0008-client-bundle.patch— +17 / −8: the sameclient.jsedit, re-hunked against ds-bot's copy bynode scripts/ds-bot-patches.mjs generate, never hand-edited.patches/ds-bot/0001-core-fixes.patch— +3 / −1: the i18n entries in ds-bot's own patch, generated.patches/ds-bot/files/src/client/kanban.js— +22 / −7: the source's generated mirror.patches/ds-bot/files/test/kanban.test.js— +61: the test file's generated mirror.docs/subsystems/core-client.md— +1 / −1: the ownership row forkanban.jsnow says what the two numbers mean.changelog.d/2045-board-archive-counts.fixed.md— +1.Base
This branch has been rebased three times, and never by editing a generated file. It was first written on
dfc4af7aand held unopened while#2044merged, because that pull request also rewritescore/client.jsandpatches/ds-bot/0008-client-bundle.patch; it was moved onto3eca1e54(that merge) and opened.#2046then landed as816d1a8b(single parent3eca1e54) and collided with the same artifacts, so it moved onto816d1a8b.#2049then landed as8378222d(single parent816d1a8b, my own Bot own-chat dock change) and collided again oncore/client.js,patches/ds-bot/0001-core-fixes.patch,0008,core/src/client/i18n.jsanddocs/subsystems/core-client.md, so the final move is onto8378222d.Each time: the hand-written paths (
kanban.js, the test, the i18n entries, the doc sentence, the changelog fragment) were re-applied by line —#2046's two i18n keys and itscore-client.mdsection, and#2049's two dock keys and its dock sentence, are other lines and are untouched —patches/ds-bot/was reset to the new base, and every generated artifact (core/client.js,0008,0001, bothpatches/ds-bot/files/mirrors) was rebuilt bybuild-client+generate. Nothing underpatches/or incore/client.jswas resolved or edited by hand. The file set is unchanged by every move: the same ten paths,+205/−30against this base. Every gate below, and the red-first run itself, was measured on this pull request's real base8378222d.Red first
On a clean checkout of this pull request's base
8378222d7f2c26b88fce6e64723eaac7250c6547carrying only the new test file (git statusshows that one path): 11 tests, 9 pass / 2 fail, and the two failures are the two new cases:#2045: a full archive page says how many rows were drawn and that there are more…fails onthe heading says these are the newest twenty, not the total:false !== true— the heading has no way to say the page was cut.#2045: a page that is not full still reads as the whole history, unchangedfails ona page at the limit is treated as cut:'20/22' !== '20+ done'— a full page is presented as a total.The other nine are the file's existing cases, unchanged. At the tip the same file is 11 tests / 11 pass / 0 fail.
The tests bite: five mutations, each caught
Each mutation was applied to
kanban.jsalone, the file run with the new cases, then restored (tree clean afterwards):capped = false)>=→>)capped = count.done > 0)not-started/in-progressplain-count assertions)A is caught by the heading, B by the rail, C and D by the boundary pair (exactly twenty must hedge, nineteen must not), and E only by the two "keeps a plain count" assertions — which is why they are in the full-page case: the hedge must not leak to a column that is the active list.
The generated patch: 0008 is a clean re-hunk now
core/client.jschanges by +15/−6 andpatches/ds-bot/0008-client-bundle.patchby +17/−8, with no added line byte-identical to a removed line of that diff (17 added, 8 deleted, zero overlap) —#2044already re-based the bundle text, so this hunk sits on top of it and the bundle is not re-paired around it. The gates prove the result:ok 10 patches apply cleanly in order,ok core/ equals ds-bot/ + patches/ds-bot/,ok patches/ds-bot/ is what generate writes (generate is a no-op). Nothing inpatches/was hand-edited.Verified on the tree this tip carries
TZ=UTC ARCH_DOCS_BASE=8378222d7f2c26b88fce6e64723eaac7250c6547 npm run verifyin the clean tree at568b0605249ec5039b8d9b9afac1267de6b386a5(treed66f183d474387ea73b5123f83a6c54e3b296978), exit 0. The pushed tip carries that same tree: the push re-signs the commit, so the tip's sha differs from the one the checks ran at and nothing else does.2496/2496, social541(540pass +1skip), tree127(126+1), timeline135(134+1), workflow336/336,.github/scripts563/563, interop37/37, live-e2e24/24. Skip counts are host-dependent and are stated as this run produced them.arch-docs: OK (10 changed file(s))against base8378222d; core lint73 warnings and 0 errors, none in a line this diff adds.ok 10 patches apply cleanly in order;ok core/ equals ds-bot/ + patches/ds-bot/;ok patches/ds-bot/ is what generate writes (generate is a no-op);node core/scripts/build-client.mjs --checkexit 0.generatethe tree stayed byte-identical (core/client.jsmd571f16754147d298d4ee52cad172f543a,0001-core-fixes.patchmd551be3324827bb21a9d1fe0d151a34f99,0008-client-bundle.patchmd5fa195602a6852f5b3a11e7300d35d144).dev/bench.sh smoke) — the diff is two renderings of a count, two i18n entries and the generated artifacts, with noindex.js, host, turn, store or archive-query change.Scope
Two numbers on the task board and their two i18n keys, the generated artifacts, the tests, one doc row and one changelog fragment. The archive read, its
limit, the number of cards drawn, the host's return shape, the group board's scoping and every other column's count are untouched.Checkpoint
{ "pr": 2047, "claim": "Dev-2", "issue": 2045, "summary": "The task board's Done column is one 20-card archive page, but the column heading and a Bot's rail row printed that page as the total, so 45 finished tasks read as 20 and 20/22. When the page is full the heading now reads 20 newest and the rail reads 20+ done; under the limit both keep exactly today's strings, and the exactly-twenty boundary counts as full because a 21st card cannot be ruled out. The archive read, the twenty drawn cards and the host return shape are unchanged, and the group board says the same thing. Five mutations of the source each red their own case.", "files": [ "core/src/client/kanban.js", "core/src/client/i18n.js", "core/test/kanban.test.js", "core/client.js", "patches/ds-bot/0001-core-fixes.patch", "patches/ds-bot/0008-client-bundle.patch", "patches/ds-bot/files/src/client/kanban.js", "patches/ds-bot/files/test/kanban.test.js", "docs/subsystems/core-client.md", "changelog.d/2045-board-archive-counts.fixed.md" ], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=8378222d7f2c26b88fce6e64723eaac7250c6547 npm run verify", "result": "green", "at": "568b0605249ec5039b8d9b9afac1267de6b386a5" }, "next": "none — ready for review" }