Repository navigation
Project board decision rows can be listed but never opened #2041
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: 95933d41dc0c46eca230c105c4174339
Branch: dev/1-decision-open
Base: 00a3e67
At: 2026-11-01T13:05:13Z
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.
Written-by: FlyBot Lead
What happens today
The Project board lists a project's decision files as rows: each row carries data-path and data-sha256 and nothing else — the row is a plain div, with no click target, no link, no copy action (core/src/client/project-board.js, the decision row in DecisionsSection). So a person reading the board can see that decisions/2026-10-31-…md exists and when it was last written, but cannot open it.
The only way to read a decision's text today is to ask a Bot, which reaches for the project_state tool (core/src/host/project-state.js, readProjectState), which already knows how to answer three states for one file: its text, absent, or too large, with MAX_BYTES bounding the read.
So the panel can name 200 decision files and open none of them.
Expected
- A decision row is reachable and activatable: keyboard focus and activation work, and the row is a button or link rather than an inert
div. - Activating a row expands that decision's text under the row, in place; activating it again collapses it. One row open at a time is enough; the open row must be identifiable in the test view.
- The text comes from a host endpoint that accepts only a name this board read listed — the set the board itself produced for this render, including a name past the 200-row cut. A name outside that set answers
not listedand is never read: nostat, no open, no content in the response. - The three states are the reader's own: text,
absent,too large. A file overMAX_BYTESanswerstoo largerather than being truncated silently. - A row whose file has since been deleted answers
absentand stays a row; it does not become an error banner. - Nothing new is read as a side effect: the endpoint must not become a second, wider read path beside
readProjectState.
Acceptance (red first)
Host tests (core/test/project-board.test.js or the reader's own suite, the Dev's call, recorded in the PR):
- A decision listed by the board opens and returns its text.
- A name outside the listed set (
decisions/../outside.md, an absolute path, a name one level down under the decisions folder, a symlink) answersnot listedand the file is provably not read — the case fails if the host stats or opens it. - A file over the bound answers
too largeand no partial text is returned. - A name that fell past the 200-row cut but is still on disk still opens (this depends on the past-cut fix that is already on main).
- A row whose file was deleted after listing answers
absent.
Client tests (core/test/…client… in the existing style): activating a row asks for that row's path and renders the returned text under the row; activating again collapses it; a not listed/absent answer renders its own sentence and never a stack of raw HTML.
Scope
core/src/host/project-board.js(or the reader beside it): one endpoint that takes a project and a name, answers the reader's three states, and refuses anything not in the listing the board just produced.core/src/client/project-board.js: the decision row becomes activatable, expands the text, collapses on a second activation, keyboard reachable.- Tests on both sides;
docs/subsystems/core-host.mdand the client doc row; changelog fragment. core/client.jsandpatches/ds-bot/0008-client-bundle.patchregenerated bygenerate; the host mirror underpatches/ds-bot/files/src/host/likewise. Nothing else in the generated set may move.- Out of scope: editing a decision, opening more than one at a time, search, and any change to how decisions are written.
Neighbour issues (not this cut)
- 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 / Keep the newest Project board decisions and say how many the cap hid #2023: which 200 rows survive, plus a
decisions.totalcounter. - Project board: past the 200-decision cap a decided meeting on a dropped file reads "decision file gone" #2033 / Name a decided meeting's decision file past the board's 200-row cut #2034: a decided meeting's file that fell past the cut read as gone.
- Project board: read a decision from the folder the meeting archived in #1818: reading a decision from the folder a meeting archived in.
- host: a decision file name can point outside decisions/ and be read, and project_state follows a symlink out of the project #2037 / Refuse a decision name that leaves decisions/ and stop project_state following a symlink #2038: the name rule and the
lstatread — this cut builds on them and must not re-implement them.
None of those gives a person a way to open a decision's text; this one does.
Dedupe
Searched before opening: decision file open/read panel across title and body, 46 / 101 / 19 results scanned, nearest #1809, #1818, #1772, #1749 (all closed, none about reading a decision's body from the board). No issue covers this.
Activity
HugoXK commentedon Nov 1, 2026
Heartbeat
Written-by: FlyBot Lead
Still-working: 95933d41dc0c46eca230c105c4174339
At: 2026-11-01T13:54:04Z
HugoXK commentedon Nov 1, 2026
Heartbeat
Written-by: FlyBot Lead
Still-working: 95933d41dc0c46eca230c105c4174339
At: 2026-11-01T13:54:12Z
host: open a decision file the board listed, from its row (ThinkFlowL…
HugoXK commentedon Nov 1, 2026
Heartbeat
Written-by: FlyBot Lead
Still-working: 95933d41dc0c46eca230c105c4174339
At: 2026-11-01T14:15:32Z
HugoXK commentedon Nov 1, 2026
Release
Written-by: FlyBot Lead
Released: 95933d41dc0c46eca230c105c4174339
Reason: shipped in #2046 (merged 816d1a8)
At: 2026-11-01T14:33:50Z
Open a decision file the board listed, from its row (ThinkFlowLab#2046)
Keep a decided meeting's cards when its record saved no folder (Think…