Repository navigation
The unattended flag implies nothing: --help and docs/cli.md both promise --yes, --max-risk high and --non-interactive #440
Description
Rechecked 2026-10-01 at
603ca81f. #436 (622cdea) fixed the two prose halves:
apps/sysknife-cli/src/cli.rs:63-69now says the flag "implies nothing else", and
docs/cli.md:351and:393-394say it switches on none of the three. What is left
is the comment atapps/sysknife-cli/src/approval.rs:252-253, which still reads
"The flag implies --yes at the CLI layer", and the test built through
build_run_optsdescribed under Tests first. Thecli.rsquote below is the text
as filed.
--help and docs/cli.md both say --dangerously-skip-approval implies three flags. The binary wires none of them.
apps/sysknife-cli/src/cli.rs:63-68:
/// Execute HIGH-risk steps with no human confirmation.
///
/// Requires `SYSKNIFE_I_ACCEPT_UNATTENDED_ROOT=1` in the environment as
/// well; the flag on its own refuses to run. Implies `--yes`,
/// `--max-risk high` and `--non-interactive` unless you set a lower
/// `--max-risk`, which still wins.
apps/sysknife-cli/src/main.rs:26 parses and never mutates the result. build_run_opts at :187 copies the flags across one for one:
yes: cli.yes,
max_risk: cli.max_risk.map(crate::approval::MaxRisk::from),
non_interactive: cli.non_interactive,
and ApprovalPolicy::effective_auto_ceiling at apps/sysknife-cli/src/approval.rs:133 leaves before the ceiling is computed:
if !self.yes {
return None;
}
No default_value_if on yes, max_risk or non_interactive, and no assignment to any of them anywhere in the binary.
The unit test at apps/sysknife-cli/src/approval.rs:251 assumes the wiring lives somewhere it does not:
fn the_override_is_inert_without_yes() {
// The flag implies --yes at the CLI layer. If that wiring is ever
// dropped, the policy must not auto-approve on the override alone.
Measured
Two throwaway tests appended to apps/sysknife-cli/src/main.rs in a scratch clone of a3d17ad, run in a container, then removed. git status --porcelain | wc -l was 0 afterwards.
The first parses ["sysknife", "--dangerously-skip-approval", "check disk usage"] and asserts cli.yes is false, cli.max_risk is None, cli.non_interactive is false. The second sets SYSKNIFE_I_ACCEPT_UNATTENDED_ROOT=1, builds the real RunOpts through build_run_opts, and asserts effective_auto_ceiling() is None and decide_step(&PlanRiskLevel::Low) is RequiresPrompt.
The podman line below carried an absolute path to a cargo target cache on my own
machine when I filed this. I have replaced it with $CARGO_TARGET_CACHE; point that
at any writable directory, or drop both -v .../ct and CARGO_TARGET_DIR and let
cargo build into the checkout. Nothing else about the run changed.
$ podman run --rm --network=none -v "$PWD:/repo:z" -v "$CARGO_TARGET_CACHE:/ct:z" -v "$HOME/.cargo:/cargo:O" -w /repo -e CARGO_HOME=/cargo -e CARGO_TARGET_DIR=/ct/rv436 -e CARGO_NET_OFFLINE=true -e HOME=/tmp docker.io/library/rust:1-slim cargo test -p sysknife-cli --bins --offline maintainer_probe_336
running 2 tests
test maintainer_probe_336::probe_parsed_flags ... ok
test maintainer_probe_336::probe_decision_with_both_keys ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 268 filtered out; finished in 0.00s
So SYSKNIFE_I_ACCEPT_UNATTENDED_ROOT=1 sysknife --dangerously-skip-approval "check disk usage" prompts for every step, LOW included. An operator following the Unattended mode section into a cron job gets a run that blocks on a prompt instead of one that executes, or gets nothing at all if stdin is closed.
Which side moves
The documentation, not the binary.
A flag that silently switches on three others is the opposite of the deliberateness the rest of this design insists on: the two-key rule exists so that neither a flag left in a script nor a variable left in a profile is enough on its own, and --yes typed explicitly is a third act of intent worth keeping. The current behaviour is also the safe one, which is why this survived unnoticed.
So --dangerously-skip-approval raises the --yes auto-approval ceiling from MEDIUM to HIGH and does nothing else, and every place that describes it says that.
Scope
Fixed in docs: align CLI unattended mode contract #436.apps/sysknife-cli/src/cli.rs:63-68, the doc comment that becomes--help.apps/sysknife-cli/src/approval.rs:251-253, the comment insidethe_override_is_inert_without_yes. The assertion is correct and describes the binary; only its comment claims otherwise. Rename it if a better name suggests itself, and keep the test.Fixed in docs: align CLI unattended mode contract #436.docs/cli.md, the Global flags row and the "What it turns off" bullet.- A test that pins the decision rather than the parse: with both keys and no
--yes,decide_stepon a LOW step isRequiresPrompt.the_override_is_inert_without_yesalready proves this at the policy layer; what is missing is the same assertion built from parsed CLI flags throughbuild_run_opts, so a futuredefault_value_ifcannot change the contract without turning a test red.
Both prose figures and the test baseline move with any test you add: UPDATE_TEST_BASELINE=1 scripts/test_baseline.sh writes tests/evidence/workspace-tests.json, and the {count} Rust tests literal in README.md, docs/introduction.md and docs/distro-support.md moves in the same commit.
Found while reviewing #436 against #336.
Tests first
Break what the guard protects. Build RunOpts through build_run_opts from
parsed CLI flags with --dangerously-skip-approval and no --yes, then assert
decide_step(&PlanRiskLevel::Low) is RequiresPrompt. That pins the contract at
the layer the documentation describes, which is the layer nothing currently
tests. the_override_is_inert_without_yes proves the same thing one level down,
at the policy, so a future default_value_if on yes could change the user-
visible behaviour with every test still green.
Break the guard's own input. Assert the parse selected the flag. A test that
builds RunOpts from a Cli whose flag never parsed passes for the wrong
reason, and renaming the long flag would not turn it red. Assert
cli.dangerously_skip_approval is true in the same test that asserts the
decision, so the two cannot drift apart.
Fix the comment inside the_override_is_inert_without_yes while you are there.
The assertion is right; only its comment claims wiring that does not exist.
Difficulty
easy. One code comment and one new test. No VM, no daemon, no
provider credentials. The reading is the work: confirm for yourself that the
binary wires none of the three before you rewrite the sentence that says it does.
Getting started
CONTRIBUTING.md
has the build and test commands. cargo test -p sysknife-cli --bins is the
suite for this one.
Both prose figures and the test baseline move with any test you add:
UPDATE_TEST_BASELINE=1 scripts/test_baseline.sh writes
tests/evidence/workspace-tests.json, and the {count} Rust tests literal in
README.md, docs/introduction.md and docs/distro-support.md moves in the
same commit. It is 1,936 today.
No CLA and no copyright waiver. The project is MIT.
Activity
[-]probe[/-][+]The unattended flag implies nothing: --help and docs/cli.md both promise --yes, --max-risk high and --non-interactive[/+]atanishka308 commentedon Sep 16, 2026
I would like to work on this issue. So, kindly assign it to me.
vladimirrott commentedon Sep 16, 2026
@atanishka308 #440 is yours. I have assigned you and applied
claimed, so it shows on your dashboard and other contributors will stay off it.It is a good match for what you asked for. The binary and its own documentation disagree about one flag:
--helpanddocs/cli.mdboth say--dangerously-skip-approvalimplies--yes,--max-risk highand--non-interactive, andapps/sysknife-cli/src/main.rswires none of the three. The decision in the body is that the documentation moves and the binary stays, so most of this is editing two prose blocks and one stale comment to match the code that is already there.Two things worth knowing before you start:
docs/cli.mdhas an open pull request against it, #436 by @armutlutost, covering the same two rows. Leave that file to them and say so in your PR description; the Rust side and the test are the part nobody else is touching.The test in the scope list is the piece with real teeth.
the_override_is_inert_without_yesalready proves the policy layer refuses to auto-approve, and what is missing is the same assertion built from parsed CLI flags throughbuild_run_opts, so that a futuredefault_value_ifonyescannot change the contract while every test stays green. Adding one movestests/evidence/workspace-tests.jsonand the{count} Rust testsliteral inREADME.md,docs/introduction.mdanddocs/distro-support.md;UPDATE_TEST_BASELINE=1 scripts/test_baseline.shwrites the first and you edit the other three in the same commit.No deadline. Ask here if any of it reads ambiguously and I will answer on the thread rather than in a review three days later.
vladimirrott commentedon Sep 16, 2026
Edited the body. The
podmanline quoted an absolute path to a cargo target cache on my own machine, which is no use to you and had no business in a public issue. It reads$CARGO_TARGET_CACHEnow: point that at any writable directory, or drop it along with-e CARGO_TARGET_DIRand let cargo build into the checkout. The run is otherwise the one I described.While I was in there I re-checked every reference in the body against
b2c823e6, because v0.16.0 landed after I filed this and I did not want you working from line numbers that had moved. All four still land:So the binary still wires none of the three, and the documentation is still the side that moves.
vladimirrott commentedon Sep 16, 2026
@atanishka308 added
Tests first,DifficultyandGetting startedto the body, on top of the edit I described above. No claim changed and nothing about the work moved.The part worth reading is the second direction under
Tests first. A test that buildsRunOptsfrom aCliwhose flag never parsed passes for the wrong reason and would survive a rename of the long flag, so assertcli.dangerously_skip_approvalis true in the same test that assertsdecide_stepreturnsRequiresPrompt.The workspace baseline is 1,861 today, and it moves with any test you add:
vladimirrott commentedon Sep 23, 2026
@atanishka308 a week on from taking #440, I don't see a pull request for it yet, and I can see why: #445 got a new commit today and #486 is open too. Finishing those first is the right order.
The claim stays yours if you still want it, with no deadline. If you'd rather let it go, one line here is enough and I'll put it back in the pool.
vladimirrott commentedon Sep 23, 2026
One number in
Getting startedhas moved: the workspace baseline intests/evidence/workspace-tests.jsonand the three prose files is 1,873 now, not 1,861, and the body says so.apps/sysknife-cli/src/cli.rs:63-68and the rest of the issue are unchanged at0ad26871.vladimirrott commentedon Sep 28, 2026
The workspace baseline is 1,879 now, after #445 and #508 added tests, and I've changed the one sentence in
Getting startedthat names it. The rest of the issue is unchanged at9d862835.vladimirrott commentedon Oct 1, 2026
@atanishka308 it's been two weeks since you took #440 and I haven't seen a branch, so I'm freeing it up for someone else. Thank you for picking it up. If you had work in progress, open a draft PR and I'll hand it back to you.
vladimirrott commentedon Oct 1, 2026
@armutlutost this is the other half of the unattended-mode contract you fixed in #436 and that #561 picks up:
--helpanddocs/cli.mdpromise things--yesand--max-riskdon't do. The two fit in one PR if you like. I'm holding it for you.vladimirrott commentedon Oct 1, 2026
@armutlutost, a correction to my note above. Your #436 merged an hour before I wrote it and already fixed the
--helptext anddocs/cli.md, so the half I described as open is done, and done by you.What remains here is smaller: the comment at
apps/sysknife-cli/src/approval.rs:252-253, which still says the flag implies--yes, and one test that buildsRunOptsthroughbuild_run_optsfrom parsed flags. I've updated the body to match. The offer stands on those terms, with no pressure either way.atanishka308 commentedon Oct 2, 2026
Hi, sorry for not being able to work on this issue over the past two weeks. I was occupied with other commitments and couldn’t give it the attention it needed.
I’m available to work on it now and will make sure to actively work on it. If possible, could you please assign the issue to me again? Thank you for understanding!
vladimirrott commentedon Oct 3, 2026
@atanishka308, thanks for coming back to it, and no apology needed.
You should know where it stands. After I freed it on 1 October I offered it to @armutlutost, whose #436 had already fixed the
--helpanddocs/cli.mdhalf. They haven't answered, so I'll keep that offer open until Monday 5 October. If they pass or stay quiet until then, it's yours and I'll assign you.The work left is smaller than when you took it: the comment at
apps/sysknife-cli/src/approval.rs:252-253, which still says the flag implies--yes, and one test that buildsRunOptsthroughbuild_run_opts(apps/sysknife-cli/src/main.rs:187) from parsed flags. The body has the rest.vladimirrott commentedon Oct 5, 2026
@atanishka308, it's Monday and the other offer went unanswered, so #440 is yours. I've assigned you.
The scope is the one from my last note: correct the comment at
apps/sysknife-cli/src/approval.rs:252-253, which still says the flag implies--yes, and add a test that buildsRunOptsthroughbuild_run_opts(apps/sysknife-cli/src/main.rs:187) from parsed flags. I checked both lines again on today'smain(054320c) and they haven't moved.Your fork from 2 October is a fine place to start. Open a draft PR whenever you have something, even half done, and I'll look at it.