Give lexicon refs a provider discriminator, before any code path persists one.
Problem
EntryRef / SenseRef carry an id plus an optional projectId, documented as a "Lexicon project identifier (FwData / Harmony code)" — already shaped around FW Lite. The interlinearizer has to support a lexicon with no FW Lite connection (see the epic), so more than one provider can mint refs, and nothing in the ref says which one did.
Why this is first
Refs are part of dedupe identity. analysesAreIdentical in src/utils/analysis-identity.ts compares glossSenseRef by structural equality, and morphemeIdentity folds entryRef / senseRef / allomorphRef / grammarRef into the per-morpheme identity projection. Two providers minting refs that look alike means mergeIntoIdenticalPayload merges analyses that mean different things; the mirror failure is never merging two analyses that do mean the same thing.
Nothing writes a ref today — #186 notes the lexicon ref fields "are never written by any current code path." So this is free to fix now and expensive to retrofit once drafts in the wild carry refs.
Ask
- Add a provider discriminator to the ref types so a ref is self-describing.
- Decide whether
projectId stays FW-Lite-specific or becomes provider-scoped, and re-document it either way — the current wording bakes in FwData/Harmony.
- Define what happens when a project is opened under a different provider than the one that wrote its refs: the ref is foreign, it does not resolve, and rendering falls back to the stored free-form gloss. A foreign ref must never resolve against the wrong lexicon, and must never be silently dropped.
- Cover the ref fields in dedupe identity so provenance participates in comparison.
Non-goals
No resolution, no provider, no UI. Type shape and identity semantics only.
Relationship to #129
#129 scope item (b) covers whether the Scripture Burrito alignment flavor's reference schemes fit our EntryRef / SenseRef — the same two types this issue reshapes. Findings. What they settle here:
- The alignment format does not express authority. Its
scheme is a selector-parse rule: shared across documents, hoisted to group level, and orthogonal to which system minted a selector — two providers both minting GUIDs share a scheme. There is no ref shape to adopt from it.
- The mechanism exists one layer up, in Burrito core metadata:
idAuthorities (a declared table of identity authorities) plus prefixedId — "Opaque system-specific identifier, prefixed with the name of the system as declared in idAuthorities" — already used for relationship.id, the closest analogue to a ref into another project. Adopt that concept, and the alignment format's separation of authority ⟂ dataset ⟂ selector, but neither one's encoding: the discriminator is a required inline field, not a prefix on projectId. projectId has to stay optional — a tier-1 in-extension lexicon has no project id — and a provider riding on it would be dropped whenever it is omitted, which is the failure this issue exists to prevent.
projectId is provider-scoped and optional. Its form is defined by the provider, not by this model. The alignment format's docid is explicitly omissible, which is the precedent.
- Dedupe identity needs no code change.
morphemeIdentity already projects all four refs and analysesAreIdentical already compares glossSenseRef, both through structural deepEqual, so a discriminator inside the ref object participates automatically. What is owed is a regression test that two refs identical but for provider do not merge, plus fixture updates once the field is required. deepEqual compares key counts, which is the other reason the field must be required rather than optional.
- Foreign-ref semantics are ours to specify. Neither spec says what a consumer does with an unrecognized authority; proposed wording is in the findings.
#129 (a) and (c) are tied to an alignment-editing UI and do not gate this issue.
Blocked on
Nothing. Must land before anything writes a ref.
Give lexicon refs a provider discriminator, before any code path persists one.
Problem
EntryRef/SenseRefcarry an id plus an optionalprojectId, documented as a "Lexicon project identifier (FwData / Harmony code)" — already shaped around FW Lite. The interlinearizer has to support a lexicon with no FW Lite connection (see the epic), so more than one provider can mint refs, and nothing in the ref says which one did.Why this is first
Refs are part of dedupe identity.
analysesAreIdenticalinsrc/utils/analysis-identity.tscomparesglossSenseRefby structural equality, andmorphemeIdentityfoldsentryRef/senseRef/allomorphRef/grammarRefinto the per-morpheme identity projection. Two providers minting refs that look alike meansmergeIntoIdenticalPayloadmerges analyses that mean different things; the mirror failure is never merging two analyses that do mean the same thing.Nothing writes a ref today — #186 notes the lexicon ref fields "are never written by any current code path." So this is free to fix now and expensive to retrofit once drafts in the wild carry refs.
Ask
projectIdstays FW-Lite-specific or becomes provider-scoped, and re-document it either way — the current wording bakes in FwData/Harmony.Non-goals
No resolution, no provider, no UI. Type shape and identity semantics only.
Relationship to #129
#129 scope item (b) covers whether the Scripture Burrito alignment flavor's reference schemes fit our
EntryRef/SenseRef— the same two types this issue reshapes. Findings. What they settle here:schemeis a selector-parse rule: shared across documents, hoisted to group level, and orthogonal to which system minted a selector — two providers both minting GUIDs share a scheme. There is no ref shape to adopt from it.idAuthorities(a declared table of identity authorities) plusprefixedId— "Opaque system-specific identifier, prefixed with the name of the system as declared in idAuthorities" — already used forrelationship.id, the closest analogue to a ref into another project. Adopt that concept, and the alignment format's separation of authority ⟂ dataset ⟂ selector, but neither one's encoding: the discriminator is a required inline field, not a prefix onprojectId.projectIdhas to stay optional — a tier-1 in-extension lexicon has no project id — and a provider riding on it would be dropped whenever it is omitted, which is the failure this issue exists to prevent.projectIdis provider-scoped and optional. Its form is defined by the provider, not by this model. The alignment format'sdocidis explicitly omissible, which is the precedent.morphemeIdentityalready projects all four refs andanalysesAreIdenticalalready comparesglossSenseRef, both through structuraldeepEqual, so a discriminator inside the ref object participates automatically. What is owed is a regression test that two refs identical but for provider do not merge, plus fixture updates once the field is required.deepEqualcompares key counts, which is the other reason the field must be required rather than optional.#129 (a) and (c) are tied to an alignment-editing UI and do not gate this issue.
Blocked on
Nothing. Must land before anything writes a ref.