Phone inventory, segmenter and weksa lower --phones (the Weksa->AquaSynth phones line) #16

Merged
weksa merged 4 commits from weksa-lowering/phones-line into main 2026-11-09 10:15:51 +00:00
Member

The phones line for AquaSynth: ruled IPA to an ordered {ipa, stress} list, one JSON object per cited entity.

weksa lower <store> <key> --phones
{"language":"en-US","source":"ˈnibu","phones":[{"ipa":"n","stress":"none"},{"ipa":"i","stress":"primary"},{"ipa":"b","stress":"none"},{"ipa":"u","stress":"none"}]}
  • Fixture (first commit, before the code): data/phonology/fixtures/nibu-en-US.phones.json; a test compares the library and the CLI output to it byte for byte.
  • Phone inventory: data/phonology/en-US.phones.tsv (class and tense/lax per phone, so /i/ and /ɪ/ are two phones). Read by crates/weksa/src/phones.rs through include_str!.
  • Segmenter: longest inventory match; ˈ/ˌ put primary/secondary stress on the next vowel (the nucleus), every other phone is none.
  • Refusals, never guesses: LowerError::UnknownPhone {entity, at, text} for a symbol the inventory lacks (or a language with no inventory), LowerError::DanglingStress {entity, at} for a mark with no vowel to land on. --phones conflicts with --ipa.
  • No durations, pitch or acoustics anywhere.

Tests: crates/weksa/tests/lower.rs (tense /i/ passes; rejected lax /ɪ/ is a different phone and is not what the seed emits; stress placement; multi-character phones; unknown phone and dangling stress refusals; parser refusals) and tests/cli.rs. Full suite green.

Mutation check: cargo-mutants is not installed in this run and the stopgap runner is not executable from a Self run (Permission denied), so I ran 21 hand-made mutants (stress marks swapped, shortest match, stress on any phone, wrong dangling offset, missing trailing check, lax parsed as tense, data edits for i/ɪ/u/tʃ, language tag, flag inversions, ...) against lower and cli tests: all 21 caught, none missed. Probes are not committed. A real cargo-mutants pass is still owed.

Decision to rule on: the inventory is a tab-separated file embedded in the binary, not a CultCache store, to avoid a new store and CLI for one table. It should move to a typed store if phone inventories grow past en-US.

Tense/lax and its source. /i/ is tense and /ɪ/ is lax in data/phonology/en-US.phones.tsv (the tenseness column; the file's # comment header now cites the ruling). The ruling it rests on is Nibu's of 2026-11-07, source weksa-body:follow_up:chat-s3981-3983 (#minds): tense vowel, two syllables, stress on the first. It chose ˈnibu and rejected ˈnɪbu, recorded in the entity entry's rejected list. The inventory is what makes the two forms two different phones; the tests pin both.

🤖 Generated with Claude Code

The phones line for AquaSynth: ruled IPA to an ordered `{ipa, stress}` list, one JSON object per cited entity. ``` weksa lower <store> <key> --phones {"language":"en-US","source":"ˈnibu","phones":[{"ipa":"n","stress":"none"},{"ipa":"i","stress":"primary"},{"ipa":"b","stress":"none"},{"ipa":"u","stress":"none"}]} ``` - Fixture (first commit, before the code): `data/phonology/fixtures/nibu-en-US.phones.json`; a test compares the library and the CLI output to it byte for byte. - Phone inventory: `data/phonology/en-US.phones.tsv` (class and tense/lax per phone, so /i/ and /ɪ/ are two phones). Read by `crates/weksa/src/phones.rs` through `include_str!`. - Segmenter: longest inventory match; `ˈ`/`ˌ` put primary/secondary stress on the next vowel (the nucleus), every other phone is `none`. - Refusals, never guesses: `LowerError::UnknownPhone {entity, at, text}` for a symbol the inventory lacks (or a language with no inventory), `LowerError::DanglingStress {entity, at}` for a mark with no vowel to land on. `--phones` conflicts with `--ipa`. - No durations, pitch or acoustics anywhere. Tests: `crates/weksa/tests/lower.rs` (tense /i/ passes; rejected lax /ɪ/ is a different phone and is not what the seed emits; stress placement; multi-character phones; unknown phone and dangling stress refusals; parser refusals) and `tests/cli.rs`. Full suite green. Mutation check: cargo-mutants is not installed in this run and the stopgap runner is not executable from a Self run (Permission denied), so I ran 21 hand-made mutants (stress marks swapped, shortest match, stress on any phone, wrong dangling offset, missing trailing check, lax parsed as tense, data edits for i/ɪ/u/tʃ, language tag, flag inversions, ...) against `lower` and `cli` tests: all 21 caught, none missed. Probes are not committed. A real cargo-mutants pass is still owed. Decision to rule on: the inventory is a tab-separated file embedded in the binary, not a CultCache store, to avoid a new store and CLI for one table. It should move to a typed store if phone inventories grow past en-US. **Tense/lax and its source.** /i/ is tense and /ɪ/ is lax in `data/phonology/en-US.phones.tsv` (the `tenseness` column; the file's `#` comment header now cites the ruling). The ruling it rests on is Nibu's of 2026-11-07, source `weksa-body:follow_up:chat-s3981-3983` (#minds): tense vowel, two syllables, stress on the first. It chose `ˈnibu` and rejected `ˈnɪbu`, recorded in the entity entry's `rejected` list. The inventory is what makes the two forms two different phones; the tests pin both. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The expected `weksa lower --phones` output for the proof packet, committed
before the code that must produce it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
data/phonology/en-US.phones.tsv lists the phones (tense/lax a column); the
segmenter splits ruled IPA into {ipa, stress} by longest inventory match and
refuses a symbol it does not list (LowerError::UnknownPhone) or a stress mark
with no vowel (DanglingStress). No durations, pitch or acoustics.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Author
Member

Main merged into the branch (0ca9b5e; both LowerError sets and both test groups kept). cargo test --locked --workspace all ok on 0ca9b5e, run by me and by an independent Soul pass: all promises hold, 11 hand mutants on phones.rs (10 killed, 1 equivalent). Lows go to a follow-up (empty IPA unpinned, multi-entity unpinned, ASCII g vs IPA ɡ refused, affricate/sequence ambiguity). Not run: cargo-mutants (not installed). Merging.

Main merged into the branch (0ca9b5e; both LowerError sets and both test groups kept). `cargo test --locked --workspace` all ok on 0ca9b5e, run by me and by an independent Soul pass: all promises hold, 11 hand mutants on phones.rs (10 killed, 1 equivalent). Lows go to a follow-up (empty IPA unpinned, multi-entity unpinned, ASCII g vs IPA ɡ refused, affricate/sequence ambiguity). Not run: cargo-mutants (not installed). Merging.
weksa merged commit fd18056b5b into main 2026-11-09 10:15:51 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
GameCult/Weksa!16
No description provided.