Skip to content

Investigate whether PT9 import can produce morphemes with blank forms #285

Description

@alex-rawlings-yyc

What prompted this

Reviewing the suggestion breakdown work (#213), a Devin finding pointed at breakdownOf in src/utils/suggestion-engine.ts. Verifying it turned up a reachable path where a TokenAnalysis can carry morphemes whose form is the empty string. The breakdown render is only where it happens to be visible; the record itself is the problem, and it originates in the PT9 importer.

What is established

parseLexemeKeyId accepts an id with an empty form. LEXEME_KEY_ID_RE in src/parsers/pt9/lexemeKey.ts uses a lazy, zero-width-capable form group, so parseLexemeKeyId('Stem:') returns { Type: 'Stem', Form: '' } rather than undefined (verified by a throwaway probe against HEAD).

Nothing on the bare-word path rejects that. src/converters/pt9/bareWordAnalyses.ts:122 copies key.Form onto the morpheme verbatim; its only rejection is a keys.length === 0 check, which counts keys rather than inspecting their content. So a word parse with ids ["Stem:", "Suffix:"] yields a TokenAnalysis with two blank-form morphemes.

The cluster path in src/converters/pt9/analysisMerger.ts is incidentally protected: clusterAnchoring.ts matches the concatenation of a parse's forms against the token's surface text, and an all-blank parse concatenates to '', which matches nothing. That protection is a side effect of anchoring, not a guard — a mixed parse like ["", "ba"] still anchors to token "ba" and persists the blank morpheme alongside the real one.

The in-app write paths cannot produce this. MorphemeEditor.tsx normalizes and blocks empty commits, and TokenChip.tsx independently re-filters with .filter(Boolean), so import is the only source.

What is not established

Whether PT9 ever actually emits an id of this shape. The reachability above is a code-path argument, not an observed input — no real project data was examined. That is the question this issue exists to answer.

Investigation

  • Determine whether PT9 emits bare Type: ids in practice, and if so what they mean. src/parsers/pt9/pt9-xml.md documents the schema; real interlinear data from a PT9 project is the better evidence.
  • Decide whether the parse boundary should reject an empty form regardless of the answer. The machinery already exists but is never reached: returning undefined routes the cluster path to the existing unparseableLexemeId drop counter, and makes bareWordAnalyses' keys.length !== ids.length check catch it too.
  • If blank forms turn out to be meaningful rather than malformed, the fix belongs downstream instead, and the consumers below need to handle them deliberately.

Downstream consumers if such a record exists

  • breakdownOf (src/utils/suggestion-engine.ts) joins forms on a space, so all-blank morphemes yield " " and a mixed parse yields " ba". Both are !== undefined, so the suggestion row renders a blank annotation and speaks an incomplete aria-label.
  • MorphemeBox renders blank forms as empty grid cells that are still clickable edit triggers.
  • morphemeIdentity (src/utils/analysis-identity.ts) folds morphemes into the payload identity, so all-blank breakdowns for different words collapse into one dedup bucket.

Patching any one of these in isolation would hide the symptom at one surface while leaving the malformed record in the store, which is why this is filed as an import-level investigation rather than a render fix.

Activity

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions