Skip to content

[refactor] education.ts hand-rolls entry segmentation instead of using parseEntryBlocks — the repo's #1 complexity function #914

Description

@s-annam

Problem

extractEducation is the highest-cognitive-complexity function in the repository, and the reason is that education.ts hand-rolls the entry segmentation that every other extractor gets from the shared parseEntryBlocks primitive.

fallow health (repo-wide, 14,244 functions analyzed) ranks it #1 and #5:

rank function cognitive
1 extractEducation (src/lib/heuristics/extract/education.ts:1123) 80
5 educationFromChunk (src/lib/heuristics/extract/education.ts:921) 42

src/lib/heuristics/entry-blocks.ts records the gap in its own prose:

education.ts does not use this primitive at all.
src/lib/heuristics/entry-blocks.ts:279-280

The three other entry-shaped sections are thin callers of the shared primitive:

  • src/lib/heuristics/extract/experience.ts:44 (and a second pass at :58)
  • src/lib/heuristics/extract/projects.ts:39
  • src/lib/heuristics/extract/achievements.ts:75

education.ts instead imports only two leaf predicates from it — isDateOnlyLine, isEntryHeaderShape (education.ts:22) — and re-implements the rest as a private "chunker": educationFromChunk (:921) joins a chunk with " | " and re-parses fields out of the joined string (:925), with the chunking gates spread across isRealEntryHeader (:258), isCourseworkContinuation (:290), isInlineDatedProgram (:362), isProgramLeadAt (:484) and isInstitutionLeadAt (:545).

The file carries 27 distinct issue references — tied for second-highest in the repo — which is what that hand-rolled path costs in accreted special cases.

Why this is the right shape

parseEntryBlocks is already a parameterized segmentation engine, not an experience-specific one. EntryBlockConfig (entry-blocks.ts:185-213) exposes exactly the axes education needs:

  • anchor — education entries anchor on an institution or degree line, not necessarily a date range
  • collectBody — education entries may be header-only
  • headerLookback — the Degree\nInstitution <dates> stack
  • dateParsing: "all" | "date_anchors_only" — education's dates ride the institution line

Projects and achievements were both onboarded onto it by supplying a config. Education is the one holdout.

Scope

Behaviour-preserving segmentation swap. education.ts keeps ownership of its education-specific field logic (degree/field parsing, GPA and honors via education-grade.ts, institution/location splitting, date precision) — the goal is to delete the private chunker, not the domain knowledge.

Expect this to need a new EntryAnchor variant or a widened anchor config; adding one is in the spirit of the primitive and benefits the other three callers.

Implementation plan

  1. Characterize the current chunker: what line sequences does it group, and on which fixtures? The education corpus fixtures plus the batch:parser-education lane are the reference set.
  2. Express that grouping as an EntryBlockConfig, extending EntryAnchor if the institution-lead / degree-lead cues have no existing anchor.
  3. Replace educationFromChunk's chunk-assembly with parseEntryBlocks output, keeping the per-field extraction that runs on each block.
  4. Delete the now-dead chunking gates, or demote any that survive as genuine field-level predicates.
  5. Re-run fallow health and record the new cognitive numbers for both functions.

Acceptance criteria

  • education.ts imports and calls parseEntryBlocks; the private chunk-assembly path is gone.
  • Corpus snapshots are byte-identical, or every diff is reviewed fixture-by-fixture and justified in the PR description.
  • extractEducation and educationFromChunk both drop below the fallow complexity threshold; the PR quotes before/after.
  • npm run verify green; no new KNOWN_FAILURES entries.
  • Any new EntryBlockConfig axis is documented in the interface docblock the way the existing four are.

Context

Surfaced by an architecture review of the "should we introduce a plugin architecture?" question. The answer was no — but this convergence was the one structural item the review found that epic #646 does not already cover, and fallow ranks it as the single highest-complexity function in the tree. Related: #646, #831 (batch:parser-education).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    improvementEnhancing existing functionalityrefactorCode restructuring without behavior changeux:parsingUX program: parsing accuracy as the user experiences it

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions