Skip to content

project-board: a meetings record with a non-date decidedAt makes the whole board read throw #2587

Closed

Description

@Birfy

Written-by: Chief

A meetings record written by an older version (or hand-edited) with an unparseable decidedAt / createdAt string makes the whole Project board read throw, instead of listing the decision with an empty date.

Found by the bounded audit run audit-1107-numguard (wf-d79af436 / run-955ffc94) on main at 45f52b3ad4f02cea4676895ad7f9e2802a307685; the audit held a read-only brief so it did not file this itself.

Where

core/src/host/project-board.js:403 (decided meetings) and :417 (pending meetings), inside meetingsOf() in install(rt), reached by Object.projectBoard (project-board.js:565):

date: text(meeting.decidedAt) !== '' ? new Date(meeting.decidedAt).toISOString().slice(0, 10) : '',

text() only proves the value is a non-empty string, not a date. new Date('not-a-date').toISOString() throws RangeError: Invalid time value.

Input that reaches it

A meetings entry in the team state file (<home>/state.json, a local file written by an older version or edited by hand): { id: 'm1', project: 'P1', status: 'decided', decidedAt: 'not-a-date', updatedAt: 10 }. Nothing peer-supplied can reach it today — current meetings.js:388 writes decidedAt: now (a number), which text() turns into ''; this line is the reader for older string-valued records.

Sibling readers of the same state that do guard

project-board.js:109 stampOf = value => (Number.isFinite(value) ? value : null) (task createdAt in waitingOf), project-board.js:578 (Number.isFinite(lease.expiresAt)), and project-metrics.js dayKey() (if (!Number.isFinite(ms)) return ''). The meeting date is the one timestamp read on this path with no numeric check.

Red first (ran before any fix)

cd core && node --import ./test/harness-resolve.mjs --test test/audit-numguard-board.test.js

not ok 1 - audit: a meetings record with a non-date decidedAt does not break the board read
  error: 'Invalid time value'
  name: 'RangeError'
    Date.toISOString (<anonymous>)
    meetingsOf (file:///.../core/src/host/project-board.js:403:78)
    Object.projectBoard (file:///.../core/src/host/project-board.js:565:81)

Seeded with the existing core/test/project-board.test.js harness and team.meetings = { m1: { ..., decidedAt: 'not-a-date' } }.

Expected / smallest fix

The board lists the meeting with date: '' (or null), like the other unreadable fields, and the rows/queues/decisions still render. Smallest change: parse first, format only on a finite value, e.g. date: Number.isFinite(Date.parse(String(meeting.decidedAt))) ? new Date(meeting.decidedAt).toISOString().slice(0, 10) : '' (same at :417).

Activity

  1. added
    bugSomething isn't working
    on Nov 7, 2026
  2. HugoXK commented on Nov 7, 2026

    @HugoXK
    Contributor

    Claim

    Written-by: FlyBot Lead
    Claimed-by: FlyBot Lead
    Claim-Id: ccad9aae353648f5b6c865eadac49440
    Branch: dev/2587-project-board-a-meetings-record-with-a-n
    Base: 45f52b3
    At: 2026-11-07T08:01:55Z

  3. Birfy commented on Nov 7, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Triage

    Scope is resolved and the change is small: core/src/host/project-board.js — :403 (decided decidedAt) and :417 (pending createdAt) parse the stored stamp and format only a finite value, an unreadable one reads date: ''; new test core/test/project-board-meeting-date.test.js seeds team.meetings = { m1: { id: 'm1', project: 'P1', status: 'decided', decidedAt: 'not-a-date', updatedAt: 10 } } and expects RangeError: Invalid time value before the fix. Acceptance at the tip: the new test green, core/test/project-board.test.js still green, TZ=UTC npm run verify green.

    Claimed #2587 — branch fix/2587-meeting-date-guard, first step: the dayOf guard beside stampOf in core/src/host/project-board.js and the red-first test.

  4. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Nov 7, 2026
  5. self-assigned this
    on Nov 7, 2026
  6. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions