Fix: loader coerces blank numeric frontmatter to 0 instead of documented defaults

Closed Beginner friendly
#543 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

A pull request for this has already been merged.

  • #559 by @Kuldeep2822k — merged

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
85/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
node.js, typescript
Domain
cli, testing

Research direction

Start with the numeric parsers in src/storage/loader.ts and compare their blank string handling with parseScore, then read the documented defaults in docs/05-1-topic-and-assessment-schema.md. Update the tests in test/storage-loader.test.ts to cover blank numeric fields. Done when blank values load as the stated defaults, including last_quality as null.

Written by the indexing model from the issue text.

Description

bug priority: medium storage

Loader coerces blank numeric frontmatter fields to 0 instead of documented defaults

This is subtle and easy to miss. If a topic note has interval_days: "" (empty
string, maybe from a botched manual edit or a YAML quirk), the loader silently
turns it into 0 instead of the documented default of 1. Same for
ease_factor → 0 instead of 2.5, and last_quality → 0 instead of
null.

The difference matters: last_quality: 0 means "reviewed, quality 0" while
last_quality: null means "never reviewed." The loader is turning the latter
into the former.

Reproduction
# Edit a topic note to have blank numeric fields:
# interval_days: ""
# ease_factor: ""
# last_quality: ""
palee next --json
# The topic loads with mastery 0.0 instead of using defaults
Root cause

src/storage/loader.ts:

  • parseScore (line 145-153) does guard blank strings:
    if (trimmed) {
      return Number(trimmed);
    }
    return fallback;
    
  • But parseInteger (line 180-182), parseOptionalInteger (212-214), and
    parseNumber (240-242) do not:
    return Number(val.trim()); // Number('') === 0
    

Call sites at loader.ts:386-390 pass fallbacks (2.5 for ease_factor, 1 for
interval_days, 0 for repetition, null for last_quality), but the unguarded
parsers return 0 for empty strings before the fallback is ever consulted.

parseOptionalInteger's own docstring (loader.ts:189-191) says it exists to
preserve the SM-2 "no value yet" state as null — a blank string defeats that
intent, becoming quality 0 instead.

ADR / convention

docs/05-1-topic-and-assessment-schema.md:86 (ease_factor default 2.5) and
:154-158 (adopt shape: ease_factor 2.5, interval_days 1, last_quality null).

src/validation/rules/valid-review-fields.ts:18,58 defines the contract:
"last_quality: null, or an integer 0-5" and "never-reviewed = null, else
integer 0-5."

Acceptance criteria
  • parseInteger/parseOptionalInteger/parseNumber guard blank strings the
    same way parseScore does.
  • A topic note with interval_days: "" loads as 1 (default), not 0.
  • A topic note with last_quality: "" loads as null (never reviewed), not
    0 (reviewed, quality 0).
  • Test in test/storage-loader.test.ts: blank-string fields resolve to their
    documented defaults.

I did not use AI to generate this issue report.

Dominant language
TypeScript
Stars
1
Forks
0
Avg merge
17h 46m
Merged PRs (30d)
170

Getting set up

  • No Dockerfile or Docker Compose file
  • Has a pull request template
  • No contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Kuldeep2822k/cli

All issues in Kuldeep2822k/cli

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.