Skip to content

Design: the 3.0 Lexicon surface, configuring by reading (#754, 4i proposed) - #988

Open
derek73 wants to merge 10 commits into
masterfrom
claude/lexicon-surface-design-6e662a
Open

derek73 wants to merge 10 commits into
masterfrom
claude/lexicon-surface-design-6e662a

Conversation

@derek73

@derek73 derek73 commented Nov 2, 2026 •

Copy link
Copy Markdown
Owner

Docs-only. This PR proposes the API that the 2026-10-28 decision ("a caller configures vocabulary by reading, and the compiled sets become internal") left open. Proposed, not decided, except the title warning in question 1, decided 2026-11-01; nothing is built.

  • docs/design/vocabulary-model-rfc.md gains a Phase 4 section, "The Lexicon surface: configuring by reading", with seven items and a 4i work-order row. Phase 3's provenance and API sections now point to it.
  • docs/design/decisions/vocabulary-model.md gains a 2026-11-01 Phase 4: order and shapes, the rules a pack can choose, and the output model #754 entry marked proposed. It records the measurements with their recipes and what each review round changed. Its Open line now points at that entry in place of the open Parser.languages and method-name questions.

All parses were run on master a3b1f3e1 through throwaway prototypes, which are not committed. Eleven 2.x recipes parse every field the same under the proposed spelling.

Questions to decide

  1. Configuring. Should add and remove take words plus a vocabulary kind?
    • Signatures: Lexicon.add(*words, kind, **properties) and Lexicon.remove(*words, kind=None).
    • remove also accepts a category, name and relation included.
    • Properties use the TOML names.
    • Derived markers follow at construction through compile_readings.
    • Decided 2026-11-01: an add of a title reading warns when, under no choice, a given reading makes it a title only inside a phrase. The warning names remove(w, kind="given"), the fix for the trap in the add(titles={"dean"}) recipe. It fires on an add that creates a title reading or re-adds a held one unchanged (add("dean", kind="office")), and not on a property-only update (add("prince", kind="nobility", attaches=[...])). It has its own decisions entry and Standing line.
    • Should the generation, credentials and patronymic views read the kind from the lexicon instead of the shipped index? That would amend rules/R8 and rules/R9.
  2. Queries. Should Lexicon.readings_of(word) return a new public LexiconReading, and should the repr count readings by kind?
  3. Lexicon state. Should a Lexicon be five parts: a base, an ordered log of edits, a choice, the lists and the masks?
    • Equality is structural, the edit log compared as written, so it survives a later language choice. Bases compare by name, or by rows for a snapshot.
    • Every compile validates, including the one in parser_for, whose errors name the choice. For an edit that clashes with a reading a later choice turns on (add("st", kind="credential") under cs), should it (a) raise (recommended) or (b) let the caller's reading win?
    • The base, log and choice are underscore constructor parameters, so dataclasses.replace carries them.
    • | concatenates the logs, later wins. parser_for joins the packs' fragments in pack order, so a later pack wins as in the policy fold, and then the caller's lexicon, so the caller's edits win. | refuses two different choices or two different non-empty bases.
    • Edits are stored frozen (properties as sorted tuples), so the Lexicon stays hashable.
    • The underscore choice parameters are validated as parser_for validates a choice. They are a private route beside 3j's decided public spelling ("No public locales.choose. A bare choice is a Locale with an empty lexicon and the tags"), not an exception to it.
    • The public constructor takes only surnames, given_names and capitalization_exceptions.
    • The nameparser.config constants stay.
  4. The choice. Should the Lexicon record its languages and strictness, read-only and set publicly only by parser_for, with the underscore parameters carrying it through replace and pickle?
    • Chained calls union their languages.
    • Caller edits apply after the choice, in the order written.
    • This supersedes the 2026-10-29 chained-choices entry, and under strict, 3j's clause that a caller's listing of a dropped word is dropped.
    • A separate question: should the default's base, every index row including the off ones, be loaded lazily? That keeps the 2026-10-29 lazy-load decision. The alternative is an eager base, which costs about 2 ms with bytecode cached and 18–20 ms without.
  5. Pickle. Should a Lexicon pickle its five parts, with the base stored as a snapshot?
    • Should a 2.x state translate onto the nearer named base plus a per-word log (option b) rather than be refused (option a)?
    • Under (b), 2.8.0's pickled default loads as the default with the 23 suffix words 3.0 added replaced; it then parses as it did in 2.8.
    • The 2.8 fixtures, the pack fragments included, stay empty-based and keep their equality.
    • The ADDED_IN guard question is already settled (Teach Lexicon's pickle guard the ADDED_IN marker (3.0, #959 prerequisite) #960).
  6. Migration. Should old spellings fail loudly with a per-word translation, using the same code as the pickle load?
    • Calls in tree, measured 2026-11-01:
      • tests: 204 configuring calls and 157 reads
      • user docs: 29 calls
      • tools: 3 files
      • library: 111 internal reads
  7. Work order. Should 4i be built in four PRs, none moving a default parse?
    • 4i-a: the internal sets object
    • 4i-b: the break
    • 4i-c: the recorded choice
    • 4i-d (optional): Reading.languages follows the choice

Review

  • First reviewer pass, by the design-docs reviewer, found five problems, all fixed:

    • the default's base was missing the 71 off-by-default readings;
    • the 2.x pickle translation refused the dean recipe;
    • the caller counts included surnames;
    • the field count was wrong;
    • one pointer was dangling.
  • An independent review of c5263dc8 found nine problems, all addressed in eca9d861.

  • Two further reviewer passes, on eca9d861 and 9b500a18, found:

    • pack fragments being stripped through |;
    • a warning that fired on edits a later choice makes meaningful;
    • base equality left unstated;
    • a stale work-order row;
    • the fixture equality.

    These were fixed in 9b500a18 and 911d97a0.

  • A second independent review of c5263dc8..911d97a0 found seven more, fixed in 855c3b02. Its review found two more, fixed in 5541986c.

  • A third independent review of 911d97a0..f5b4e014 found six more, fixed in the head commit.

The doc tests pass (2,186).

🤖 Generated with Claude Code

derek73 and others added 2 commits November 1, 2026 23:08
…posed)

Proposes the API the 2026-10-28 Lexicon-surface decision left open:
add/remove by words and a reading kind, readings_of for queries, a
constructor narrowed to the lists and masks, the choice recorded on the
Lexicon so chained choices union, and a 2.x pickle loaded where its sets
translate back to readings. Proposed, not decided; nothing is built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The default's base holds every index row, its off readings included, so
a choice can still turn them on; a 2.x lexicon with a word in both titles
sets translates as a title; the caller counts leave out surnames, which
stays; the field count and the ADDED_IN pointer corrected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@derek73 derek73 added docs Documentation fixes and updates breaking-change Backwards-incompatible API change design labels Nov 2, 2026
@derek73 derek73 self-assigned this Nov 2, 2026
derek73 and others added 8 commits November 1, 2026 23:28
A 2.x pickle translates as the default plus a log; a lexicon keeps its
base and an ordered edit log apart; replace carries the underscore state;
the default's base loads lazily; the views read the lexicon's kinds; an
add that changes nothing warns; equality and | state the choice; the
strict supersession is marked; name and relation are categories only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 2.x state translates onto the nearer named base, so a 2.8 pack
fragment stays empty-based; the warning is narrowed to a title made
phrase-only by a given reading under no choice; bases compare by name,
a snapshot by its rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 4i-b row names the narrowed warning; the 2.x translation removes a
word first only where the base holds it, so the 2.8 fixture's lexicon
equals its fresh 3.0 spelling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Logs compare as written; parser_for recompiles and refuses with the choice
named; the underscore choice parameters are validated, a private exception
to 3j; a 2.x chosen parser loads with no choice; the title warning fires
only on a new title kind; bound_given is a fifth named kind; parser_for
joins a pack fragment under the caller's lexicon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dings (#754)

The title warning is decided, with its own entry and Standing line; it
warns save a property update of a held title reading, so a re-add of
dean's own kind warns again. No 2.x pickle carries a choice, so that
paragraph goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…entence (#754)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quote 3j verbatim and treat the underscore parameters as a private route,
not an exception; item 4 says the choice is set publicly only by
parser_for; fragments join in pack order, a later pack winning, before the
caller's lexicon; edits stored frozen; the parser_for clash offers raise
(recommended) or caller-wins; the decided entry's attribution follows the
file's head convention.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ry (#754)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

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

Labels

breaking-change Backwards-incompatible API change design docs Documentation fixes and updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant