You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Umbrella for the goal raised in #94: fully support importing PT9 interlinearizations into an InterlinearProject — the end-to-end pipeline from PT9 data to a working, editable project.
Generalize the interlinearizer — an imported PT9 project behaves like any other: its source, model, and output can be changed or dropped afterward.
First-contact discoverable — import is offered where a user would look for it (e.g. from the project picker / "open" flow), not as a required up-front decision.
Current state
The XML parsers this issue started from are gone. #272 replaced them with a converter over the platform-parsed Pt9InterlinearProjectData payload, fetched through the read-only Pt9Interlinear projectInterface — which settles where PT9 data comes from: a PAPI-exposed project addressed by its Platform.Bible sourceProjectId, so there is no ScrTextName mapping and no file picker. src/parsers/pt9/ now holds only lexemeKey.ts and the schema notes in pt9-xml.md. The pipeline lives in src/converters/pt9/ and pt9ImportService.ts. Earlier parser groundwork, now superseded: #237, #238.
Checklist against the scope items below — link the PR that completes an item as you check it.
5. Keys & offsets — clusterAnchoring.ts (Convert the platform-parsed PT9 interlinear payload instead of raw XML #272), but not as this issue specified: a cluster whose range does not match the tokenized baseline is dropped and counted (clusterDrops.formMismatch, versesNotFound), never marked stale. staleKept in pt9ImportService is a different notion — the source's interlinear files vanished, so an earlier import is kept rather than replaced.
Open follow-ups on what shipped: #280 (lexicon refs), #283 (test projects, PR open), #285 (morphemes with blank forms), #296 (report prints the same language tag twice).
Scope — the pipeline
Import command + UI. An interlinearizer.importPt9 command and an entry point (file/folder picker for Interlinear_<lang>_<book>.xml, or selection of a PT9 project). Creates an InterlinearProject via projectStorage / interlinearizer.createProject.
Cluster → Token mapping (the crux). PT9 Cluster.Range is an Index/Length span in the verse text; our tokens carry charStart/charEnd within Segment.baselineText from bookTokenizer. Reconcile the two so each cluster resolves to the token(s) it covers. A single-lexeme cluster over one token → a TokenAnalysis; a cluster spanning multiple tokens → a PhraseAnalysis; a multi-lexeme cluster → a morpheme breakdown (TokenAnalysis.morphemes).
Gloss-text resolution. The XML stores only LexemeId + SenseId — not gloss text. PT9's
own lexicon files hold the text, and inlined gloss text is what every imported record renders
from.
The resolver shipped today (unresolvedPt9LexiconResolver) resolves nothing, so import persists
no refs at all yet. Whether an identity that resolves against no lexicon is additionally stored
as a foreign PT9-authority ref is open, and owned by Add lexicon ref resolution when copying a Paratext 9 import for editing #280.
Punctuation / Excluded / Hash. Punctuation tokens come from the tokenizer already; PT9 Punctuation change records are likely informational. Excluded clusters import as status: 'rejected'. Verse Hash present ⇒ 'approved', absent ⇒ 'suggested' — PT9 writes
the hash only for an approved verse, so absence is the not-approved state (pt9-xml.md:49).
Two further rules the shipped merger applies, neither derivable from a single cluster: gloss
languages that disagree on a record reduce it to 'suggested' (mergeStatus), and a
would-be-approved record whose tokens are already claimed by an approved one is demoted to 'candidate' (counted as approvedDemotedToCandidate).
confidence is not derived from approval. The interlinear XML carries no confidence source
— no IsGuess, no Score, only Hash — and the importer spends the field on anchoring
instead: confidence: 'low' marks a record whose token placement was a guess among equal
folded forms (analysisMerger.ts:339, :406).
Multi-file merge. One file per book per gloss language (Interlinear_<lang>_<book>.xml). Merge across books and languages into one project whose analysisLanguages union the GlossLanguages.
A lexicon — for refs, not gloss text (item 3 inlines the text). PT9-minted ids are their own
id space, so resolution means mapping a PT9 lexical identity onto a lexicon holding its own ids
for the same thing, and the first real Pt9LexiconResolver needs that lexicon's authority
constant. Define the lexicon port and capability descriptor #226 defined the port and the authority/provider split (completed 2026-08-31); this
model defines no authority values (interlinearizer.d.ts:403), so the constant comes from
whichever lexicon is chosen — Add lexicon ref resolution when copying a Paratext 9 import for editing #280.
bookTokenizer — the token grid clusters must map onto.
Exporting back to PT9 XML (separate, if ever needed).
Open questions
Gloss resolution: inlined gloss text always; a stored ref where a resolver resolves. Open,
both in Add lexicon ref resolution when copying a Paratext 9 import for editing #280: who implements the first real Pt9LexiconResolver and against which lexicon, and
whether an unresolved PT9 identity is stored as a foreign ref so it survives into an editable
copy.
Should this go to user-questions.md for review outside the dev team? (per AGENTS.md UX-decisions guidance)
Size: L (multi-stage pipeline; the cluster↔token mapping and gloss resolution are each substantial). Priority: P1 — this is the concrete goal ("fully support importing PT9 interlinearizations").
Umbrella for the goal raised in #94: fully support importing PT9 interlinearizations into an
InterlinearProject— the end-to-end pipeline from PT9 data to a working, editable project.Design principles this follows
Current state
The XML parsers this issue started from are gone. #272 replaced them with a converter over the platform-parsed
Pt9InterlinearProjectDatapayload, fetched through the read-onlyPt9InterlinearprojectInterface — which settles where PT9 data comes from: a PAPI-exposed project addressed by its Platform.BiblesourceProjectId, so there is noScrTextNamemapping and no file picker.src/parsers/pt9/now holds onlylexemeKey.tsand the schema notes inpt9-xml.md. The pipeline lives insrc/converters/pt9/andpt9ImportService.ts. Earlier parser groundwork, now superseded: #237, #238.Checklist against the scope items below — link the PR that completes an item as you check it.
interlinearizer.importPt9Projecttaking asourceProjectId, not theinterlinearizer.importPt9+ file picker the scope item guessed.clusterAnchoring.ts(Convert the platform-parsed PT9 interlinear payload instead of raw XML #272). Clusters that cannot be anchored drop and are counted (clusterDrops,ambiguousAnchors).pt9GlossSource.ts, Convert the platform-parsed PT9 interlinear payload instead of raw XML #272). The lexicon-ref half is deliberately unimplemented: thePt9LexiconResolverseam is wired at every ref site but no implementation is supplied, so imports carry zero refs — Add lexicon ref resolution when copying a Paratext 9 import for editing #280.Excluded/Hash—languageAnalysisBuilder.ts+analysisMerger.ts(Convert the platform-parsed PT9 interlinear payload instead of raw XML #272). Punctuation entries are ignored and counted (punctuationEntriesIgnored);Excluded→'rejected';Hash→'approved'/'suggested'.clusterAnchoring.ts(Convert the platform-parsed PT9 interlinear payload instead of raw XML #272), but not as this issue specified: a cluster whose range does not match the tokenized baseline is dropped and counted (clusterDrops.formMismatch,versesNotFound), never markedstale.staleKeptinpt9ImportServiceis a different notion — the source's interlinear files vanished, so an earlier import is kept rather than replaced.languageAnalysisBuilder.ts,glossLanguageTags.ts,convertPt9Project.ts(Convert the platform-parsed PT9 interlinear payload instead of raw XML #272). Per-language reports plusmerge.sameTagCollisionscover files that disagree on a tag.Open follow-ups on what shipped: #280 (lexicon refs), #283 (test projects, PR open), #285 (morphemes with blank forms), #296 (report prints the same language tag twice).
Scope — the pipeline
Import command + UI. An
interlinearizer.importPt9command and an entry point (file/folder picker forInterlinear_<lang>_<book>.xml, or selection of a PT9 project). Creates anInterlinearProjectviaprojectStorage/interlinearizer.createProject.Cluster → Token mapping (the crux). PT9
Cluster.Rangeis anIndex/Lengthspan in the verse text; our tokens carrycharStart/charEndwithinSegment.baselineTextfrombookTokenizer. Reconcile the two so each cluster resolves to the token(s) it covers. A single-lexeme cluster over one token → aTokenAnalysis; a cluster spanning multiple tokens → aPhraseAnalysis; a multi-lexeme cluster → a morpheme breakdown (TokenAnalysis.morphemes).Gloss-text resolution. The XML stores only
LexemeId+SenseId— not gloss text. PT9'sown lexicon files hold the text, and inlined gloss text is what every imported record renders
from.
A lexicon reference is stored when a resolver maps a PT9 lexical identity onto a lexicon that
can actually resolve it (
Pt9LexiconResolver, Convert the platform-parsed PT9 interlinear payload instead of raw XML #272). Under Namespace lexicon refs by provider before any ref is persisted #225 every ref carries a requiredauthoritynaming the id space that minted its ids, so:PT9's.
projectIdis likewise that lexicon's project — not the PT9 project, and not thePlatform.Bible
sourceProjectId.authority, and one unstamped ref fails validation for the whole draft rather than just that
record.
The resolver shipped today (
unresolvedPt9LexiconResolver) resolves nothing, so import persistsno refs at all yet. Whether an identity that resolves against no lexicon is additionally stored
as a foreign PT9-authority ref is open, and owned by Add lexicon ref resolution when copying a Paratext 9 import for editing #280.
Punctuation /
Excluded/Hash. Punctuation tokens come from the tokenizer already; PT9Punctuationchange records are likely informational.Excludedclusters import asstatus: 'rejected'. VerseHashpresent ⇒'approved', absent ⇒'suggested'— PT9 writesthe hash only for an approved verse, so absence is the not-approved state (
pt9-xml.md:49).Two further rules the shipped merger applies, neither derivable from a single cluster: gloss
languages that disagree on a record reduce it to
'suggested'(mergeStatus), and awould-be-approved record whose tokens are already claimed by an approved one is demoted to
'candidate'(counted asapprovedDemotedToCandidate).confidenceis not derived from approval. The interlinear XML carries no confidence source— no
IsGuess, noScore, onlyHash— and the importer spends the field on anchoringinstead:
confidence: 'low'marks a record whose token placement was a guess among equalfolded forms (
analysisMerger.ts:339,:406).Keys & offsets. Verse key (
"MAT 1:1") →Segment.id; cluster ranges → token offsets (step 2). Non-matching ranges (baseline drift) →stalehandling.Multi-file merge. One file per book per gloss language (
Interlinear_<lang>_<book>.xml). Merge across books and languages into one project whoseanalysisLanguagesunion theGlossLanguages.Link model & output. Split out into Attach the discovered model and output projects to an imported PT9 project #215.
Dependencies
id space, so resolution means mapping a PT9 lexical identity onto a lexicon holding its own ids
for the same thing, and the first real
Pt9LexiconResolverneeds that lexicon's authorityconstant. Define the lexicon port and capability descriptor #226 defined the port and the authority/provider split (completed 2026-08-31); this
model defines no authority values (
interlinearizer.d.ts:403), so the constant comes fromwhichever lexicon is chosen — Add lexicon ref resolution when copying a Paratext 9 import for editing #280.
bookTokenizer— the token grid clusters must map onto.Out of scope
Open questions
both in Add lexicon ref resolution when copying a Paratext 9 import for editing #280: who implements the first real
Pt9LexiconResolverand against which lexicon, andwhether an unresolved PT9 identity is stored as a foreign ref so it survives into an editable
copy.
user-questions.mdfor review outside the dev team? (per AGENTS.md UX-decisions guidance)Size: L (multi-stage pipeline; the cluster↔token mapping and gloss resolution are each substantial).
Priority: P1 — this is the concrete goal ("fully support importing PT9 interlinearizations").