chore(maestro): drop dead maestro-names crate
Nothing in the workspace depended on it — session handles are generated client-side in web/src/names.ts, which carried its own copy of the wordlists. Also drops two unused deps: rust-embed (no crate used it; maestro-web embeds via include_dir) and urlencoding in maestro-cli. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c856333e4a
commit
bf7def5cf1
@@ -49,7 +49,6 @@ crates/
|
|||||||
├── maestro/ # umbrella binary `maestro` (cli + daemon + tui, feature-gated) (NAP-245)
|
├── maestro/ # umbrella binary `maestro` (cli + daemon + tui, feature-gated) (NAP-245)
|
||||||
├── maestro-protocol/ # shared wire types (NAP-211)
|
├── maestro-protocol/ # shared wire types (NAP-211)
|
||||||
├── maestro-config/ # shared on-disk config schema (NAP-233)
|
├── maestro-config/ # shared on-disk config schema (NAP-233)
|
||||||
├── maestro-names/ # orchestra-themed session-handle generator (#23)
|
|
||||||
├── maestro-cli/ # `maestro list/spawn/attach/kill` dispatch (NAP-216)
|
├── maestro-cli/ # `maestro list/spawn/attach/kill` dispatch (NAP-216)
|
||||||
├── maestro-daemon/ # `maestro daemon` — tmux wrapper + HTTP/WS API (NAP-212, NAP-214)
|
├── maestro-daemon/ # `maestro daemon` — tmux wrapper + HTTP/WS API (NAP-212, NAP-214)
|
||||||
├── maestro-web/ # standalone `maestro-web` browser UI (NAP-241, NAP-249)
|
├── maestro-web/ # standalone `maestro-web` browser UI (NAP-241, NAP-249)
|
||||||
@@ -61,9 +60,6 @@ web/ # browser SPA (NAP-218)
|
|||||||
|
|
||||||
See `docs/INSTALL.md` for end-user install instructions.
|
See `docs/INSTALL.md` for end-user install instructions.
|
||||||
|
|
||||||
The dated design docs under `docs/superpowers/specs/` predate the single-host
|
|
||||||
refactor; they carry banners marking what no longer applies.
|
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
- **TDD by default.** Failing test first, then minimal impl. Commit test + impl together.
|
- **TDD by default.** Failing test first, then minimal impl. Commit test + impl together.
|
||||||
|
|||||||
Generated
-8
@@ -1077,7 +1077,6 @@ dependencies = [
|
|||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"urlencoding",
|
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1147,13 +1146,6 @@ dependencies = [
|
|||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "maestro-names"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"rand 0.8.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "maestro-protocol"
|
name = "maestro-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -18,13 +18,11 @@ dirs = "5"
|
|||||||
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
|
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
maestro-config = { path = "crates/maestro-config" }
|
maestro-config = { path = "crates/maestro-config" }
|
||||||
maestro-names = { path = "crates/maestro-names" }
|
|
||||||
maestro-protocol = { path = "crates/maestro-protocol" }
|
maestro-protocol = { path = "crates/maestro-protocol" }
|
||||||
maestro-tui = { path = "crates/maestro-tui" }
|
maestro-tui = { path = "crates/maestro-tui" }
|
||||||
nix = { version = "0.29", default-features = false, features = ["term", "signal"] }
|
nix = { version = "0.29", default-features = false, features = ["term", "signal"] }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream"] }
|
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream"] }
|
||||||
rust-embed = "8"
|
|
||||||
include_dir = "0.7"
|
include_dir = "0.7"
|
||||||
mime_guess = "2"
|
mime_guess = "2"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ tokio-tungstenite = { workspace = true, features = ["rustls-tls-webpki-roots"] }
|
|||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
urlencoding = "2"
|
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "maestro-names"
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
publish.workspace = true
|
|
||||||
description = "Orchestra-flavoured session-handle generator (adjective × instrument) shared across Maestro"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
rand.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
rand = { workspace = true, features = ["small_rng"] }
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
//! `maestro-names` — the shared session-handle generator.
|
|
||||||
//!
|
|
||||||
//! Maestro is a conductor's podium for every shell you care about, so the
|
|
||||||
//! handles it hands out are drawn from the orchestra pit: a musical adjective
|
|
||||||
//! (tempo, dynamics, articulation) paired with an instrument or musical form.
|
|
||||||
//! The result reads like a marking on a score — `allegro-cello`,
|
|
||||||
//! `crescendo-oboe`, `staccato-viola`.
|
|
||||||
//!
|
|
||||||
//! ## Determinism
|
|
||||||
//!
|
|
||||||
//! Every pure path takes an **injected** [`rand::Rng`]; nothing here reaches
|
|
||||||
//! for `thread_rng()` or the clock, so seeded tests are reproducible (a repo
|
|
||||||
//! invariant). [`generate_thread`] is the one impure convenience wrapper for
|
|
||||||
//! production callers that just want a name and don't care about the seed.
|
|
||||||
//!
|
|
||||||
//! The Rust generator is the source of truth. If the web UI wants an instant
|
|
||||||
//! client-side reroll it can mirror [`ADJECTIVES`] / [`NOUNS`] verbatim, but
|
|
||||||
//! the wordlists live here.
|
|
||||||
|
|
||||||
use rand::seq::SliceRandom;
|
|
||||||
use rand::Rng;
|
|
||||||
|
|
||||||
/// Musical adjectives — tempo, dynamics, and articulation markings.
|
|
||||||
pub const ADJECTIVES: &[&str] = &[
|
|
||||||
"allegro",
|
|
||||||
"andante",
|
|
||||||
"adagio",
|
|
||||||
"presto",
|
|
||||||
"vivace",
|
|
||||||
"largo",
|
|
||||||
"grave",
|
|
||||||
"lento",
|
|
||||||
"moderato",
|
|
||||||
"crescendo",
|
|
||||||
"staccato",
|
|
||||||
"legato",
|
|
||||||
"forte",
|
|
||||||
"dolce",
|
|
||||||
"agitato",
|
|
||||||
"cantabile",
|
|
||||||
"sostenuto",
|
|
||||||
"tremolo",
|
|
||||||
"rubato",
|
|
||||||
"marcato",
|
|
||||||
"brillante",
|
|
||||||
"maestoso",
|
|
||||||
"grazioso",
|
|
||||||
"espressivo",
|
|
||||||
"tranquillo",
|
|
||||||
"animato",
|
|
||||||
"spirito",
|
|
||||||
"sotto",
|
|
||||||
"vivo",
|
|
||||||
"feroce",
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Musical nouns — instruments and forms from the concert hall.
|
|
||||||
pub const NOUNS: &[&str] = &[
|
|
||||||
"cello", "violin", "viola", "oboe", "flute", "clarinet", "bassoon", "trumpet", "trombone",
|
|
||||||
"tuba", "horn", "timpani", "harp", "piano", "organ", "lyre", "lute", "fugue", "sonata",
|
|
||||||
"octave", "chord", "cadence", "prelude", "rondo", "nocturne", "scherzo", "refrain", "coda",
|
|
||||||
"motif", "overture",
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Generate a single `adjective-noun` handle from an injected RNG.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rand::SeedableRng;
|
|
||||||
/// let mut rng = rand::rngs::SmallRng::seed_from_u64(7);
|
|
||||||
/// let name = maestro_names::generate(&mut rng);
|
|
||||||
/// assert!(name.contains('-'));
|
|
||||||
/// ```
|
|
||||||
pub fn generate<R: Rng + ?Sized>(rng: &mut R) -> String {
|
|
||||||
// Both lists are compile-time non-empty (asserted in tests), so the
|
|
||||||
// `choose` calls cannot return `None`.
|
|
||||||
let adj = ADJECTIVES.choose(rng).expect("ADJECTIVES is non-empty");
|
|
||||||
let noun = NOUNS.choose(rng).expect("NOUNS is non-empty");
|
|
||||||
format!("{adj}-{noun}")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a handle that isn't already taken.
|
|
||||||
///
|
|
||||||
/// Draws `adjective-noun` candidates from `rng`; if `is_taken` rejects one, a
|
|
||||||
/// numeric uniqueness suffix is appended and incremented (`-2`, `-3`, …) until
|
|
||||||
/// a free handle is found. The bare form is preferred — the suffix only
|
|
||||||
/// appears on collision, matching the score-marking aesthetic.
|
|
||||||
///
|
|
||||||
/// `is_taken` is the single source of "already used"; given the same RNG and
|
|
||||||
/// the same predicate the output is fully deterministic.
|
|
||||||
pub fn generate_unique<R, F>(rng: &mut R, is_taken: F) -> String
|
|
||||||
where
|
|
||||||
R: Rng + ?Sized,
|
|
||||||
F: Fn(&str) -> bool,
|
|
||||||
{
|
|
||||||
let base = generate(rng);
|
|
||||||
if !is_taken(&base) {
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
// Start at 2 so the first duplicate reads `name`, `name-2`, `name-3` …
|
|
||||||
for suffix in 2u64.. {
|
|
||||||
let candidate = format!("{base}-{suffix}");
|
|
||||||
if !is_taken(&candidate) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unreachable!("u64 suffix space is effectively inexhaustible")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Impure convenience wrapper: generate a handle from the thread RNG.
|
|
||||||
///
|
|
||||||
/// For production callers that just want a name. Tests and any deterministic
|
|
||||||
/// path should use [`generate`] / [`generate_unique`] with a seeded RNG.
|
|
||||||
pub fn generate_thread() -> String {
|
|
||||||
generate(&mut rand::thread_rng())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use rand::rngs::SmallRng;
|
|
||||||
use rand::SeedableRng;
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
fn seeded(seed: u64) -> SmallRng {
|
|
||||||
SmallRng::seed_from_u64(seed)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn wordlists_are_non_empty() {
|
|
||||||
assert!(!ADJECTIVES.is_empty());
|
|
||||||
assert!(!NOUNS.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn wordlists_have_no_duplicates() {
|
|
||||||
assert_eq!(
|
|
||||||
ADJECTIVES.iter().collect::<HashSet<_>>().len(),
|
|
||||||
ADJECTIVES.len(),
|
|
||||||
"duplicate adjective"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
NOUNS.iter().collect::<HashSet<_>>().len(),
|
|
||||||
NOUNS.len(),
|
|
||||||
"duplicate noun"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn generate_is_adjective_dash_noun() {
|
|
||||||
let mut rng = seeded(1);
|
|
||||||
let name = generate(&mut rng);
|
|
||||||
let (adj, noun) = name.split_once('-').expect("must contain a single dash");
|
|
||||||
assert!(ADJECTIVES.contains(&adj), "{adj} not an adjective");
|
|
||||||
assert!(NOUNS.contains(&noun), "{noun} not a noun");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn generation_is_deterministic_under_seed() {
|
|
||||||
// Same seed → identical sequence; this is the core repo invariant.
|
|
||||||
let mut a = seeded(42);
|
|
||||||
let mut b = seeded(42);
|
|
||||||
for _ in 0..50 {
|
|
||||||
assert_eq!(generate(&mut a), generate(&mut b));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn different_seeds_diverge() {
|
|
||||||
// Not a guarantee for a single draw, but across a run the sequences
|
|
||||||
// must differ — otherwise the RNG isn't actually being threaded.
|
|
||||||
let mut a = seeded(1);
|
|
||||||
let mut b = seeded(2);
|
|
||||||
let seq_a: Vec<_> = (0..30).map(|_| generate(&mut a)).collect();
|
|
||||||
let seq_b: Vec<_> = (0..30).map(|_| generate(&mut b)).collect();
|
|
||||||
assert_ne!(seq_a, seq_b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unique_returns_bare_name_when_free() {
|
|
||||||
let mut rng = seeded(3);
|
|
||||||
let name = generate_unique(&mut rng, |_| false);
|
|
||||||
assert!(
|
|
||||||
!name.ends_with("-2"),
|
|
||||||
"no suffix expected when nothing taken"
|
|
||||||
);
|
|
||||||
assert!(name.contains('-'));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unique_appends_incrementing_suffix_on_collision() {
|
|
||||||
// Reserve the bare name and the first two suffixed variants; the
|
|
||||||
// generator must walk past them to `-4`.
|
|
||||||
let mut probe = seeded(9);
|
|
||||||
let base = generate(&mut probe);
|
|
||||||
let taken: HashSet<String> = [base.clone(), format!("{base}-2"), format!("{base}-3")]
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut rng = seeded(9);
|
|
||||||
let name = generate_unique(&mut rng, |n| taken.contains(n));
|
|
||||||
assert_eq!(name, format!("{base}-4"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unique_suffix_starts_at_two() {
|
|
||||||
let mut probe = seeded(11);
|
|
||||||
let base = generate(&mut probe);
|
|
||||||
let taken: HashSet<String> = [base.clone()].into_iter().collect();
|
|
||||||
|
|
||||||
let mut rng = seeded(11);
|
|
||||||
let name = generate_unique(&mut rng, |n| taken.contains(n));
|
|
||||||
assert_eq!(name, format!("{base}-2"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-3
@@ -1,7 +1,6 @@
|
|||||||
// Client-side session-handle generator for the spawn form's instant reroll
|
// Client-side session-handle generator for the spawn form's instant reroll
|
||||||
// (#23/#27). Mirrors the wordlists in `crates/maestro-names/src/lib.rs` verbatim
|
// (#23/#27). The spawn form rerolls without a round trip, so the wordlists
|
||||||
// — the Rust generator is the source of truth, but the web wants a zero-round-
|
// live here; nothing server-side generates handles.
|
||||||
// trip reroll, so the lists are duplicated here. Keep them in sync.
|
|
||||||
|
|
||||||
/** Musical adjectives — tempo, dynamics, and articulation markings. */
|
/** Musical adjectives — tempo, dynamics, and articulation markings. */
|
||||||
const ADJECTIVES = [
|
const ADJECTIVES = [
|
||||||
|
|||||||
Reference in New Issue
Block a user