6Random word generator for session and plan names.
10Produces readable random-word identifiers (e.g., ``sunny-weaving-honey``)
11to replace timestamp-based defaults for sessions and plans.
15- Maintain three curated word lists (adjectives, gerunds, nouns)
16- Generate kebab-case n-word identifiers via :func:`random_words`
17- Produce unique names with collision avoidance via :func:`random_name`
18- Generate numbered plan filenames via :func:`random_plan_filename`
24 L2 — module load lifecycle
25 L3 — generation events (name produced, collisions retried)
26 L4 — per-call word selection details
30- :func:`random_words` returns ``list[str]`` of lowercase words
31- :func:`random_name` returns a unique kebab-case string (no underscores)
32- :func:`random_plan_filename` returns ``NNN-word-word-word.md``
33- Word lists are module-level tuples; immutable after import
34- No external dependencies required
38- oc_diagnostics (structured debug logging via _dbg)
40from __future__
import annotations
43from typing
import Callable
45from oc_diagnostics
import _dbg
47_ADJECTIVES: tuple[str, ...] = (
48 "amber",
"ancient",
"azure",
"bold",
"bright",
"broad",
"calm",
"clear",
49 "cold",
"cool",
"crisp",
"crystal",
"curious",
"dark",
"deep",
"distant",
50 "dreamy",
"drifting",
"dusky",
"electric",
"emerald",
"eternal",
"fading",
51 "fierce",
"floating",
"fluid",
"fragrant",
"free",
"frosted",
"frozen",
52 "gentle",
"gilded",
"gleaming",
"glowing",
"golden",
"graceful",
"grand",
53 "hidden",
"hollow",
"humble",
"hushed",
"icy",
"idle",
"indigo",
"inner",
54 "jade",
"jovial",
"keen",
"kind",
"light",
"lonely",
"lucid",
"luminous",
55 "mellow",
"misty",
"moonlit",
"mossy",
"muted",
"mystic",
"narrow",
56 "nimble",
"noble",
"northern",
"ocean",
"open",
"pale",
"patient",
57 "peaceful",
"phantom",
"quiet",
"radiant",
"rapid",
"rare",
"raw",
58 "restless",
"rising",
"rosy",
"rustic",
"sacred",
"scattered",
"serene",
59 "shimmering",
"silent",
"silver",
"simple",
"sleek",
"slender",
"slow",
60 "smooth",
"soft",
"solar",
"solemn",
"solitary",
"somber",
"sparkling",
61 "still",
"stoic",
"sturdy",
"subtle",
"summer",
"sunlit",
"sunny",
62 "supple",
"swift",
"tender",
"timeless",
"twilight",
"vast",
"velvet",
63 "verdant",
"vibrant",
"vivid",
"wandering",
"warm",
"weathered",
"wild",
64 "windy",
"wintry",
"wise",
"wooden",
"woven",
"young",
"zephyr",
65 "acorn",
"breezy",
"burnished",
"canopy",
"cedar",
"cobalt",
"coral",
66 "crimson",
"dawning",
"dense",
"dappled",
"earthy",
"enchanted",
67 "fern",
"floral",
"foggy",
"granite",
"grassy",
"harvest",
"heather",
68 "highland",
"hazy",
"ivory",
"jasper",
"lavender",
"leafy",
"linen",
69 "lofty",
"lunar",
"maple",
"marble",
"meadow",
"midnight",
"mineral",
70 "morning",
"mosaic",
"murky",
"native",
"nebula",
"onyx",
"opaque",
71 "opaline",
"orchid",
"overcast",
"pearlescent",
"pebbled",
"petal",
72 "pine",
"plains",
"polished",
"porous",
"primal",
"pristine",
"quartz",
73 "reedy",
"regal",
"remote",
"rippling",
"roaming",
"rocky",
"rugged",
74 "sagebrush",
"sandy",
"sapphire",
"saturated",
"shadowed",
"shallow",
75 "shaded",
"shifting",
"shoreline",
"silken",
"skeletal",
"sleepy",
76 "sloped",
"smoky",
"snowy",
"speckled",
"spiral",
"starlit",
"steady",
77 "steep",
"stony",
"stormy",
"stranded",
"stripped",
"sunken",
"sunward",
78 "tapered",
"temperate",
"textured",
"tidal",
"timbered",
"tinted",
79 "towering",
"tranquil",
"tropical",
"tundra",
"turquoise",
"twisted",
80 "undulant",
"uneven",
"untamed",
"upland",
"veiled",
"volcanic",
81 "watery",
"winding",
"woolly",
"worn",
"wrapped",
"amber",
"ashen",
82 "barren",
"billowing",
"bleached",
"brackish",
"braided",
"breathless",
83 "brine",
"briny",
"broken",
"cascading",
"ceaseless",
"charcoal",
84 "chill",
"chiseled",
"churning",
"circling",
"coppery",
"crested",
85 "crossing",
"crunchy",
"crystalline",
"damp",
"debris",
"decayed",
86 "dewed",
"dim",
"dormant",
"dried",
"dusty",
"fading",
"fallen",
87 "fallow",
"fertile",
"filmy",
"flinty",
"flowing",
"foamy",
"forested",
88 "fractured",
"fragile",
"glistening",
"glittering",
"gravel",
"gray",
89 "green",
"grotto",
"groundless",
"gushing",
"halcyon",
"hammered",
90 "highland",
"hollow",
"horizoned",
"humid",
"incandescent",
"inky",
91 "island",
"knotted",
"lakeward",
"lapping",
"latticed",
"leaning",
92 "limestone",
"loamy",
"lonesome",
"loosened",
"lowland",
"luminant",
95_GERUNDS: tuple[str, ...] = (
96 "blooming",
"blossoming",
"bouncing",
"breathing",
"brewing",
"bridging",
97 "burning",
"carving",
"cascading",
"casting",
"chasing",
"circling",
98 "climbing",
"coiling",
"cooling",
"crossing",
"dancing",
"drifting",
99 "echoing",
"falling",
"fading",
"floating",
"flowing",
"flying",
100 "folding",
"forging",
"gathering",
"gliding",
"gleaming",
"glowing",
101 "growing",
"guiding",
"healing",
"humming",
"hunting",
"journeying",
102 "leaping",
"learning",
"lifting",
"listening",
"looping",
"mapping",
103 "melting",
"mending",
"merging",
"mirroring",
"moving",
"murmuring",
104 "opening",
"pacing",
"passing",
"pausing",
"peeling",
"reaching",
105 "reading",
"resting",
"returning",
"rising",
"roaming",
"rolling",
106 "rooting",
"running",
"sailing",
"searching",
"seeking",
"shifting",
107 "shining",
"singing",
"sinking",
"sleeping",
"sliding",
"soaring",
108 "sorting",
"sparkling",
"spinning",
"spreading",
"sprouting",
"staying",
109 "streaming",
"stretching",
"sweeping",
"swelling",
"swirling",
110 "threading",
"tumbling",
"turning",
"unfolding",
"walking",
"wandering",
111 "watching",
"weaving",
"weaving",
"whistling",
"winding",
"wondering",
112 "working",
"wrapping",
"yielding",
"arching",
"awakening",
"bending",
113 "binding",
"braiding",
"branching",
"breaking",
"calming",
"changing",
114 "charting",
"clearing",
"collecting",
"combining",
"condensing",
115 "connecting",
"containing",
"converging",
"cooling",
"covering",
116 "cresting",
"curling",
"curving",
"cutting",
"deepening",
"descending",
117 "digging",
"dimming",
"dissolving",
"diverging",
"diving",
"drawing",
118 "edging",
"emerging",
"encircling",
"ending",
"eroding",
"escaping",
119 "etching",
"expanding",
"extending",
"filtering",
"finding",
"finishing",
120 "flashing",
"flattening",
"fleeing",
"flickering",
"flipping",
"flooding",
121 "flushing",
"focusing",
"following",
"forming",
"fracturing",
"gathering",
122 "grounding",
"hanging",
"hiding",
"holding",
"hovering",
"illuminating",
123 "intersecting",
"intertwining",
"isolating",
"joining",
"keeping",
124 "kindling",
"knotting",
"landing",
"launching",
"layering",
"leaving",
125 "lining",
"linking",
"loosening",
"lowering",
"making",
"marking",
126 "meeting",
"misting",
"narrowing",
"navigating",
"nearing",
"nesting",
127 "noting",
"nudging",
"outlining",
"overflowing",
"overlapping",
128 "parting",
"peaking",
"pooling",
"pouring",
"pressing",
"pulling",
129 "pushing",
"radiating",
"raising",
"raking",
"redirecting",
"reflecting",
130 "releasing",
"remaining",
"renewing",
"repeating",
"resolving",
131 "retreating",
"revealing",
"rewinding",
"rippling",
"routing",
132 "scattering",
"sealing",
"sending",
"separating",
"setting",
"shaping",
133 "sharing",
"sheltering",
"showing",
"signaling",
"skimming",
"skipping",
134 "smoothing",
"spiraling",
"stabilizing",
"stacking",
"starting",
135 "steadying",
"stepping",
"stitching",
"stopping",
"submerging",
136 "surfacing",
"surrounding",
"swaying",
"tapping",
"teaching",
"testing",
137 "tightening",
"tilting",
"tracing",
"tracking",
"trading",
"trailing",
138 "transforming",
"traveling",
"traversing",
"trimming",
"twisting",
139 "uncovering",
"unfolding",
"unwinding",
"upwelling",
"vanishing",
140 "varying",
"venturing",
"viewing",
"waking",
"warming",
"weathering",
143_NOUNS: tuple[str, ...] = (
144 "amber",
"anchor",
"arc",
"ash",
"basin",
"bay",
"beacon",
"bloom",
145 "blossom",
"bone",
"branch",
"bridge",
"brook",
"candle",
"canyon",
146 "cedar",
"channel",
"clay",
"clearing",
"cliff",
"cloud",
"coal",
147 "coast",
"comet",
"compass",
"coral",
"crest",
"current",
"dawn",
148 "delta",
"den",
"desert",
"dew",
"drift",
"dune",
"dust",
"echo",
149 "ember",
"estuary",
"fawn",
"feather",
"fern",
"field",
"fjord",
150 "flame",
"flint",
"fog",
"forest",
"frost",
"gale",
"garden",
"gate",
151 "glade",
"glacier",
"gorge",
"grain",
"granite",
"grove",
"harbor",
152 "haven",
"hawk",
"heath",
"hill",
"hollow",
"honey",
"horizon",
153 "hue",
"inlet",
"island",
"jade",
"kelp",
"lagoon",
"lake",
"lantern",
154 "lark",
"laurel",
"leaf",
"ledge",
"lighthouse",
"loch",
"log",
155 "marsh",
"meadow",
"mesa",
"mist",
"moon",
"moonbeam",
"moss",
156 "mountain",
"mud",
"nest",
"night",
"oak",
"ocean",
"orbit",
"otter",
157 "passage",
"path",
"peat",
"pebble",
"peak",
"pine",
"plain",
"pool",
158 "prairie",
"quartz",
"rain",
"rapids",
"ravine",
"reef",
"ridge",
159 "rift",
"ripple",
"river",
"rock",
"root",
"rose",
"ruin",
"salt",
160 "sand",
"sandbar",
"sea",
"sedge",
"sediment",
"shadow",
"shale",
161 "shelf",
"shore",
"shrub",
"sierra",
"signal",
"sky",
"slate",
162 "snow",
"soil",
"spring",
"star",
"starlight",
"steppe",
"stone",
163 "storm",
"strand",
"stream",
"summit",
"swamp",
"swift",
"thorn",
164 "tide",
"timber",
"tundra",
"vale",
"valley",
"vapor",
"vine",
165 "volcano",
"wave",
"wetland",
"willow",
"wind",
"woodland",
"wraith",
166 "acorn",
"algae",
"alder",
"alluvium",
"anemone",
"antler",
"aster",
167 "aurora",
"bark",
"bedrock",
"birch",
"blight",
"bluff",
"bog",
168 "boulder",
"bract",
"brine",
"burrow",
"butte",
"cairn",
"canopy",
169 "cape",
"carve",
"cascade",
"cave",
"chamomile",
"chasm",
"cirque",
170 "cobble",
"copse",
"cove",
"crater",
"crag",
"creek",
"crevice",
171 "crust",
"culm",
"cyme",
"dale",
"datum",
"debris",
"declivity",
172 "depths",
"dewdrop",
"divide",
"downdraft",
"downpour",
"dragonfly",
173 "eddy",
"elevation",
"elm",
"erosion",
"escarpment",
"esteem",
174 "expanse",
"faultline",
"fen",
"fernbed",
"fissure",
"flare",
175 "floodplain",
"flume",
"foliage",
"foothill",
"ford",
"geyser",
176 "gust",
"habitat",
"halo",
"hardwood",
"headland",
"heartwood",
177 "helix",
"heron",
"hillside",
"hoarfrost",
"hotspring",
"humus",
178 "hurricane",
"hydra",
"icefall",
"icefield",
"ignite",
"impasse",
179 "inlet",
"iris",
"ironwood",
"isthmus",
"jetty",
"juniper",
"karst",
180 "kettle",
"kindling",
"knoll",
"lichen",
"lignite",
"limb",
"linden",
181 "lithosphere",
"loam",
"loess",
"lumen",
"lupine",
"mantle",
182 "manzanita",
"marl",
"massif",
"millstone",
"moraine",
"morass",
183 "mudflat",
"mulch",
"mussel",
"narrows",
"nocturne",
"nodule",
184 "nutrient",
"obsidian",
"olivine",
"onyx",
"opal",
"outcrop",
185 "overhang",
"oxbow",
"oyster",
"parcel",
"pasture",
"penumbra",
186 "permafrost",
"pillar",
"pinnacle",
"plateau",
"plover",
"plume",
187 "polarity",
"pollen",
"pond",
"poplar",
"pumice",
"reedbed",
188 "remnant",
"resin",
"rill",
"rivulet",
"rockfall",
"runoff",
189 "rushes",
"sandstone",
"sapwood",
"savanna",
"scarp",
"schist",
190 "scree",
"seabed",
"seam",
"seawall",
"sedge",
"seep",
"shingle",
191 "silhouette",
"silt",
"sinew",
"skyline",
"slough",
"snowdrift",
192 "snowfield",
"softwood",
"solstice",
"spillway",
"spruce",
"spur",
193 "starling",
"strata",
"subsoil",
"sundew",
"tableland",
"talus",
194 "tamarack",
"tangent",
"tendril",
"terrace",
"thicket",
"thornbush",
195 "thunderhead",
"topsoil",
"torrent",
"treeline",
"tributary",
196 "turf",
"twilight",
"understory",
"upland",
"uplift",
"vestige",
197 "vista",
"vortex",
"watershed",
"weathervane",
"wetgrass",
198 "windbreak",
"windfall",
"windrow",
"winterkill",
"wren",
"yew",
203 """Return n random words from the internal word lists.
205 For n=3 draws one adjective, one gerund, one noun (in order).
206 For other n, samples from the combined pool without replacement.
208 func =
"random_words"
211 random.choice(_ADJECTIVES),
212 random.choice(_GERUNDS),
213 random.choice(_NOUNS),
216 pool = list(_ADJECTIVES + _GERUNDS + _NOUNS)
217 words = random.sample(pool, min(n, len(pool)))
218 _dbg(
"OC_CHAT.MAIN", func, f
"selected {words}", 4)
223 exists_fn: Callable[[str], bool] |
None =
None,
225 max_attempts: int = 5,
227 """Generate a unique kebab-case random name.
229 Retries on collision (via exists_fn) up to max_attempts times.
230 Falls back to appending a 4-digit suffix if all attempts collide.
233 exists_fn: Optional callable; returns True if name already taken.
234 n: Number of words in the name.
235 max_attempts: Maximum retries before falling back to suffix.
238 Kebab-case string such as ``sunny-weaving-honey``.
241 for attempt
in range(max_attempts):
243 if exists_fn
is None or not exists_fn(name):
244 _dbg(
"OC_CHAT.MAIN", func, f
"generated {name!r} (attempt {attempt + 1})", 3)
246 _dbg(
"OC_CHAT.MAIN", func, f
"collision on {name!r}, retrying", 3)
249 name = f
"{'-'.join(random_words(n))}-{random.randint(1000, 9999)}"
250 _dbg(
"OC_CHAT.MAIN", func, f
"fallback to suffixed {name!r}", 3)
255 """Generate a numbered plan filename with random-word tail.
257 Format: ``NNN-word-word-word.md`` (zero-padded 3-digit prefix).
258 Example: ``007-sunny-weaving-honey.md``
261 prefix_number: Numeric prefix (0–999).
262 n: Number of random words in the tail.
265 Filename string including the ``.md`` extension.
267 func =
"random_plan_filename"
269 filename = f
"{prefix_number:03d}-{tail}.md"
270 _dbg(
"OC_CHAT.MAIN", func, f
"generated {filename!r}", 3)
list[str] random_words(int n=3)
str random_name(Callable[[str], bool]|None exists_fn=None, int n=3, int max_attempts=5)
str random_plan_filename(int prefix_number, int n=3)