keld is often invoked from systemd timers, cron jobs, CI pipelines, and
shell scripts with no controlling terminal. Capture the intended
behaviour as a Gherkin feature ahead of implementation.
The spec covers four rules: without a terminal keld never launches the
TUI, non-interactive mode requires explicit inputs (with specific errors
when preset selection, backup paths, or the subcommand itself is
missing), interactive mode is preserved when a terminal is attached, and
KELD_NONINTERACTIVE overrides terminal detection as a fallback for users
who want scripted behaviour on a terminal.
Pure spec, no automation — matches the other files under features/.
@@ -0,0 +1,86 @@
+Feature: Non-interactive execution
+
+ keld is often invoked from systemd timers, cron jobs, CI pipelines,
+ and shell scripts where no controlling terminal is attached. In those
+ contexts it must run restic directly without attempting to launch the
+ interactive TUI. When a user wants scripted behaviour even on a
+ terminal, an environment variable provides an escape hatch.
+
+ Rule: Without a terminal keld never launches the interactive TUI
+
+ Scenario: Wrapped subcommand runs restic when the preset supplies everything
+ Given keld is invoked with a wrapped subcommand and no positional arguments
+ And standard input is not a terminal
+ And the selected preset supplies every value the command needs
+ When keld runs
+ Then restic is executed with the resolved configuration
+ And no interactive TUI is launched
+
+ Scenario: Wrapped subcommand runs restic when positional arguments cover missing values
+ Given keld is invoked with a wrapped subcommand and positional arguments
+ And standard input is not a terminal
+ When keld runs
+ Then restic is executed with the resolved configuration
+ And no interactive TUI is launched
+
+ Scenario: --show-command prints the resolved command without launching the TUI
+ Given keld is invoked non-interactively with --show-command
+ When keld runs
+ Then the resolved restic command is printed
+ And keld exits successfully without executing restic
+ And no interactive TUI is launched
+
+ Rule: Non-interactive mode requires explicit inputs
+
+ Scenario: Multiple presets are configured and none is specified
+ Given the configuration defines more than one preset
+ And keld is invoked non-interactively without --preset
+ When keld runs
+ Then keld fails with an error naming the --preset flag
+ And the error lists the available presets
+ And no interactive TUI is launched
+
+ Scenario: Backup invoked with no paths available
+ Given keld is invoked non-interactively with the backup subcommand
+ And no paths are provided on the command line
+ And the resolved preset defines no backup paths
+ When keld runs
+ Then keld fails with an error naming the missing paths
+ And no interactive TUI is launched
+
+ Scenario: Bare keld with no subcommand
+ Given keld is invoked non-interactively with no subcommand
+ When keld runs
+ Then keld fails with an error naming the missing subcommand
+ And no interactive TUI is launched
+
+ Rule: Interactive mode is preserved when a terminal is attached
+
+ Scenario: Wrapped subcommand with no arguments opens the session
+ Given keld is invoked with a wrapped subcommand and no positional arguments
+ And standard input is a terminal
+ When keld runs
+ Then the interactive session is launched with the command pre-selected
+
+ Scenario: Bare keld opens the command menu
+ Given keld is invoked with no subcommand
+ And standard input is a terminal
+ When keld runs
+ Then the interactive session is launched starting at the command menu
+
+ Rule: KELD_NONINTERACTIVE overrides terminal detection
+
+ Scenario: Environment variable forces non-interactive behaviour on a terminal
+ Given keld is invoked with a wrapped subcommand and no positional arguments
+ And standard input is a terminal
+ And KELD_NONINTERACTIVE is set to a non-empty value
+ When keld runs
+ Then restic is executed with the resolved configuration
+ And no interactive TUI is launched
+
+ Scenario: Empty KELD_NONINTERACTIVE is treated as unset
+ Given keld is invoked with a wrapped subcommand and no positional arguments
+ And standard input is a terminal
+ And KELD_NONINTERACTIVE is set to an empty value
+ When keld runs
+ Then the interactive session is launched with the command pre-selected