Skip to content

A decided meeting with an empty decisionDir loses its cards, so its decision text is open to the wrong readers #2052

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: be2dc4c36a5c4f11ad5077ed05de9ec7
Branch: dev/2-decisiondir-card-binding
Base: 8378222
At: 2026-11-01T15:21:21Z

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.
Written-by: FlyBot Lead
Owner: Dev-2
Branch: dev/2-decisiondir-card-binding
Base: 8378222

What happens

A decided meeting written before #1809 names no decisionDir, and its decision file sits in the
Project's own decisions/ folder. decisionsOf registers a meeting's cards only when
archivedOf(meeting) resolves (core/src/host/project-board.js:294 skips every other meeting), and
archivedOf returns undefined when decisionDir is empty (:251-266). Such a row is therefore
listed from the folder scan with no entry in cardsOf.

At the open path, entry.cards.get(name) is undefined (:598), and decisionVisible reads that
as "this name came from no meeting" and answers through the Project role list
(:577 if (taskIds === undefined) return role !== null). The result: a member of the same Project
who cannot see the meeting's card can still read the decision text.

Reproduction

  • Project project-p1, owner owner-1, roles.members: ['owner-3'].
  • Meeting m1: { status: 'decided', decisionDir: '', decisionFile: 'decisions/2026-10-29-meeting.md', taskIds: ['t1'] }.
  • Card t1 belongs to a bot of owner-1, so owner-3 does not see it — the board answers tasks: [].
  • project-decision for that name then answers text: "# decided by the meeting\n" to owner-3.
  • Set decisionDir to the same folder and the same call answers not listed to owner-3.

One field differs between the two runs, so the same file with the same cards behind it is
card-gated in one shape and plain-record-gated in the other. Found by FlyBot Security's
post-merge self-test of #2046 and re-checked by FlyBot Lead against live main 8378222d.

Expected

A row a decided meeting named is judged by that meeting's cards, whichever shape wrote the meeting.
An empty decisionDir must not turn a card-gated decision into a plain record. The direction this
opens is exactly the one #2041 already allows for a record that saved a folder: a card's party may
read it even without a Project role, because the cards are the door. Nothing else widens.

Scope

  • core/src/host/project-board.js, decisionsOf only: collect named from a decided meeting whose
    archivedOf cannot resolve, keyed by text(meeting.decisionFile) and accepted only through
    isDecisionPath; first meeting wins per path. After deduped is built, fill cardsOf from
    named only for paths the read actually listed and only when cardsOf has no entry yet — so a
    record that saved a folder always keeps the cards it supplies, and no row is invented.
  • decisionVisible stays a pure decision and is not edited, and no cards.length > 0 filter is
    added: parity with the folder shape is the point, and taskIds: [] is already answered by the
    role list at :580.
  • core/test/project-board.test.js, five cases: the reproduction with a same-shape contrast
    (decisionDir set to the same folder, same readings); taskIds: [] still answers through the
    role list with the outsider refused; a named file the folder did not list opens nothing; a record
    of one Project puts no cards on another Project's same name; two records naming one file keep the
    first one's cards across two reads.
  • Docs/changelog as usual; the generated mirrors come from generate, never hand-edited.

Non-goals: the #2038 name-shape and symlink rules, the 200-row cut, the refusal wording and the
archivedOf folder rule — none of them change.

Dedup

Search of live main for cardsOf, archivedOf, decisionVisible, decisionDir finds #1809
(archive folder), #2038 (name shape / symlink), #2041 (opening a listed row) and the merged #2046.
None covers a meeting with an empty decisionDir whose file is in the Project folder. Closed
issues checked for the same shape: no match, no duplicate open issue.

Activity

  1. HugoXK commented on Nov 1, 2026

    @HugoXK
    ContributorAuthor

    Release

    Written-by: FlyBot Lead
    Released: be2dc4c36a5c4f11ad5077ed05de9ec7
    Reason: merged as 212f151 (squash of #2053, tree 958aa8db matches the reviewed tip e87ecc4)
    At: 2026-11-01T15:40:44Z

  2. added a commit that references this issue on Nov 1, 2026
    212f151
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