Weak endings carry their coda

The weak-end key is now the full final-syllable rime in its own
keyspace, and groups advertise their founding rime's final syllable:
infancy still joins see/be/me (open IY), but screams (IY M Z) no
longer rides the divinity/entirety family.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 03:40:39 -04:00
parent da3e3433d0
commit 8b7ac40f51
2 changed files with 26 additions and 5 deletions
+16 -5
View File
@@ -211,6 +211,11 @@ def founding_projections(key: str) -> dict[str, str]:
mk2 = _m2_key(ph)
if mk2:
out["multi2"] = mk2
# the founding rime's final syllable, for weak-ending joins
for i in range(len(ph) - 1, -1, -1):
if ph[i] in ARPA_VOWELS:
out["weak"] = "w:" + " ".join(ph[i:])
break
elif key.startswith("v:"): # vowel tail
out["slant"] = key
mk = _multi_key(key[2:].split())
@@ -222,6 +227,8 @@ def founding_projections(key: str) -> dict[str, str]:
out["multi2"] = key
elif key.startswith("c:"):
out["vc"] = key
elif key.startswith("w:"):
out["weak"] = key
return out
@@ -344,13 +351,17 @@ def multi_keys(word: str) -> tuple[str, ...]:
def weak_end_key(word: str) -> str | None:
"""The bare final vowel, stress be damned — at line ends poets rhyme
the weak syllable (infancy / see, eternity / be)."""
"""Final-syllable rime, stress be damned — at line ends poets rhyme
the weak syllable (infancy / see), but the coda still has to agree
(divinity does not rhyme screams)."""
ph = phones_for(word)
if not ph:
return None
vowels = _all_vowels(ph)
return ("v:" + vowels[-1]) if vowels else None
pl = ph.split()
for i in range(len(pl) - 1, -1, -1):
if pl[i][-1].isdigit():
return "w:" + DIGITS.sub("", " ".join(pl[i:]))
return None
def slant_key(word: str) -> str | None:
@@ -627,7 +638,7 @@ def analyze(draft: Draft):
# A leftover ending first tries to JOIN an existing group whose
# founding sound shares its vowel tail (time -> the mind/find group);
# otherwise leftovers form a new slant group among themselves.
group_by_slant = gmap_for("slant")
group_by_slant = {**gmap_for("slant"), **gmap_for("weak")}
by_slant = defaultdict(list)
for t in tokens:
if t["is_end"] and id(t) not in grouped:
+10
View File
@@ -435,3 +435,13 @@ def test_weak_ending_rhymes_at_line_end():
def test_cmu_override_stasis():
# CMU transcribes stasis as "STAH-seez"; everyone says STAY-sis
group_with("Oasis of stasis", "oasis", "stasis")
def test_weak_ending_requires_matching_coda():
# divinity ends open (..tee); screams ends IY M Z — same vowel,
# different coda, not the same family
text = ("Roots surrounding our entirety\n"
"Tangled in divinity\n"
"Weaving heaven among hellish screams")
fam = group_with(text, "entirety", "divinity")
assert "screams" not in fam