What
parseEntryBlocks can emit an entry whose headerLines is empty (title === ""), and the achievement / project extractors map it straight through to a value[] entry with title: "". No filter drops it.
Surfaced during review of #144 (flat-bullet-list awards fallback), but the behavior is pre-existing and shared across all three extractors — not specific to that PR.
How it happens
A header/anchor line whose entire text is a date token leaves nothing after stripDateRange:
- Flat-list path: a bullet like
• 2023 → stripBullet → "2023" → parseDateRange matches the YEAR_RE fallback → stripDateRange returns "" → headerLines: [].
- Same shape reachable via
buildEntryBlock for a date-only anchor line.
Downstream, achievementFromBlock (src/lib/heuristics/extract/achievements.ts:58-81) and the sibling projectFromBlock build { title: "", year, ... } with score 0 and push it into value[]. The zero score dilutes section confidence and a title-less item lands in output.
Expected
A block with empty headerLines (no title) is not a real entry — drop it before it reaches the score, rather than emitting { title: "" }.
Proposed fix
Filter empty-title blocks in the two thin extractors so the fix covers every anchor path at once:
extractAchievements (src/lib/heuristics/extract/achievements.ts)
extractProjects (src/lib/heuristics/extract/projects.ts)
e.g. drop blocks where liftHeaderLabel(block.headerLines).label is empty before mapping/scoring. extractExperience should be checked for the same shape.
Acceptance
- A date-only / title-less block produces no entry (not a
{title:""} entry).
- A new unit test in
entry-blocks/extractor tests covering a date-only bullet.
- Corpus snapshots re-baked; confirm no real fixture regresses (this should only remove phantom empty entries, if any).
Notes
Low priority — the trigger (an award/project/role whose only text is a bare year) is an unrealistic résumé shape. Hardening + honest-output cleanup, not a live bug on real input.
What
parseEntryBlockscan emit an entry whoseheaderLinesis empty (title === ""), and the achievement / project extractors map it straight through to avalue[]entry withtitle: "". No filter drops it.Surfaced during review of #144 (flat-bullet-list awards fallback), but the behavior is pre-existing and shared across all three extractors — not specific to that PR.
How it happens
A header/anchor line whose entire text is a date token leaves nothing after
stripDateRange:• 2023→stripBullet→"2023"→parseDateRangematches theYEAR_REfallback →stripDateRangereturns""→headerLines: [].buildEntryBlockfor a date-only anchor line.Downstream,
achievementFromBlock(src/lib/heuristics/extract/achievements.ts:58-81) and the siblingprojectFromBlockbuild{ title: "", year, ... }withscore 0and push it intovalue[]. The zero score dilutes section confidence and a title-less item lands in output.Expected
A block with empty
headerLines(no title) is not a real entry — drop it before it reaches the score, rather than emitting{ title: "" }.Proposed fix
Filter empty-title blocks in the two thin extractors so the fix covers every anchor path at once:
extractAchievements(src/lib/heuristics/extract/achievements.ts)extractProjects(src/lib/heuristics/extract/projects.ts)e.g. drop blocks where
liftHeaderLabel(block.headerLines).labelis empty before mapping/scoring.extractExperienceshould be checked for the same shape.Acceptance
{title:""}entry).entry-blocks/extractor tests covering a date-only bullet.Notes
Low priority — the trigger (an award/project/role whose only text is a bare year) is an unrealistic résumé shape. Hardening + honest-output cleanup, not a live bug on real input.