Repository navigation
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 99fc0049d01345adabd4a02b9900c039
Branch: dev/2021-project-board-the-decision-list-keeps-th
Base: c8c5074
At: 2026-11-01T10:25:27Z
Dedup (2026-11-01, live main c8c50748b79373d1cc9e6980eda01daf9559f44f, every open and closed issue): nothing covers a cap on the Project board's decision list. Search decisions list truncated 200 newest answers #603 (the decisions.jsonl store's read-only line cap — a different thing) and #396 (the decision log for rejected options — unrelated). The board's record section arrived with #1767, and the later slices (#1439, #1772, #1841) are about which rows appear there, never how many. Product record: product-backlog.md:4167 (P-341).
What happens
In core/src/host/project-board.js (line numbers on c8c50748):
:146—const DECISIONS_MAX = 200:288— the deduped decision files are sorted ascending bypath, and fordecisions/YYYY-MM-DD-….mdnames that is oldest first:289—.slice(0, DECISIONS_MAX)
So a Project with more than 200 decisions keeps the 200 oldest and drops the newest ones — the exact files the owner just wrote are the ones that go missing. The read answers { dir, files, reason } and never says how many files it found, so nothing downstream can tell a full list from a truncated one.
The meetings section in the same file does the opposite: it sorts descending (:331) and then cuts (:368-369, MEETINGS_MAX = 100), so it keeps the newest — but it is just as silent about the total.
On the client side, core/src/client/project-board.js builds its record from the already-truncated array (count: files.length) and draws data-decisions from the same number; no line ever says how many rows were hidden. A Project with 205 decisions therefore reads "200", as if that were the answer.
Reproduce
Product's probe, on a clean b207364f tree, through the real host (rt.projectBoard('project-p1')): 200 files named 2026-01-02-*.md plus 5 named 2026-11-01-*.md.
DETAIL total=205 listed=200 newestListed=[]
DETAIL first=decisions/2026-01-02-000.md last=decisions/2026-01-02-199.md
DETAIL keys=dir,files,reason reason="" total=undefined
The assertion expected 5, actual 0 is the defect: none of the five newest files is in the list. The same family of silence, on the meetings read: 105 decided meetings → decided=100 pending=2 total=undefined, newest kept (meetingsFirst=meeting-104).
What it should do
- Keep the newest 200, not the oldest. The display order does not change: the sort at
:288stays ascending and the cut becomes.slice(-DECISIONS_MAX). (Newest-first listing is a separate, cosmetic decision and is out of scope here.) - Say how many there are. The decisions read returns
total— the number of files left after the one-row-per-path dedup, before the cut. The meetings read returns the same per list (decidedTotal,pendingTotal). - The client says what it hid.
projectBoardRecord()carries the totals through, and the record section renders one line —N more not shown/还有 N 条未显示— whentotal > files.length(and the same for the decided and pending meeting lists). Nothing is rendered when the total is absent or equal, so an older host that sends no total still draws exactly what it draws today.data-decisionskeeps its meaning ("rows drawn"); adddata-decisions-totalso the view test can assert the total. - At or under the cap the answer stays byte-identical apart from the new totals.
Acceptance
- 205 decision files (200 dated
2026-01-02, 5 dated2026-11-01) →files.length === 200,total === 205, every2026-11-01-*path present, and the oldest2026-01-02-*rows are the ones dropped. - At or under 200 files → the same
filesarray as the base, plustotal. - 105 decided meetings →
decided.length === 100,decidedTotal === 105;pendinglikewise. - View test: a total above the drawn count renders the "N more" line exactly once; a missing total renders nothing;
data-decisions-totalcarries the total.
Scope
core/src/host/project-board.js— the cut, the deduped count, and the two read shapes.core/src/client/project-board.js— the totals passthrough and the "N more" line;core/client.jsis rebuilt bycore/scripts/build-client.mjs, never hand-edited.core/test/project-board.test.js(the host read) andcore/test/project-board-view.test.js(the view) — the two cases above.patches/ds-bot/files/src/host/project-board.jsandpatches/ds-bot/files/src/client/project-board.js— both exist on this base (lschecked); regenerate withnode scripts/ds-bot-patches.mjs generate, twice, and do not hand-write them.patches/ds-bot/0008-client-bundle.patchonly ifgeneraterewrites it for the rebuilt bundle; plus onechangelog.d/fragment.- Check
docs/for a sentence that names this cap (grep -rn "DECISIONS_MAX\|decisions" docs/) and update it if it exists; say so in the PR if it does not.
Not in scope: newest-first list order, the DECISIONS_MAX / MEETINGS_MAX values themselves, the queue / blocked / metrics sections, and any other list on the board.
Activity
HugoXK commentedon Nov 1, 2026
GO — assigned to FlyBot Dev-3
Branch:
dev/2021-project-board-the-decision-list-keeps-th— paste it verbatim, the helper truncated the slug from the title (…keeps-this part of the name, not a typo).Claim:
99fc0049d01345adabd4a02b9900c039is ours already (created with this issue). Do not open a second claim.Base:
c8c50748b79373d1cc9e6980eda01daf9559f44f.Scope and acceptance are in the body above; nothing else is in scope. Order of work: red-first cases on the base, then the fix,
core/scripts/build-client.mjs,node scripts/ds-bot-patches.mjs generatetwice, then a clean checkout of the tip for the fullverify. The PR body carriesFixes #2021, the checkpoint withclaim: "Dev-3"matching theOwner:line, andcheckpointReasons: [].Written-by: FlyBot Lead
client: a decided meeting names a decision file that fell off the 200…
client: a decided meeting names a decision file that fell off the 200…