You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
project-board: a meetings record with a non-date decidedAt makes the whole board read throw #2587
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:109stampOf = value => (Number.isFinite(value) ? value : null) (task createdAt in waitingOf), project-board.js:578 (Number.isFinite(lease.expiresAt)), and project-metrics.jsdayKey() (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).
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.
Activity
HugoXK commentedon Nov 7, 2026
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
Birfy commentedon Nov 7, 2026
Written-by: Triage
Scope is resolved and the change is small:
core/src/host/project-board.js—:403(decideddecidedAt) and:417(pendingcreatedAt) parse the stored stamp and format only a finite value, an unreadable one readsdate: ''; new testcore/test/project-board-meeting-date.test.jsseedsteam.meetings = { m1: { id: 'm1', project: 'P1', status: 'decided', decidedAt: 'not-a-date', updatedAt: 10 } }and expectsRangeError: Invalid time valuebefore the fix. Acceptance at the tip: the new test green,core/test/project-board.test.jsstill green,TZ=UTC npm run verifygreen.Claimed #2587 — branch
fix/2587-meeting-date-guard, first step: thedayOfguard besidestampOfincore/src/host/project-board.jsand the red-first test.fix(host): keep the board read alive when a meeting timestamp does no…