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-field → generate 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:
- Naming.
config-fieldis a code term (it is literally a struct field onAppConfig). Users don't think "fields", they think "settings I configure". Rename the user-facing verb tosetting, which also sets up a clean mental contrast withflag:
flag= a CLI argument you type per-invocation (transient).setting= a value your tool reads from its config file / env (persistent).
- Completeness. The scaffolder has
generate config-fieldbut no inverse, leaving the "I can generate but not ungenerate" trap thatremove flagfixed 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
flagandsettingas distinct verbs. A clapArgand a typedAppConfigfield are different things in RTB (different files, different input channels). Confirmed. - D2 — Rename the verb
config-field→setting.generate config-fieldbecomesgenerate setting; the new inverse isremove setting.config-valuewas the runner-up; bareconfigrejected (collides with the conventional runtimeconfigcommand 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'sgenerate config-field(insertpub <name>: <type>,into the config marker region ofsrc/main.rs;--defaultstays 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 toremove flag(remove/flag.rs): marker-region line removal, manifest re-hash,--forcepast aprotected:src/main.rs,--dry-run. No AST surgery, nodefault.yaml—generate config-fieldas shipped only editssrc/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¶
- Validate
<name>(validate::validate_flag_name, as generate uses). - Read
.rtb/manifest.yaml;src/main.rsis always tracked. protectedonsrc/main.rswithout--force→ refuse with a--forcehint (mirrorsremove flag).- Locate
pub <name>:inside the config marker region ofsrc/main.rs. Absent → errorsettingnot found. - Remove the field line; remove the immediately-preceding
// default:comment line if present (Q-1). --dry-run→ print, no write.- Write
src/main.rs; re-hash intomanifest.generated; write manifest.
Errors are miette::Diagnostic throughout, mirroring remove flag.
5. Documentation / telegraphing (D3)¶
generate settingabout: 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, usegenerate flag."remove settingabout: "Remove a setting from your tool's config (inverse ofgenerate setting)."- Bidirectional cross-refs:
flag's help points tosettingand 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 anddocs/components/rtb-cli-bin.md.
6. Rename surface (implementation checklist)¶
crates/rtb-cli-bin/src/commands/generate/config_field.rs→ rename module tosetting.rs; verbname,about,long_about.generate/mod.rs,remove/mod.rs— module decls + subcommand variants + dispatch arms.remove/setting.rs— new (thestrip_fieldhelper +run).- Templates
cli|minimal/src/main.rs.j2— doc comments referencinggenerate config-field→generate setting. Marker name: see Q-4. - Tests: rename
tests/generate_config_field.rs→tests/generate_setting.rs; extendtests/remove_verbs.rswithremove settingscenarios; updatetests/regenerate.rsreferences. - Docs:
docs/components/rtb-cli-bin.md,docs/components/index.md, newdocs/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.rsfile level via--force(likeremove 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 tosettingsmakes generated code consistent with the verb, but breaksregenerate/generate settingon any already-scaffolded project still carrying the old marker. Recommend YES (rename tosettings) —config-fieldshipped 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: keepconfig-fieldsmarker, 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-polishfixonly) 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 againstgenerate setting's documentation-only--default. - Folding config into flags gtb-style — explicitly rejected (D1).