Daily Word: grow the hopeful answer pool + lock it with a validation test

Per Codex. Pool grown 51/44 → 115/104 hopeful answers (5/6 letter) via the
agreed workflow: LLM proposes themed candidates → code filters to the bundled
guess dictionary (length/alpha/dedup) → human spot-check prunes tone-drift
("growl", "plain", "color"…). ~3.5-month runway before repeats per variant.

test_wordpool.py locks the invariant in CI: every answer must be lowercase
alpha, correct length, unique, and present in words-5/6.json — so no future
addition can become an unguessable puzzle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-10 17:34:44 -04:00
parent 215a5c4d64
commit a7fb8e5739
2 changed files with 149 additions and 5 deletions
+129 -5
View File
@@ -1,101 +1,225 @@
{ {
"5": [ "5": [
"adore",
"agile",
"alive",
"amaze",
"ample", "ample",
"amply", "angel",
"arise",
"aroma",
"award", "award",
"aware",
"beams",
"bless",
"bliss", "bliss",
"bloom", "bloom",
"blush",
"bonus", "bonus",
"brave", "brave",
"brisk",
"charm", "charm",
"cheer", "cheer",
"chime",
"clean",
"clear", "clear",
"climb",
"craft",
"daisy",
"dance",
"dawns",
"dream", "dream",
"drift",
"eager",
"earth",
"elate",
"enjoy",
"faith",
"fancy",
"favor", "favor",
"feast",
"fresh", "fresh",
"gifts", "gifts",
"glade",
"gleam",
"glide",
"glory",
"glows", "glows",
"grace", "grace",
"grand",
"green", "green",
"grins",
"happy", "happy",
"heals", "heals",
"heart", "heart",
"honor", "honor",
"hopes", "hopes",
"humor",
"ideal", "ideal",
"jewel",
"jolly",
"laugh",
"learn", "learn",
"light", "light",
"lucky", "lucky",
"magic",
"maple",
"mends", "mends",
"mercy", "mercy",
"merit",
"merry", "merry",
"mirth",
"music",
"noble", "noble",
"oasis",
"peace", "peace",
"plant",
"pluck",
"poise",
"pride",
"prize", "prize",
"proud",
"quiet", "quiet",
"reach", "quilt",
"ready", "ready",
"relax", "relax",
"renew", "renew",
"river",
"savor", "savor",
"share", "share",
"shine", "shine",
"smile", "smile",
"space",
"spark",
"stars",
"still", "still",
"sunny", "sunny",
"sweet",
"swift",
"teach", "teach",
"thank", "thank",
"treat", "treat",
"trust", "trust",
"truth", "truth",
"tulip",
"unity", "unity",
"value", "value",
"vital" "vigor",
"vital",
"vivid",
"water",
"winds",
"wings",
"witty",
"worth",
"youth",
"yummy",
"zesty"
], ],
"6": [ "6": [
"adored",
"ascend",
"aspire",
"beauty", "beauty",
"bestow", "bestow",
"better",
"bouncy",
"breeze",
"bright", "bright",
"credit", "bubbly",
"calmly",
"caring",
"cheery",
"cherub",
"chirpy",
"clever",
"dainty",
"dazzle",
"devote", "devote",
"divine",
"dreamy",
"earthy",
"esteem", "esteem",
"family",
"flower",
"fondly",
"forest",
"friend",
"garden",
"gather", "gather",
"genial",
"gentle", "gentle",
"giving", "giving",
"glossy",
"golden",
"growth", "growth",
"health", "health",
"heaven",
"honest",
"joyful", "joyful",
"joyous",
"kindly", "kindly",
"laurel",
"lively", "lively",
"lovely", "lovely",
"loving",
"mellow", "mellow",
"mended", "mended",
"morale",
"nature", "nature",
"nestle",
"nicely",
"nuzzle",
"please",
"plenty",
"praise", "praise",
"pretty",
"prized",
"purify",
"purity",
"quaint",
"reborn", "reborn",
"refine",
"relish",
"renews",
"repair", "repair",
"rescue", "rescue",
"revive", "revive",
"reward", "reward",
"savior",
"savory",
"secure", "secure",
"serene", "serene",
"settle",
"simple", "simple",
"smiled", "smiled",
"smooth",
"soothe", "soothe",
"spirit", "spirit",
"spring",
"sprout",
"steady", "steady",
"strong", "strong",
"summit", "summit",
"superb", "superb",
"supple",
"tender", "tender",
"thanks", "thanks",
"thrive", "thrive",
"unfold",
"united", "united",
"uplift", "uplift",
"valued", "valued",
"velvet",
"verity",
"vision", "vision",
"voyage",
"warmly",
"warmth", "warmth",
"willow",
"wisdom", "wisdom",
"wonder" "wonder",
"zenith",
"zephyr"
] ]
} }
+20
View File
@@ -0,0 +1,20 @@
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
def test_wordpool_answers_are_valid_and_guessable():
"""Every daily-word answer must be lowercase alpha, the right length, unique,
and present in the bundled guess dictionary — so no answer is ever unguessable.
Locks the 'code disposes' invariant against future pool additions."""
pool = json.loads((ROOT / "goodnews" / "data" / "wordpool.json").read_text())
for variant, n in (("5", 5), ("6", 6)):
dictwords = set(json.loads((ROOT / "frontend" / "static" / f"words-{n}.json").read_text()))
answers = pool[variant]
assert answers, f"{variant}-letter pool is empty"
assert len(answers) == len(set(answers)), f"{variant}-letter pool has duplicates"
for w in answers:
assert isinstance(w, str) and w.isalpha() and w.islower(), f"bad token: {w!r}"
assert len(w) == n, f"{w!r} is not {n} letters"
assert w in dictwords, f"{w!r} not in the guess dictionary → would be unguessable"