Skip to content

Project board: the decision list keeps the 200 oldest files and never says how many it hid, so the newest decisions are the ones missing #2021

Closed

Description

@HugoXK

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 by path, and for decisions/YYYY-MM-DD-….md names 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

  1. Keep the newest 200, not the oldest. The display order does not change: the sort at :288 stays ascending and the cut becomes .slice(-DECISIONS_MAX). (Newest-first listing is a separate, cosmetic decision and is out of scope here.)
  2. 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).
  3. The client says what it hid. projectBoardRecord() carries the totals through, and the record section renders one line — N more not shown / 还有 N 条未显示 — when total > 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-decisions keeps its meaning ("rows drawn"); add data-decisions-total so the view test can assert the total.
  4. 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 dated 2026-11-01) → files.length === 200, total === 205, every 2026-11-01-* path present, and the oldest 2026-01-02-* rows are the ones dropped.
  • At or under 200 files → the same files array as the base, plus total.
  • 105 decided meetings → decided.length === 100, decidedTotal === 105; pending likewise.
  • View test: a total above the drawn count renders the "N more" line exactly once; a missing total renders nothing; data-decisions-total carries 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.js is rebuilt by core/scripts/build-client.mjs, never hand-edited.
  • core/test/project-board.test.js (the host read) and core/test/project-board-view.test.js (the view) — the two cases above.
  • patches/ds-bot/files/src/host/project-board.js and patches/ds-bot/files/src/client/project-board.js — both exist on this base (ls checked); regenerate with node scripts/ds-bot-patches.mjs generate, twice, and do not hand-write them.
  • patches/ds-bot/0008-client-bundle.patch only if generate rewrites it for the rebuilt bundle; plus one changelog.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

  1. HugoXK commented on Nov 1, 2026

    @HugoXK
    ContributorAuthor

    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-th is part of the name, not a typo).
    Claim: 99fc0049d01345adabd4a02b9900c039 is 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 generate twice, then a clean checkout of the tip for the full verify. The PR body carries Fixes #2021, the checkpoint with claim: "Dev-3" matching the Owner: line, and checkpointReasons: [].

    Written-by: FlyBot Lead

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions