random_name_generator 4.0.2 → 4.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/AI-BOM.md +85 -0
- data/Gemfile +2 -3
- data/Gemfile.lock +4 -2
- data/README.md +8 -5
- data/docs/AUDIT_Fable_5.md +151 -0
- data/exe/random_name_generator +6 -4
- data/lib/languages/belter.txt +102 -0
- data/lib/languages/welsh.txt +91 -0
- data/lib/random_name_generator/syllable.rb +9 -8
- data/lib/random_name_generator/version.rb +1 -1
- data/lib/random_name_generator.rb +9 -7
- data/random_name_generator.gemspec +3 -2
- metadata +22 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d87857214e4e8fc700351d66eaf1c4858b8b3eff7149d00355abdfc7d2388f2a
|
|
4
|
+
data.tar.gz: d8ef37050bbdd23e0be3dd44f708b840033da928a6db157e7e73237cc4d2201c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8eafd322e156f87f867941204975a214686cb24fee1dbdd676e2a3b2d3ddb58ba596f04d27b35437eb9d3baf13456a0a0fc164fb34b60037c5cc351b405b0ffe
|
|
7
|
+
data.tar.gz: df1c293f18705b8eac7d46c8f3beac17f7d9fedd312a9fd2ab0f12a57c25d5af5ce9c0cc3b7523999a1072e2e0f6ee2b4e81e94921104e6f9da83ae0a0eef289
|
data/AI-BOM.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# AI Bill of Materials — random_name_generator
|
|
2
|
+
|
|
3
|
+
_Last updated: 2026-07-19 · random_name_generator v4.0.2_
|
|
4
|
+
|
|
5
|
+
An inventory of every AI component associated with this repository — development
|
|
6
|
+
tools used to build it, AI-generated content shipped within it, and external AI
|
|
7
|
+
services it integrates with (current or planned). Modeled on the SBOM concept
|
|
8
|
+
applied to AI systems.
|
|
9
|
+
|
|
10
|
+
**AI was first used in the `4.0.0` release.** Everything through the `3.x` line
|
|
11
|
+
was authored without AI assistance.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Development Tools
|
|
16
|
+
|
|
17
|
+
AI tools used to author this codebase. Not part of the shipped gem, but relevant
|
|
18
|
+
to provenance.
|
|
19
|
+
|
|
20
|
+
| Tool | Vendor | Role | Notes |
|
|
21
|
+
|------|--------|------|-------|
|
|
22
|
+
| Claude Code | Anthropic | AI coding assistant | Introduced in 4.0.0; drives the [`lang-gen`](./.claude/skills/lang-gen/SKILL.md) skill and general maintenance |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 2. AI Audits
|
|
27
|
+
|
|
28
|
+
Formal code reviews performed by AI models. Full reports in `docs/`.
|
|
29
|
+
|
|
30
|
+
| Date | Model | Report | Version |
|
|
31
|
+
|------|-------|--------|---------|
|
|
32
|
+
| 2026-07-19 | Claude Fable 5 | [`docs/AUDIT_Fable_5.md`](./docs/AUDIT_Fable_5.md) | v4.0.2 |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 3. AI-Generated Content
|
|
37
|
+
|
|
38
|
+
Syllable data and tooling produced with AI assistance and shipped in the gem.
|
|
39
|
+
All output was human-reviewed before commit.
|
|
40
|
+
|
|
41
|
+
| Artifact | Type | Tool | Notes |
|
|
42
|
+
|----------|------|------|-------|
|
|
43
|
+
| [`lang-gen`](./.claude/skills/lang-gen/SKILL.md) | Claude Code skill | — | Generates a fully integrated language from a plain-English theme |
|
|
44
|
+
| [German Curse](./lib/languages/experimental/german-curse.txt) (`GERMAN_CURSE`) | Language syllable file | `lang-gen` | Experimental; seeded from real roots, extended with invented syllables |
|
|
45
|
+
| [Demonic](./lib/languages/experimental/demonic.txt) (`DEMONIC`) | Language syllable file | `lang-gen` | Experimental; completed from infernal-name source lists |
|
|
46
|
+
| [Portable lang-gen prompt](./docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md) | Text prompt | — | LLM-agnostic version of the skill |
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 4. Algorithms Implemented
|
|
51
|
+
|
|
52
|
+
Generative logic built directly into the gem. **No machine-learning models and
|
|
53
|
+
no external ML dependencies** — all name generation is deterministic,
|
|
54
|
+
in-process, and driven by plain syllable-adjacency rules.
|
|
55
|
+
|
|
56
|
+
| Algorithm | Location | Notes |
|
|
57
|
+
|-----------|----------|-------|
|
|
58
|
+
| Probabilistic syllable composition | `RandomNameGenerator::Generator` | Random prefix/middle/suffix assembly honoring `+v`/`+c`/`-v`/`-c` adjacency flags |
|
|
59
|
+
| Syllable parsing / classification | `RandomNameGenerator::Syllable` | Parses each language-file line into a flagged syllable |
|
|
60
|
+
|
|
61
|
+
This is a rules-based random generator, not an AI/ML system. It is listed here
|
|
62
|
+
for completeness so the boundary between "AI-assisted authoring" and "AI at
|
|
63
|
+
runtime" is explicit.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 5. External AI Integrations
|
|
68
|
+
|
|
69
|
+
random_name_generator ships **zero external AI service dependencies**. It makes
|
|
70
|
+
no network calls and requires no API keys. None are planned.
|
|
71
|
+
|
|
72
|
+
| Service | Type | Status |
|
|
73
|
+
|---------|------|--------|
|
|
74
|
+
| — | — | None (current or planned) |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 6. References
|
|
79
|
+
|
|
80
|
+
| Document | Purpose |
|
|
81
|
+
|----------|---------|
|
|
82
|
+
| [`docs/AUDIT_Fable_5.md`](./docs/AUDIT_Fable_5.md) | Full-codebase audit with empirically verified findings |
|
|
83
|
+
| [`.claude/skills/lang-gen/SKILL.md`](./.claude/skills/lang-gen/SKILL.md) | The lang-gen language-generation skill |
|
|
84
|
+
| [`README.md`](./README.md) | Project overview, including the lang-gen workflow |
|
|
85
|
+
| [`docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md`](./docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md) | Paste-into-any-LLM version of the prompt |
|
data/Gemfile
CHANGED
|
@@ -7,14 +7,13 @@ gemspec
|
|
|
7
7
|
|
|
8
8
|
gem "pry"
|
|
9
9
|
gem "rake", "~> 13.4"
|
|
10
|
-
gem "slop", "~> 4.10.1"
|
|
11
10
|
|
|
12
11
|
group :development do
|
|
12
|
+
gem "logger" # logger leaves the default gems in Ruby 4.0; Reek (via dry-core) requires it implicitly.
|
|
13
13
|
gem "reek"
|
|
14
14
|
gem "rspec", "~> 3.12.0"
|
|
15
15
|
gem "rubocop"
|
|
16
16
|
gem "rubocop-rake", require: false
|
|
17
17
|
gem "rubocop-rspec", require: false
|
|
18
|
-
# tsort left the default gems in Ruby 4.1; RuboCop requires it implicitly.
|
|
19
|
-
gem "tsort"
|
|
18
|
+
gem "tsort" # tsort left the default gems in Ruby 4.1; RuboCop requires it implicitly.
|
|
20
19
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
random_name_generator (4.0.
|
|
4
|
+
random_name_generator (4.0.4)
|
|
5
|
+
slop (~> 4.10)
|
|
5
6
|
|
|
6
7
|
GEM
|
|
7
8
|
remote: https://rubygems.org/
|
|
@@ -40,6 +41,7 @@ GEM
|
|
|
40
41
|
zeitwerk (~> 2.6)
|
|
41
42
|
json (2.7.2)
|
|
42
43
|
language_server-protocol (3.17.0.3)
|
|
44
|
+
logger (1.7.0)
|
|
43
45
|
method_source (1.1.0)
|
|
44
46
|
parallel (1.26.3)
|
|
45
47
|
parser (3.3.5.0)
|
|
@@ -100,6 +102,7 @@ PLATFORMS
|
|
|
100
102
|
x86_64-linux
|
|
101
103
|
|
|
102
104
|
DEPENDENCIES
|
|
105
|
+
logger
|
|
103
106
|
pry
|
|
104
107
|
rake (~> 13.4)
|
|
105
108
|
random_name_generator!
|
|
@@ -108,7 +111,6 @@ DEPENDENCIES
|
|
|
108
111
|
rubocop
|
|
109
112
|
rubocop-rake
|
|
110
113
|
rubocop-rspec
|
|
111
|
-
slop (~> 4.10.1)
|
|
112
114
|
tsort
|
|
113
115
|
|
|
114
116
|
BUNDLED WITH
|
data/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
[](https://github.com/folkengine/random_name_generator/actions/workflows/ruby.yml)
|
|
5
5
|
[](https://codeclimate.com/github/folkengine/random_name_generator)
|
|
6
6
|
[](http://inch-ci.org/github/folkengine/random_name_generator)
|
|
7
|
+
[](./AI-BOM.md)
|
|
7
8
|
|
|
8
9
|
Ruby port of
|
|
9
10
|
[java-random-name-generator](https://github.com/folkengine/java-random-name-generator),
|
|
@@ -40,7 +41,7 @@ Or install it yourself as:
|
|
|
40
41
|
generate a first and last name for you:
|
|
41
42
|
|
|
42
43
|
```shell
|
|
43
|
-
$> exe/random_name_generator [-
|
|
44
|
+
$> exe/random_name_generator [-egrkbfcxdß?]
|
|
44
45
|
```
|
|
45
46
|
|
|
46
47
|
You can also install it so that it's instantly available to you:
|
|
@@ -58,12 +59,14 @@ RandomNameGenerator comes with several styles of syllable files:
|
|
|
58
59
|
[Fantasy](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/fantasy.txt),
|
|
59
60
|
[Goblin](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/goblin.txt),
|
|
60
61
|
[Roman](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/roman.txt),
|
|
62
|
+
[Klingon](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/klingon.txt),
|
|
63
|
+
[Welsh](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/welsh.txt),
|
|
61
64
|
and
|
|
62
|
-
[
|
|
65
|
+
[Belter](https://github.com/folkengine/random_name_generator/blob/master/lib/languages/belter.txt).
|
|
63
66
|
By default it uses Fantasy. Instantiate RandomNameGenerator and then
|
|
64
67
|
call compose on the object to generate a random name. If you don't pass
|
|
65
68
|
in the number of syllables you want for your name to compose, it will
|
|
66
|
-
randomly pick between
|
|
69
|
+
randomly pick between 2 and 5.
|
|
67
70
|
|
|
68
71
|
```ruby
|
|
69
72
|
require 'random_name_generator'
|
|
@@ -152,9 +155,9 @@ https://github.com/folkengine/random_name_generator.
|
|
|
152
155
|
This ships with a skill designed to work with [Claude Code](https://claude.com/claude-code)
|
|
153
156
|
and other LLMs under `.claude/skills/`.
|
|
154
157
|
|
|
155
|
-
- [lang-gen](.claude/skills/lang-gen/SKILL.md)
|
|
158
|
+
- [lang-gen](.claude/skills/lang-gen/SKILL.md): Generate a new random_name_generator language from a free-text theme (e.g. "german curse words"), assembles flagged pre/mid/sur syllable collections, registers the File constant, adds a smoke spec and README entry, and samples names to verify.
|
|
156
159
|
|
|
157
|
-
There is also [a text version](docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md) of the prompt that can be pasted into an LLM.
|
|
160
|
+
There is also [a text version](docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md) of the prompt that can be pasted into an LLM.
|
|
158
161
|
|
|
159
162
|
### lang-gen
|
|
160
163
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# random_name_generator Repository Audit — Claude Fable 5
|
|
2
|
+
|
|
3
|
+
_Date:_ 2026-07-19
|
|
4
|
+
_Repo:_ `random_name_generator` v4.0.2 (working tree, including the in-flight Demonic language and CLI additions)
|
|
5
|
+
_Model:_ Claude Fable 5 (`claude-fable-5`)
|
|
6
|
+
_Audit basis:_ Full codebase read + empirical verification — every defect below was reproduced with a live repro before being reported.
|
|
7
|
+
|
|
8
|
+
> **Remediation:** all nine findings were fixed on 2026-07-19, immediately following
|
|
9
|
+
> this audit, with regression specs added for each (suite grew from 61 to 168
|
|
10
|
+
> examples). The findings below describe the codebase as audited.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Executive Summary
|
|
15
|
+
|
|
16
|
+
`random_name_generator` is a small, clean, dependency-light gem that does what it says: green test suite (61 examples, 0 failures), RuboCop clean at repo settings (11 files, no offenses), no runtime gem dependencies, and a simple two-class core (`Generator`, `Syllable`) that is easy to hold in your head.
|
|
17
|
+
|
|
18
|
+
The audit found **3 high-severity defects** — one packaging, two runtime — and a cluster of medium/low issues concentrated in `Syllable`'s letter classification and error paths. None affect the happy path exercised by the specs, which is precisely why they have survived: the suite tests that names compose, not what happens at the edges.
|
|
19
|
+
|
|
20
|
+
| # | Finding | Severity | Effort to fix |
|
|
21
|
+
|---|---------|----------|---------------|
|
|
22
|
+
| 1 | CLI requires `slop`, but the gemspec declares no runtime dependency on it | High | Low |
|
|
23
|
+
| 2 | `compose(1)` crashes (`String#map`) despite documented single-syllable support | High | Low |
|
|
24
|
+
| 3 | Unsatisfiable adjacency flags hang the process in an infinite loop | High | Medium |
|
|
25
|
+
| 4 | Blank line in a language file crashes `Generator#refresh` | Medium | Low |
|
|
26
|
+
| 5 | Documented `Syllable` clone constructor crashes | Medium | Low |
|
|
27
|
+
| 6 | Cyrillic letters are in neither letter set — all flags in the four `-ru` files are silent no-ops | Medium | Medium |
|
|
28
|
+
| 7 | Empty-syllable guard is doubly broken (unreachable + malformed `raise`) | Low | Low |
|
|
29
|
+
| 8 | `y` classified as both vowel and consonant | Low | Low |
|
|
30
|
+
| 9 | Documentation drift on syllable-count range (README vs code vs spec) | Low | Low |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Scope and Method
|
|
35
|
+
|
|
36
|
+
**Verified by direct read (complete files):**
|
|
37
|
+
|
|
38
|
+
- `lib/random_name_generator.rb`
|
|
39
|
+
- `lib/random_name_generator/syllable.rb`
|
|
40
|
+
- `lib/random_name_generator/version.rb`
|
|
41
|
+
- `exe/random_name_generator`
|
|
42
|
+
- `spec/random_name_generator_spec.rb` and both spec fixture languages
|
|
43
|
+
- `random_name_generator.gemspec`, `Gemfile`, `Rakefile`
|
|
44
|
+
- `.github/workflows/ruby.yml`
|
|
45
|
+
- All 13 language files (curated, `-ru`, and experimental)
|
|
46
|
+
|
|
47
|
+
**Verified empirically:** every High/Medium finding was reproduced by executing the failing call under the bundle, including a `Timeout`-guarded repro for the infinite loop and a crafted two-line language file for the blank-line crash (which was also hit organically during this session, completing the WIP `demonic.txt`).
|
|
48
|
+
|
|
49
|
+
**Health signals:**
|
|
50
|
+
|
|
51
|
+
- `bundle exec rspec`: **61 examples, 0 failures**
|
|
52
|
+
- `bundle exec rubocop`: **11 files inspected, no offenses**
|
|
53
|
+
- `bundle exec rake` (default: spec + rubocop): green
|
|
54
|
+
- Runtime dependencies: none declared (see Finding 1 for the catch)
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Findings
|
|
59
|
+
|
|
60
|
+
### 1. Missing runtime dependency: the installed CLI cannot start (High)
|
|
61
|
+
|
|
62
|
+
`exe/random_name_generator` line 6 does `require "slop"`, but `slop` is declared only in the **Gemfile** (`gem "slop", "~> 4.10.1"`), which end users never see. The gemspec's dependency section is an empty template comment.
|
|
63
|
+
|
|
64
|
+
Consequence: after `gem install random_name_generator`, running `random_name_generator` raises `LoadError: cannot load such file -- slop` on any machine that doesn't coincidentally have slop installed. Every CI run and local dev invocation goes through Bundler, so this is invisible to the test suite by construction.
|
|
65
|
+
|
|
66
|
+
**Fix (low effort):** add `spec.add_dependency "slop", "~> 4.10"` to the gemspec and remove it from the Gemfile.
|
|
67
|
+
|
|
68
|
+
### 2. `compose(1)` crashes despite documented support (High)
|
|
69
|
+
|
|
70
|
+
`syllable.rb`'s header documentation states: *"In case of 1 syllable, name will be chosen from amongst the prefixes."* The code disagrees:
|
|
71
|
+
|
|
72
|
+
- `Generator#compose_array` (`lib/random_name_generator.rb:89`) returns a **String** (`@pre.to_s.capitalize`) when `count < 2`, an Array otherwise.
|
|
73
|
+
- `Generator#compose` (`lib/random_name_generator.rb:97`) unconditionally calls `.map` on that result.
|
|
74
|
+
|
|
75
|
+
Repro: `RandomNameGenerator.new(ELVEN).compose(1)` → `NoMethodError: undefined method 'map' for an instance of String`.
|
|
76
|
+
|
|
77
|
+
The type-splitting return also makes `compose_array(1)` a misnomer — callers get a String from a method named `_array`. **Fix:** have `compose_array` return `[@pre]` for `count < 2`; both bugs disappear and the return type becomes honest.
|
|
78
|
+
|
|
79
|
+
### 3. Unsatisfiable flag combinations hang the process (High)
|
|
80
|
+
|
|
81
|
+
`Generator#determine_next_syllable` (`lib/random_name_generator.rb:126-133`) is a bare `loop` that resamples until it draws a compatible syllable. If **no** syllable in the target bucket is compatible, it spins forever.
|
|
82
|
+
|
|
83
|
+
Repro (hangs, killed by timeout): a language whose only prefix is `-ba +v` (next must start with a vowel) and whose only suffix is `+kar`. Since the README explicitly invites users to supply their own syllable files, this is reachable input, not just an authoring mistake — one bad file freezes the host application with no diagnostic.
|
|
84
|
+
|
|
85
|
+
**Fix (medium effort):** track attempts (or pre-filter the bucket by compatibility) and raise a descriptive error when no compatible syllable exists.
|
|
86
|
+
|
|
87
|
+
### 4. Blank lines in language files crash loading (Medium)
|
|
88
|
+
|
|
89
|
+
`Generator#refresh` (`lib/random_name_generator.rb:138`) guards with `line.empty?`, but lines from `readlines` retain `"\n"`, so the guard never fires; `Syllable#parse_args` then splits the whitespace-only string into `[]` and calls `nil.empty?` (`syllable.rb:137`) → `NoMethodError`.
|
|
90
|
+
|
|
91
|
+
This is not hypothetical: the committed WIP `demonic.txt` used blank lines as bucket separators and crashed on load — likely part of why that file sat unfinished. **Fix:** `line.strip.empty?` in `refresh` (one word), or skip whitespace-only lines in `Syllable`.
|
|
92
|
+
|
|
93
|
+
### 5. Documented `Syllable` clone constructor crashes (Medium)
|
|
94
|
+
|
|
95
|
+
The `Syllable` header advertises `Syllable.new(syllable)` as a cloning idiom, and `initialize` has an explicit `args.is_a?(Syllable)` branch for it (`syllable.rb:56-60`). But line 49 — `@raw = args.strip` — executes first, and `Syllable` defines no `strip`.
|
|
96
|
+
|
|
97
|
+
Repro: `Syllable.new(Syllable.new("-foo +c"))` → `NoMethodError: undefined method 'strip' for an instance of RandomNameGenerator::Syllable`. The clone path is dead code; nothing in the gem or specs exercises it. **Fix:** `@raw = args.to_s.strip` (Syllable already implements `to_s`), plus a spec.
|
|
98
|
+
|
|
99
|
+
### 6. Adjacency flags are silent no-ops for Cyrillic (Medium)
|
|
100
|
+
|
|
101
|
+
`Syllable::VOWELS` and `CONSONANTS` (`syllable.rb:45-46`) contain only Latin/IPA characters. For a Cyrillic syllable, `vowel_first?`, `consonant_first?`, `vowel_last?`, and `consonant_last?` are all **false** (verified: `-анг` reports neither vowel- nor consonant-final).
|
|
102
|
+
|
|
103
|
+
Consequence: in `next_incompatible?`/`previous_incompatible?`, nothing can ever be incompatible, so every `+v/+c/-v/-c` flag in the four `-ru` language files — and all four carry them — is dead weight. Names still generate; they just ignore constraints the language authors wrote, silently. **Fix:** add Cyrillic vowels (а е ё и о у ы э ю я) and consonants to the sets, or classify by Unicode-aware rules.
|
|
104
|
+
|
|
105
|
+
### 7. The empty-syllable guard is doubly broken (Low)
|
|
106
|
+
|
|
107
|
+
`syllable.rb:137`: `raise ArgumentError "Empty String is not allowed." if syll.empty?`
|
|
108
|
+
|
|
109
|
+
- **Unreachable as intended:** an empty or whitespace input produces `args[0] == nil`, so `syll.empty?` raises `NoMethodError` on `nil` before the guard can act (this is the crash surfacing in Findings 4 and the `Syllable.new("")` repro).
|
|
110
|
+
- **Malformed if it were reached:** the missing comma means `ArgumentError "…"` is parsed as a method call, which would itself raise `NoMethodError`, not the intended `ArgumentError`.
|
|
111
|
+
|
|
112
|
+
**Fix:** `raise ArgumentError, "…" if syll.nil? || syll.empty?`.
|
|
113
|
+
|
|
114
|
+
### 8. `y` is both a vowel and a consonant (Low)
|
|
115
|
+
|
|
116
|
+
`y` appears in both letter sets, so `+v` and `+c` flags each accept y-initial syllables. Linguistically defensible for a semivowel, but it's undocumented and asymmetric (no other letter is dual-classified). Worth a comment if intentional.
|
|
117
|
+
|
|
118
|
+
### 9. Documentation drift on syllable counts (Low)
|
|
119
|
+
|
|
120
|
+
Three sources disagree with the code (`pick_number_of_syllables` samples from a weighted 2–5 distribution):
|
|
121
|
+
|
|
122
|
+
- README: "it will randomly pick between 3 and 6"
|
|
123
|
+
- Module doc in `random_name_generator.rb`: "between 2 and 5" ✓ (correct)
|
|
124
|
+
- Spec title: "returns an integer between 1 and 6" (assertion actually checks 1..5)
|
|
125
|
+
|
|
126
|
+
Also cosmetic: the CLI help text spells "syllable" as "eyllable" in four option descriptions.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Test Coverage Assessment
|
|
131
|
+
|
|
132
|
+
The suite is a smoke suite: it proves every bundled language loads and composes, and that syllable counts come out right. It does **not** cover:
|
|
133
|
+
|
|
134
|
+
- Adjacency-flag semantics (no spec would catch Finding 6 — the flags could be deleted entirely and the suite stays green)
|
|
135
|
+
- Any error path (Findings 2, 4, 5, 7 are all crashes in paths with zero coverage)
|
|
136
|
+
- The CLI (`exe/` has no test at all; Finding 1 ships silently)
|
|
137
|
+
|
|
138
|
+
The fixture languages (`test-micro.txt`, `test-tiny.txt`) are well-designed for flag testing — `test-tiny.txt` deliberately exercises every flag combination — but no assertion actually inspects flag behavior.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Recommendations (priority order)
|
|
143
|
+
|
|
144
|
+
1. **Gemspec:** declare the `slop` runtime dependency. One line; unbreaks the installed CLI.
|
|
145
|
+
2. **`compose_array`:** return `[@pre]` for `count < 2`. Fixes `compose(1)` and the return-type lie.
|
|
146
|
+
3. **`refresh`:** strip lines before the empty check. Fixes blank-line crashes and makes hand-authored files forgiving.
|
|
147
|
+
4. **`determine_next_syllable`:** bound the retry loop with a clear error. Turns a hang into a diagnosis.
|
|
148
|
+
5. **Letter sets:** add Cyrillic ranges so the `-ru` flags do what their authors intended; add a flag-behavior spec pinned to `test-tiny.txt`.
|
|
149
|
+
6. **Cleanups:** fix the `raise` syntax and clone constructor, correct the README/spec-title ranges, fix "eyllable".
|
|
150
|
+
|
|
151
|
+
None of these threaten the gem's architecture, which is sound for its size. The pattern across all findings is the same: the edges were never walked. A dozen targeted specs would lock in every fix above.
|
data/exe/random_name_generator
CHANGED
|
@@ -8,10 +8,11 @@ require "slop"
|
|
|
8
8
|
lang = RandomNameGenerator::FANTASY
|
|
9
9
|
|
|
10
10
|
opts = Slop.parse do |o|
|
|
11
|
-
o.bool "-e", "--elven", "Use Elven
|
|
12
|
-
o.bool "-g", "--goblin", "Use Goblin
|
|
13
|
-
o.bool "-r", "--roman", "Use Roman
|
|
14
|
-
o.bool "-k", "--klingon", "Use Klingon
|
|
11
|
+
o.bool "-e", "--elven", "Use Elven syllable file"
|
|
12
|
+
o.bool "-g", "--goblin", "Use Goblin syllable file"
|
|
13
|
+
o.bool "-r", "--roman", "Use Roman syllable file"
|
|
14
|
+
o.bool "-k", "--klingon", "Use Klingon syllable file"
|
|
15
|
+
o.bool "-b", "--belter", "Use Belter syllable file"
|
|
15
16
|
o.bool "-f", "--flipmode", "Flip mode in effect"
|
|
16
17
|
o.bool "-c", "--cyrillic", "Use Cyrillic mode"
|
|
17
18
|
o.bool "-x", "--xrated", "Generate Curse words [NEEDS WORK]"
|
|
@@ -30,6 +31,7 @@ else
|
|
|
30
31
|
lang = RandomNameGenerator::GOBLIN if opts.goblin?
|
|
31
32
|
lang = RandomNameGenerator::ROMAN if opts.roman?
|
|
32
33
|
lang = RandomNameGenerator::KLINGON if opts.klingon?
|
|
34
|
+
lang = RandomNameGenerator::BELTER if opts.belter?
|
|
33
35
|
lang = RandomNameGenerator::CURSE if opts.xrated?
|
|
34
36
|
lang = RandomNameGenerator::DEMONIC if opts.demonic?
|
|
35
37
|
lang = RandomNameGenerator::GERMAN_CURSE if opts.german_curse?
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
-bel
|
|
2
|
+
-ber
|
|
3
|
+
-bos
|
|
4
|
+
-che
|
|
5
|
+
-da +c
|
|
6
|
+
-du
|
|
7
|
+
-fel
|
|
8
|
+
-gut
|
|
9
|
+
-im
|
|
10
|
+
-ka
|
|
11
|
+
-kep
|
|
12
|
+
-ko
|
|
13
|
+
-kop
|
|
14
|
+
-kow
|
|
15
|
+
-mal
|
|
16
|
+
-mi +c
|
|
17
|
+
-nak
|
|
18
|
+
-oye
|
|
19
|
+
-pash
|
|
20
|
+
-pel
|
|
21
|
+
-pom
|
|
22
|
+
-sa +c
|
|
23
|
+
-sab
|
|
24
|
+
-sha
|
|
25
|
+
-shu
|
|
26
|
+
-ta
|
|
27
|
+
-teng
|
|
28
|
+
-tich
|
|
29
|
+
-to
|
|
30
|
+
-tu
|
|
31
|
+
-wel
|
|
32
|
+
-xa
|
|
33
|
+
-xi
|
|
34
|
+
-ya +c
|
|
35
|
+
-ze
|
|
36
|
+
al
|
|
37
|
+
ang
|
|
38
|
+
at
|
|
39
|
+
be
|
|
40
|
+
da
|
|
41
|
+
deng
|
|
42
|
+
do
|
|
43
|
+
eng
|
|
44
|
+
ere -c
|
|
45
|
+
ga
|
|
46
|
+
im -c
|
|
47
|
+
ka
|
|
48
|
+
ken
|
|
49
|
+
la
|
|
50
|
+
lom
|
|
51
|
+
low
|
|
52
|
+
lu
|
|
53
|
+
ma
|
|
54
|
+
mang
|
|
55
|
+
mi
|
|
56
|
+
na
|
|
57
|
+
nga
|
|
58
|
+
ok
|
|
59
|
+
ol
|
|
60
|
+
pa
|
|
61
|
+
ra
|
|
62
|
+
sa
|
|
63
|
+
se
|
|
64
|
+
sha
|
|
65
|
+
shi
|
|
66
|
+
ta
|
|
67
|
+
teng
|
|
68
|
+
ti
|
|
69
|
+
to
|
|
70
|
+
tu
|
|
71
|
+
un
|
|
72
|
+
wa
|
|
73
|
+
we
|
|
74
|
+
xa
|
|
75
|
+
ya
|
|
76
|
+
+a -c
|
|
77
|
+
+ang
|
|
78
|
+
+atna
|
|
79
|
+
+da
|
|
80
|
+
+deng
|
|
81
|
+
+eng -c
|
|
82
|
+
+im -c
|
|
83
|
+
+ing
|
|
84
|
+
+kang
|
|
85
|
+
+lowda
|
|
86
|
+
+mang
|
|
87
|
+
+na
|
|
88
|
+
+nya
|
|
89
|
+
+owda -c
|
|
90
|
+
+pa
|
|
91
|
+
+ra
|
|
92
|
+
+sa
|
|
93
|
+
+shang
|
|
94
|
+
+ta
|
|
95
|
+
+teng
|
|
96
|
+
+tim
|
|
97
|
+
+to
|
|
98
|
+
+tu
|
|
99
|
+
+wa
|
|
100
|
+
+wala
|
|
101
|
+
+xa
|
|
102
|
+
+ya
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
-ang +c
|
|
2
|
+
-bled
|
|
3
|
+
-bran
|
|
4
|
+
-bryn
|
|
5
|
+
-cad
|
|
6
|
+
-caer
|
|
7
|
+
-car
|
|
8
|
+
-cyn
|
|
9
|
+
-del
|
|
10
|
+
-dyf
|
|
11
|
+
-dyl
|
|
12
|
+
-eir
|
|
13
|
+
-em +c
|
|
14
|
+
-ffion
|
|
15
|
+
-gar
|
|
16
|
+
-ger
|
|
17
|
+
-gla +c
|
|
18
|
+
-glyn
|
|
19
|
+
-gru +c
|
|
20
|
+
-gwen
|
|
21
|
+
-gwyn
|
|
22
|
+
-hedd
|
|
23
|
+
-idr +v
|
|
24
|
+
-llan
|
|
25
|
+
-llew
|
|
26
|
+
-mab
|
|
27
|
+
-mor
|
|
28
|
+
-myr +c
|
|
29
|
+
-ol +c
|
|
30
|
+
-ow +v
|
|
31
|
+
-rhi +c
|
|
32
|
+
-rhos
|
|
33
|
+
-rhys
|
|
34
|
+
-tal
|
|
35
|
+
-tre +c
|
|
36
|
+
a -c
|
|
37
|
+
ad
|
|
38
|
+
ael
|
|
39
|
+
af
|
|
40
|
+
an
|
|
41
|
+
ar
|
|
42
|
+
aw
|
|
43
|
+
del
|
|
44
|
+
dwr
|
|
45
|
+
ed
|
|
46
|
+
el
|
|
47
|
+
ell
|
|
48
|
+
en
|
|
49
|
+
er
|
|
50
|
+
es
|
|
51
|
+
gar
|
|
52
|
+
goll
|
|
53
|
+
gwa
|
|
54
|
+
ha
|
|
55
|
+
i -c
|
|
56
|
+
ian
|
|
57
|
+
lan
|
|
58
|
+
lod
|
|
59
|
+
lyn
|
|
60
|
+
mai
|
|
61
|
+
o -c
|
|
62
|
+
on
|
|
63
|
+
rhy
|
|
64
|
+
ron
|
|
65
|
+
si
|
|
66
|
+
wal
|
|
67
|
+
wy
|
|
68
|
+
+ain -c
|
|
69
|
+
+aint -c
|
|
70
|
+
+an
|
|
71
|
+
+ddyn -v
|
|
72
|
+
+dwr
|
|
73
|
+
+dydd
|
|
74
|
+
+dys
|
|
75
|
+
+ed -c
|
|
76
|
+
+ell -c
|
|
77
|
+
+eth
|
|
78
|
+
+in
|
|
79
|
+
+ion
|
|
80
|
+
+lys
|
|
81
|
+
+og -c
|
|
82
|
+
+on
|
|
83
|
+
+rad
|
|
84
|
+
+ri
|
|
85
|
+
+rys
|
|
86
|
+
+ur -c
|
|
87
|
+
+wen
|
|
88
|
+
+wyn
|
|
89
|
+
+wyr
|
|
90
|
+
+ydd -c
|
|
91
|
+
+ys
|
|
@@ -42,10 +42,15 @@ module RandomNameGenerator
|
|
|
42
42
|
class Syllable
|
|
43
43
|
attr_reader :raw, :syllable, :next_syllable_requirement, :previous_syllable_requirement
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
# "y" appears in both sets deliberately: as a semivowel it satisfies
|
|
46
|
+
# either a +v or a +c adjacency requirement.
|
|
47
|
+
VOWELS = %w[i y ɨ ʉ ɯ u ɪ ʏ ʊ ɯ ʊ e ø ɘ ɵ ɤ o ø ə ɵ ɤ o ɛ œ ɜ ɞ ʌ ɔ æ ɐ ɞ a ɶ ä ɒ ɑ ö ü
|
|
48
|
+
а е ё и о у ы э ю я].freeze
|
|
49
|
+
CONSONANTS = %w[b ɓ ʙ β c d ɗ ɖ ð f g h j k l ł m ɱ n ɳ p q r s t v w x y z ß
|
|
50
|
+
б в г д ж з й к л м н п р с т ф х ц ч ш щ ь].freeze
|
|
47
51
|
|
|
48
52
|
def initialize(args)
|
|
53
|
+
args = args.raw if args.is_a?(Syllable)
|
|
49
54
|
@raw = args.strip
|
|
50
55
|
@syllable = ""
|
|
51
56
|
@is_prefix = false
|
|
@@ -53,11 +58,7 @@ module RandomNameGenerator
|
|
|
53
58
|
@next_syllable_requirement = :letter
|
|
54
59
|
@previous_syllable_requirement = :letter
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
parse_args(args.raw)
|
|
58
|
-
else
|
|
59
|
-
parse_args(args)
|
|
60
|
-
end
|
|
61
|
+
parse_args(args)
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
def incompatible?(next_syllable)
|
|
@@ -134,7 +135,7 @@ module RandomNameGenerator
|
|
|
134
135
|
end
|
|
135
136
|
|
|
136
137
|
def parse_syllable(syll)
|
|
137
|
-
raise ArgumentError "Empty String is not allowed." if syll.empty?
|
|
138
|
+
raise ArgumentError, "Empty String is not allowed." if syll.to_s.empty?
|
|
138
139
|
|
|
139
140
|
captures = /([+-]?)(.+)/.match(syll).captures
|
|
140
141
|
parse_prefix(captures[0])
|
|
@@ -31,7 +31,9 @@ module RandomNameGenerator
|
|
|
31
31
|
GOBLIN_RU = File.new("#{dirname}/languages/goblin-ru.txt")
|
|
32
32
|
ROMAN_RU = File.new("#{dirname}/languages/roman-ru.txt")
|
|
33
33
|
|
|
34
|
+
BELTER = File.new("#{dirname}/languages/belter.txt")
|
|
34
35
|
KLINGON = File.new("#{dirname}/languages/klingon.txt")
|
|
36
|
+
WELSH = File.new("#{dirname}/languages/welsh.txt")
|
|
35
37
|
|
|
36
38
|
# Experimental
|
|
37
39
|
CURSE = File.new("#{dirname}/languages/experimental/curse.txt")
|
|
@@ -87,7 +89,7 @@ module RandomNameGenerator
|
|
|
87
89
|
# Returns the composed name as an array of Syllables.
|
|
88
90
|
def compose_array(count = RandomNameGenerator.pick_number_of_syllables)
|
|
89
91
|
@pre = pre_syllables.sample(random: @rnd)
|
|
90
|
-
return @pre
|
|
92
|
+
return [@pre] if count < 2
|
|
91
93
|
|
|
92
94
|
name = determine_middle_syllables(count - 2, @pre)
|
|
93
95
|
name << determine_last_syllable(name.last)
|
|
@@ -125,18 +127,18 @@ module RandomNameGenerator
|
|
|
125
127
|
end
|
|
126
128
|
|
|
127
129
|
def determine_next_syllable(this_syllable, sampler)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
candidates = sampler.select { |syllable| this_syllable.compatible?(syllable) }
|
|
131
|
+
if candidates.empty?
|
|
132
|
+
raise ArgumentError,
|
|
133
|
+
"No syllable in #{@language.path} is compatible with \"#{this_syllable}\" — check its +v/+c/-v/-c flags"
|
|
132
134
|
end
|
|
133
|
-
|
|
135
|
+
candidates.sample(random: @rnd)
|
|
134
136
|
end
|
|
135
137
|
|
|
136
138
|
# Loops through the language file, and pushes each syllable into the correct array.
|
|
137
139
|
def refresh
|
|
138
140
|
@language.readlines.each do |line|
|
|
139
|
-
push(RandomNameGenerator::Syllable.new(line)) unless line.empty?
|
|
141
|
+
push(RandomNameGenerator::Syllable.new(line)) unless line.strip.empty?
|
|
140
142
|
end
|
|
141
143
|
@language.rewind
|
|
142
144
|
end
|
|
@@ -28,8 +28,9 @@ Gem::Specification.new do |spec|
|
|
|
28
28
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
29
29
|
spec.require_paths = ["lib"]
|
|
30
30
|
|
|
31
|
-
#
|
|
32
|
-
#
|
|
31
|
+
# The CLI in exe/ parses its options with slop, so it must ship as a
|
|
32
|
+
# runtime dependency — a Gemfile entry is invisible to installed gems.
|
|
33
|
+
spec.add_dependency "slop", "~> 4.10"
|
|
33
34
|
|
|
34
35
|
# For more information and examples about making a new gem, checkout our
|
|
35
36
|
# guide at: https://bundler.io/guides/creating_gem.html
|
metadata
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: random_name_generator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.0.
|
|
4
|
+
version: 4.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- folkengine
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
-
dependencies:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: slop
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '4.10'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '4.10'
|
|
13
26
|
description: Generates random names based upon custom collections of syllables. Styles
|
|
14
27
|
include Elvish, Fantasy, Goblin, and Roman.
|
|
15
28
|
email:
|
|
@@ -29,6 +42,7 @@ files:
|
|
|
29
42
|
- ".rspec"
|
|
30
43
|
- ".rubocop.yml"
|
|
31
44
|
- ".tool-versions"
|
|
45
|
+
- AI-BOM.md
|
|
32
46
|
- CHANGELOG.md
|
|
33
47
|
- Gemfile
|
|
34
48
|
- Gemfile.lock
|
|
@@ -39,10 +53,12 @@ files:
|
|
|
39
53
|
- bin/run
|
|
40
54
|
- bin/setup
|
|
41
55
|
- config.reek
|
|
56
|
+
- docs/AUDIT_Fable_5.md
|
|
42
57
|
- docs/superpowers/plans/2026-07-18-lang-gen-skill.md
|
|
43
58
|
- docs/superpowers/specs/2026-07-18-lang-gen-skill-design.md
|
|
44
59
|
- docs/superpowers/specs/2026-07-19-lang-gen-portable-prompt.md
|
|
45
60
|
- exe/random_name_generator
|
|
61
|
+
- lib/languages/belter.txt
|
|
46
62
|
- lib/languages/elven-ru.txt
|
|
47
63
|
- lib/languages/elven.txt
|
|
48
64
|
- lib/languages/experimental/curse.txt
|
|
@@ -55,6 +71,7 @@ files:
|
|
|
55
71
|
- lib/languages/klingon.txt
|
|
56
72
|
- lib/languages/roman-ru.txt
|
|
57
73
|
- lib/languages/roman.txt
|
|
74
|
+
- lib/languages/welsh.txt
|
|
58
75
|
- lib/random_name_generator.rb
|
|
59
76
|
- lib/random_name_generator/syllable.rb
|
|
60
77
|
- lib/random_name_generator/version.rb
|
|
@@ -67,7 +84,6 @@ metadata:
|
|
|
67
84
|
homepage_uri: https://github.com/folkengine/random_name_generator
|
|
68
85
|
source_code_uri: https://github.com/folkengine/random_name_generator
|
|
69
86
|
changelog_uri: https://github.com/folkengine/random_name_generator/CHANGELOG.md
|
|
70
|
-
post_install_message:
|
|
71
87
|
rdoc_options: []
|
|
72
88
|
require_paths:
|
|
73
89
|
- lib
|
|
@@ -82,8 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
82
98
|
- !ruby/object:Gem::Version
|
|
83
99
|
version: '0'
|
|
84
100
|
requirements: []
|
|
85
|
-
rubygems_version: 3.
|
|
86
|
-
signing_key:
|
|
101
|
+
rubygems_version: 3.6.9
|
|
87
102
|
specification_version: 4
|
|
88
103
|
summary: Random Name Generator
|
|
89
104
|
test_files: []
|