[fix] Normalize legacy persisted tasks#30
Closed
arrokh wants to merge 1 commit into
Closed
Conversation
arrokh
marked this pull request as ready for review
July 1, 2026 01:55
tintinweb
added a commit
that referenced
this pull request
Jul 22, 2026
Task files written before the blocking feature have no blockedBy/blocks/ metadata. TaskStore.load() deserialized them as-is, so consumers reading those fields unguarded (e.g. task.blockedBy.length in the widget) threw a TypeError. Because the widget renders on a pi-tui timer, the throw was uncaught and killed the whole pi process on the first render after upgrade. - Normalize every record at the load() boundary (normalizeTask): default metadata/blocks/blockedBy, with type guards for hand-edited files. This is the single fix point — the same unguarded accesses exist across the store and index mutation paths, not just the widget. - Wrap the widget render in try/catch returning a safe fallback, so a render error can never again escape to the TUI timer and crash the host. Fixes #33. Normalization approach based on #30 (thanks @arrokh); reported by @eSaadster. Co-authored-by: Noor Octavian (Alvin) Anwar <nooroctaviananwar@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue Details
Pi could exit from an uncaught exception while rendering the task widget:
The failing path was a persisted legacy task record that did not include newer task fields such as
blockedBy,blocks, ormetadata.TaskWidgetand other consumers expectTaskStoreto return fully-shapedTaskobjects, so readingtask.blockedBy.lengthcould throw before the UI could recover.Purpose
Normalize legacy persisted task data at the
TaskStoreload boundary so every consumer receives canonical task objects. This preserves existing widget/tool behavior while making old task files compatible with the current task schema.Summary
TaskStore.metadatato{}and missing dependency arrays to[].nextIdbehavior for persisted task files.Issue → Solution Flow
flowchart TD A[Legacy persisted task file] --> B[TaskStore.load] B --> C{Missing newer fields?\nblockedBy / blocks / metadata} C -->|Before| D[Raw partial task returned] D --> E[TaskWidget.renderWidget] E --> F[task.blockedBy.length] F --> G[Uncaught TypeError\npi exits] C -->|After| H[normalizeTask fills defaults] H --> I[Canonical Task object] I --> J[Widget and tools read arrays safely] J --> K[Task list renders without throwing]Validation
npm test -- --run test/task-store.test.ts test/task-widget.test.ts— 80 tests passed.npm run typecheck— passed.npm run lint— passed.npm run build— passed.npm run prepublishOnly— passed (lint,typecheck,test,build; 170 tests).TaskStorefix for immediate local use.