feat(seed): --seed flag, ambient wiring, and /runda hardening (ADR-0048 P1.4 + DA)

P1.4 — user-visible surface:
- Grammar: `seed <table> [count] [--seed <n>]` (the first DSL flag with a
  value); build_seed disambiguates the seed value from the positional count.
- Verified the auto-wired surface: table-name completion, --seed offered as
  a candidate, validity consistent with `show data`, an ADR-0042 near-miss
  row for bare `seed`, and render tests for the seed outcome.

/runda hardening — eight DA findings, all resolved:
- FK sampling now uses ORDER BY so --seed reproducibility no longer relies
  on SQLite's unspecified DISTINCT order (D4).
- shortid columns now generate from seed's seeded RNG (new
  shortid::generate_with_rng) — D4 now holds with no exceptions.
- Added the missing coverage the DA flagged: undo-one-step (D15), replay
  re-runs a seed line (D16), advanced-mode (D5), atomic rollback on a
  constraint failure, seed 0 no-op, complex-CHECK advisory (D17), and
  FK + shortid reproducibility.

2358 pass / 0 fail / 0 skip, clippy all-targets clean.
This commit is contained in:
claude@clouddev1
2026-06-11 21:45:34 +00:00
parent e6ff63daa2
commit fbd219b631
8 changed files with 389 additions and 37 deletions
+10 -6
View File
@@ -18,17 +18,21 @@ const DEFAULT_LEN: usize = 10;
pub const MIN_LEN: usize = 10;
pub const MAX_LEN: usize = 12;
/// Generate a fresh shortid using thread-local RNG.
/// Generate a fresh shortid using the thread-local RNG.
#[must_use]
pub fn generate() -> String {
generate_len(DEFAULT_LEN)
generate_with_rng(&mut rand::rng())
}
/// Generate a shortid from a caller-supplied RNG.
///
/// Lets `seed --seed <n>` produce **reproducible** shortid values
/// (ADR-0048 D4) by threading its seeded RNG through, while the default
/// [`generate`] keeps its thread-RNG behaviour for ordinary inserts.
#[must_use]
fn generate_len(len: usize) -> String {
let mut rng = rand::rng();
let mut out = String::with_capacity(len);
for _ in 0..len {
pub fn generate_with_rng<R: RngExt + ?Sized>(rng: &mut R) -> String {
let mut out = String::with_capacity(DEFAULT_LEN);
for _ in 0..DEFAULT_LEN {
let idx = rng.random_range(0..ALPHABET.len());
out.push(ALPHABET[idx] as char);
}