From 51b6946fa2534e565468d558540fc2dc11a1b5e8 Mon Sep 17 00:00:00 2001 From: Shubhransh Gupta <54713516+shubhransh-gupta@users.noreply.github.com> Date: Sat, 15 Aug 2026 10:02:25 +0530 Subject: [PATCH 1/2] fix(education): strip middot/bullet before in/of connective (#839) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Widen the parseDegreeAndField separator class to include · and • so "B.S. · in Computer Science" no longer leaks the connective into the field. Adds regression tests alongside the #835 block. Fixes #839 Co-authored-by: Cursor --- src/lib/heuristics/extract/education.test.ts | 56 ++++++++++++++++++++ src/lib/heuristics/extract/education.ts | 4 +- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/lib/heuristics/extract/education.test.ts b/src/lib/heuristics/extract/education.test.ts index e437234b..751cb3e0 100644 --- a/src/lib/heuristics/extract/education.test.ts +++ b/src/lib/heuristics/extract/education.test.ts @@ -947,3 +947,59 @@ describe("extractEducation — cleanField strips leftover edge middot and bullet expect(value[0].field).toBeUndefined(); }); }); + +describe("extractEducation — parseDegreeAndField strips middot/bullet before in/of connective (#839)", () => { + it("strips middot then in connective from degree field", () => { + const { value } = extractEducation( + mkEduSection([ + "Springfield State University", + "B.S. · in Computer Science", + ]), + ); + expect(value).toHaveLength(1); + expect(value[0].degree).toBe("B.S."); + expect(value[0].field).toBe("Computer Science"); + }); + + it("strips bullet then of connective from degree field", () => { + const { value } = extractEducation( + mkEduSection([ + "Springfield State University", + "B.S. • of Computer Science", + ]), + ); + expect(value).toHaveLength(1); + expect(value[0].degree).toBe("B.S."); + expect(value[0].field).toBe("Computer Science"); + }); + + it("still strips hyphen and em dash before in connective", () => { + const { value: hyphen } = extractEducation( + mkEduSection([ + "Springfield State University", + "B.S. - in Computer Science", + ]), + ); + expect(hyphen[0].field).toBe("Computer Science"); + + const { value: emDash } = extractEducation( + mkEduSection([ + "Springfield State University", + "B.S. — in Computer Science", + ]), + ); + expect(emDash[0].field).toBe("Computer Science"); + }); + + it("preserves interior middot in a two-part field name", () => { + const { value } = extractEducation( + mkEduSection([ + "Springfield State University", + "B.S. Mathematics · Statistics", + ]), + ); + expect(value).toHaveLength(1); + expect(value[0].degree).toBe("B.S."); + expect(value[0].field).toBe("Mathematics · Statistics"); + }); +}); diff --git a/src/lib/heuristics/extract/education.ts b/src/lib/heuristics/extract/education.ts index 7ce31b79..d6f3a81a 100644 --- a/src/lib/heuristics/extract/education.ts +++ b/src/lib/heuristics/extract/education.ts @@ -533,9 +533,9 @@ function parseDegreeAndField(line: string): { } const fieldRaw = line .slice(fieldStart) - // Drop a leading "in "/"of " connective or a "-"/"—"/":"/"," separator. + // Drop a leading "in "/"of " connective or a "-"/"—"/":"/","/"·"/"•" separator. .replace(/^\s*(?:in|of)\s+/i, "") - .replace(/^\s*[-–—,:]\s*/, "") + .replace(/^\s*[-–—,:·•]\s*/, "") .replace(/^\s*(?:in|of)\s+/i, ""); return { degree, field: cleanField(fieldRaw) }; } From dfca1b3fe8b9c14a6a0cf496adfa95d7cf713ac3 Mon Sep 17 00:00:00 2001 From: Srinivas Annam Date: Fri, 14 Aug 2026 23:05:39 -0700 Subject: [PATCH 2/2] docs(education): name every glyph the separator strip drops (#839) The widened class also carries an en dash, which the comment omitted, and it is still narrower than `cleanField`'s (no ";"). Say both, and point the residual asymmetry at #653. Comment-only; no behaviour change. --- src/lib/heuristics/extract/education.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/heuristics/extract/education.ts b/src/lib/heuristics/extract/education.ts index d6f3a81a..035d7ea9 100644 --- a/src/lib/heuristics/extract/education.ts +++ b/src/lib/heuristics/extract/education.ts @@ -533,7 +533,12 @@ function parseDegreeAndField(line: string): { } const fieldRaw = line .slice(fieldStart) - // Drop a leading "in "/"of " connective or a "-"/"—"/":"/","/"·"/"•" separator. + // Drop a leading "in "/"of " connective or a "-"/"–"/"—"/","/":"/"·"/"•" + // separator. The middot/bullet live here, not only in `cleanField`, because + // this strip runs FIRST — `cleanField`'s edge strip accepted them already but + // runs after the connective strips, too late to rescue a "· in " + // header (#839). The two classes still differ (`cleanField` also takes ";"); + // unifying the separator vocabulary repo-wide is #653. .replace(/^\s*(?:in|of)\s+/i, "") .replace(/^\s*[-–—,:·•]\s*/, "") .replace(/^\s*(?:in|of)\s+/i, "");