Removing an option from a trackable never deletes or rewrites the entries that used it. A removed choice is kept as a retired option (tombstoned), so its entries still show its label; a removed word of a strict list leaves its text in the entries. Retiring changes what a person can pick, which is offerableOptions (packages/core/src/option-repository.ts), and never what reads the data: a label, a chart’s domain, a legend or a statistic uses every option, and leaves one out by whether it occurs in the data rather than by whether it is retired. Those entries are stranded: correct readings taken under an earlier setup, which the current setup can no longer produce. Usually that is deliberate narrowing; sometimes it is a mistake. The trackable’s page says so and offers a way back, and never a way to delete them, since deleting is the loss retiring an option exists to prevent.
Which values are stranded
packages/core/src/stranded.ts asks one question, what lies outside the live set, of whichever place a kind keeps its options:
- a value chosen from options (
coded): entry codes that are not among the options a user may still pick (offerableOptions), labelled from the retired option, which keeps its label; - a word from a strict list (
enumwithstrict): entry words that are not among the list’s words, each its own label.
A list that is not strict takes any word beside its choices, so a word outside them is there by design and not stranded. Other kinds have no set to fall out of. strandedValues returns each stranded value with its label and how many entries carry it, most-used first; liveValues derives the set a remap can go to the same way.
The check reads the trackable’s own value (ownSchemaOf), so it covers a trackable whose value field has other fields beside it, which is written as a record, as well as one whose value is its only field.
The banner
StrandedBanner sits on the trackable’s page (/trackables/read), under the header: how many entries use options that were removed, that they still count and still show their labels, then one row per stranded value with three actions.
- Remap moves every entry carrying that value to a live option the user picks (
entryRepo.remap): the app’s one bulk rewrite of past values. It stays within one stored column (a code invalue_number, a word invalue_text) and refuses a call that would cross them, holds the new value to the same check as any write, and reports how many entries moved. It is started by the user, names both options and reports its count, so it keeps no separate audit trail; each entry’srecorded_atstays as it was. - Restore brings a retired choice back (
optionRepo.restore), offered for a choice from options only: a removed list word is gone from the schema, and typing it back into the list is how it returns. A retired option keeps itssort_order, so a restored one returns to its old place. - Dismiss hides the banner for the trackable. It is kept in the
trackables:strandedDismissedsetting as a signature of the stranded set (each value and its count), not a yes/no, so dismissing means “I have seen these”: when more data is stranded the signature changes and the banner comes back.
Related
Value Types · Presentation Layer · Entry · Trackable Authoring Flow