Skip to content

Scaffolder setting verb

Status: IMPLEMENTED — Open Questions resolved per their recommendations (Q1–Q3 yes; Q4 yes = marker renamed to settings; Q5 yes = breaking rename, lands a minor under release-plz). generate config-fieldgenerate setting; new remove setting; marker // rtb:config-fields-*// rtb:settings-*; help/long_about reworded; new docs/concepts/flags-vs-settings.md with bidirectional cross-refs. 211/211 rtb-cli-bin tests pass (7 generate + 5 remove setting scenarios + inverse unit tests); fmt + clippy + rustdoc gate clean.

1. Motivation

Two threads, one change:

  1. Naming. config-field is a code term (it is literally a struct field on AppConfig). Users don't think "fields", they think "settings I configure". Rename the user-facing verb to setting, which also sets up a clean mental contrast with flag:

flag = a CLI argument you type per-invocation (transient). setting = a value your tool reads from its config file / env (persistent).

  1. Completeness. The scaffolder has generate config-field but no inverse, leaving the "I can generate but not ungenerate" trap that remove flag fixed for flags. Add the inverse as part of the rename.

This keeps RTB's deliberate split between clap flags and typed AppConfig config (the why-rtb.md paradigm swap away from viper's dynamic bag) — gtb fuses the two under "persistent flag"; RTB does not. remove flag already gives gtb parity; setting is RTB-native, so it gets a name chosen for RTB's users, not gtb parity.

2. Resolved decisions

  • D1 — Keep flag and setting as distinct verbs. A clap Arg and a typed AppConfig field are different things in RTB (different files, different input channels). Confirmed.
  • D2 — Rename the verb config-fieldsetting. generate config-field becomes generate setting; the new inverse is remove setting. config-value was the runner-up; bare config rejected (collides with the conventional runtime config command and reads as "the whole config").
  • D3 — Telegraph intent via help + docs (see §5).

3. Surface

rtb generate setting <name> [--type T] [--default V]   # was: generate config-field
rtb remove   setting <name> [--force] [--dry-run]       # new
  • generate setting — identical behaviour to today's generate config-field (insert pub <name>: <type>, into the config marker region of src/main.rs; --default stays documentation-only); only the verb name and help text change.
  • remove setting — the inverse: strip that line (and its preceding // default: comment, if any) from the marker region. Structurally identical to remove flag (remove/flag.rs): marker-region line removal, manifest re-hash, --force past a protected: src/main.rs, --dry-run. No AST surgery, no default.yamlgenerate config-field as shipped only edits src/main.rs, so the inverse does too.

Registration mirrors the existing kinds in generate/mod.rs and remove/mod.rs (new RemoveSub::Setting variant + dispatch arm).

4. Behaviour — remove setting

  1. Validate <name> (validate::validate_flag_name, as generate uses).
  2. Read .rtb/manifest.yaml; src/main.rs is always tracked.
  3. protected on src/main.rs without --force → refuse with a --force hint (mirrors remove flag).
  4. Locate pub <name>: inside the config marker region of src/main.rs. Absent → error settingnot found.
  5. Remove the field line; remove the immediately-preceding // default: comment line if present (Q-1).
  6. --dry-run → print, no write.
  7. Write src/main.rs; re-hash into manifest.generated; write manifest.

Errors are miette::Diagnostic throughout, mirroring remove flag.

5. Documentation / telegraphing (D3)

  • generate setting about: replace today's jargon ("Append a typed field to AppConfig") with: "Add a persistent setting to your tool's config (read from config file + env var; not a CLI flag)."
  • long_about: "Settings come from your layered config: defaults → config file → env (MYTOOL_<NAME>) → CLI override. For a per-command CLI argument instead, use generate flag."
  • remove setting about: "Remove a setting from your tool's config (inverse of generate setting)."
  • Bidirectional cross-refs: flag's help points to setting and vice-versa, so whichever a user reaches for, the help nudges them right.
  • New docs/concepts/flags-vs-settings.md: the transient-vs-persistent table + when-to-use, cross-linked from both command docs and docs/components/rtb-cli-bin.md.

6. Rename surface (implementation checklist)

  • crates/rtb-cli-bin/src/commands/generate/config_field.rs → rename module to setting.rs; verb name, about, long_about.
  • generate/mod.rs, remove/mod.rs — module decls + subcommand variants + dispatch arms.
  • remove/setting.rs — new (the strip_field helper + run).
  • Templates cli|minimal/src/main.rs.j2 — doc comments referencing generate config-fieldgenerate setting. Marker name: see Q-4.
  • Tests: rename tests/generate_config_field.rstests/generate_setting.rs; extend tests/remove_verbs.rs with remove setting scenarios; update tests/regenerate.rs references.
  • Docs: docs/components/rtb-cli-bin.md, docs/components/index.md, new docs/concepts/flags-vs-settings.md.

7. Open questions (sign-off before coding)

  • [Q-1] // default: comment cleanup on remove — strip the preceding // default: line too. Recommend YES.
  • [Q-2] Protection at src/main.rs file level via --force (like remove flag). Recommend YES (no per-setting protection).
  • [Q-3] Match strictness — match on pub <name>: only, type- agnostic. Recommend YES.
  • [Q-4] Rename the marker too? The anchor is // rtb:config-fields-begin/end. Renaming it to settings makes generated code consistent with the verb, but breaks regenerate / generate setting on any already-scaffolded project still carrying the old marker. Recommend YES (rename to settings)config-field shipped only in v0.6 days ago with no known downstream adopters, so this is the cheapest moment to align the marker; note it in the changelog as a breaking scaffolder change. (Fallback: keep config-fields marker, accept the verb/marker mismatch.)
  • [Q-5] Release shape. Verb rename is a breaking scaffolder-CLI change; the new verb is a feat. Pre-1.0 (0.x) this is allowed with a changelog note. Net effect under release-plz: a minor bump (v0.6.0), turning the thin v0.5.5 (help-polish fix only) into a substantive cut. Confirm we want the next cut to be this minor + carry the breaking rename.

8. Out of scope

  • Reconciling live config values on removal — the verb edits only the generated struct, exactly as generate writes only the struct.
  • A real typed --default (#[serde(default = …)]) — orthogonal, tracked against generate setting's documentation-only --default.
  • Folding config into flags gtb-style — explicitly rejected (D1).