sillsdev / sillsdev/interlinearizer-extension

Dedupe identical token analysis payloads on PT9 import

Open
#313 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
TypeScript
Stars
2
Forks
0
Avg merge
1d 17h
Merged PRs (30d)
55

Description

PT9 import writes its converted TextAnalysis straight to storage (convertPt9Project.ts:163-170savePt9Import), never reaching the find-or-create in appendApprovedAnalysis (src/store/analysisSlice.ts:253-266). Two tokens the import glosses identically therefore get two payloads, where a hand-glossed pair gets one shared payload at frequency 2.

Reproduction. PIA (test-data/pt9-projects/PIA, #283) approves four plovs tokens across PHP 1:6-1:7. The pool key plovs holds four entries, each frequency 1:

payload gloss breakdown
pt9:ta:PHP 1:6:5:0 catorce plov s
pt9:ta:PHP 1:6:15:0 catorce plov s
pt9:ta:PHP 1:7:0:0 catorce
pt9:ta:PHP 1:7:15:0 decimocuarto plov s

The first two are analysesAreIdentical and render as indistinguishable rows in the suggestion dropdown.

Consequences.

  • The frequency ranking in comparePoolEntries (suggestion-engine.ts:77-80) never sees a count above 1 on an imported project, so the id tiebreak decides every homograph.
  • #186's panel is already on main (AnalysisCatalogPanel, selectCatalogRows), so this is live rather than anticipated: every PT9-imported project renders each twin as its own row at one usage, against #186's own "one row per distinct token analysis".
  • Storage bloat, proportional to how repetitive the source is.

What to do

One content-dedupe pass in convertPt9Project.ts, just before TextAnalysis is assembled — both merged.tokenAnalyses and barePayloads exist by then, so one pass covers both. Inside mergeLanguageAnalyses would miss the bare payloads.

Take (tokenAnalyses, tokenAnalysisLinks), return them reduced:

  • Bucket by normalizeSurfaceForm(surfaceText), then find within the bucket with analysesAreIdentical (both in utils/analysis-identity.ts). A flat linear find is O(n²) — fine for the store's one-write-at-a-time growth, not for a whole Bible. Bucketing is sound because analysesAreIdentical already requires equal folded surface; same structure as buildPoolIndex (suggestion-engine.ts:95-113).
  • Keep the first payload, drop the rest, repoint every link's analysisId — what mergeIntoIdenticalPayload (analysisSlice.ts:353) does after an edit.
  • Leave the links otherwise untouched: token.surfaceText stays each token's own, which is what keeps per-token drift detection accurate.

Run this pass after #324, which strips blank morpheme forms at import — stripping a morpheme turns previously-distinct payloads into twins, so the content fold must see the final shape.

Add identicalPayloadsMerged to Pt9MergeReport (report.ts:54) and seed it at report.ts:159. No UI change — Pt9ImportModal reads only report.languages.

Why the fold stays

Considered and rejected: an import-only exact-surfaceText rule instead of analysesAreIdentical's case fold.

Against it — appendApprovedAnalysis folds, so a later hand-gloss would adopt whichever duplicate sits earlier in tokenAnalyses, splitting the links on array order. buildPoolIndex folds too, so the sentence-initial twin would stay an indistinguishable extra row. And sentence-initial is the common case in scripture, so the rule would decline to merge precisely where it matters most.

Its one real cost, accepted: the surviving payload's surfaceText is whichever occurrence came first. That is presentation, not identity, and hand-glossed data already has it — appendApprovedAnalysis's own doc describes "a sentence-initial form links to a payload first created from a mid-sentence form". The fix belongs in #186: derive a row's displayed surface from its links — the most common token.surfaceText among them — recomputed on read rather than stored, since any stored choice goes stale on the next write.

Checked, no work needed. The generated ids (pt9:ta:, pt9:wa:, pt9:pa:) and producer are write-only, so an adopted payload keeping the first token's id is safe. clusterParseIdentities is a Set keyed by folded surface plus signature and already collapses duplicates. approvedTokenSeen runs before payload creation, so the one-approved-per-token invariant is unaffected.

Decide.

  • Keep buildBareWordAnalyses' own seenByWord check (bareWordAnalyses.ts:52-64)? It keys on the raw word and the lexeme-id signature, so a content pass is strictly wider. Keeping both leaves skippedExistingIdentical its current meaning.

Tests. Twins collapse with both links surviving; a bare payload identical to a linked one; case variants collapse; and the three near-misses that must not collapse — different gloss, breakdown vs none, different breakdown. PIA is the manual counterpart.

Related: #187 (same gap for phrases, on the store write path), #186 and #254 (both compute against pool-bucket contents and assume payloads are deduped).

Size: S

Contributor guide

No contributing guide indexed for this repository

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.

Research direction

Start in convertPt9Project.ts after #324's import changes, then read analysesAreIdentical and normalizeSurfaceForm in utils/analysis-identity.ts, plus Pt9MergeReport in report.ts. Verify the PT9 import tests cover linked and bare twins, case variants, and the three near-misses; done means duplicates collapse with links preserved and identicalPayloadsMerged is reported without UI changes.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend, data, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.