Line references re-measured 2026-09-05 at 7d19864. command_mechanism("sudo", ["ufw", "status", "verbose"]) sits at line 127 now, not 124.
The defect is unchanged.
UfwDeleteRule is indexed by a rule number, and no action in the catalogue can produce one.
The parameter's own doc says where the value comes from (crates/sysknife-daemon/src/actions/ufw.rs:137):
/// `rule_number` is the 1-based integer shown by `ufw status numbered`.
The catalogue description repeats it to the planner, as "positive integer from 'ufw status numbered'". The only ufw read action runs the other rendering (crates/sysknife-daemon/src/actions/ufw.rs:127):
mechanism: command_mechanism("sudo", ["ufw", "status", "verbose"]),
grep -rn numbered crates/sysknife-daemon/src/actions/ufw.rs returns the doc comment and nothing else. status verbose prints no rule numbers; ufw renders them only for status numbered.
Why it matters
The documented workflow is a dead end. A planner asked to delete a firewall rule has no way to obtain the index, so it either guesses an index into a High-risk firewall deletion or tells the operator to leave SysKnife and run ufw by hand. Guessing is the worse branch: rule numbers shift as rules are added and removed, so a stale index deletes a different rule than the one the operator approved.
Scope
Either shape works:
- Add an optional
numbered: bool to UfwStatus, so one action serves both renderings.
- Or add a
UfwStatusNumbered read action.
The existing /usr/sbin/ufw grant in packaging/sysknife-sudoers already covers the argv, so no new privilege is needed. Whichever you pick, the new capability needs its KNOWN_ACTIONS entry and its risk level, and docs/action-reference.md regenerates.
A story would be worth adding in the same PR, since UfwDeleteRule currently has none. Correction, 2026-09-07 at adab560: it has one, and has since #206. tests/e2e/stories/story-125.sh drives the intent "delete ufw rule number 3". Read it before you change the action: it is the thing your change has to keep passing, and it was already there when this issue said otherwise.
$ ls tests/e2e/stories/story-125.sh && sed -n '1,7p' tests/e2e/stories/story-125.sh
tests/e2e/stories/story-125.sh
#!/usr/bin/env bash
# Story 125 (ubuntu, high-risk): Delete a numbered ufw rule
# Intent: "delete ufw rule number 3"
# Distro: ubuntu
set -euo pipefail
INTENT="delete ufw rule number 3"
echo "=== Story 125 (ubuntu): UfwDeleteRule ==="
$ git log --oneline -- tests/e2e/stories/story-125.sh
476e68d feat(e2e): give every Debian-only action a story, and make a retried run replay (#206)
Tests first
Assert the argv for the numbered form, then assert that the catalogue description no longer points the planner at a command SysKnife cannot run.
Difficulty
easy. One action or one parameter, and the sudoers grant already exists.
Getting started
CONTRIBUTING.md has the build and test commands. No CLA and no copyright waiver. The project is MIT.
UfwDeleteRuleis indexed by a rule number, and no action in the catalogue can produce one.The parameter's own doc says where the value comes from (
crates/sysknife-daemon/src/actions/ufw.rs:137):/// `rule_number` is the 1-based integer shown by `ufw status numbered`.The catalogue description repeats it to the planner, as "positive integer from 'ufw status numbered'". The only ufw read action runs the other rendering (
crates/sysknife-daemon/src/actions/ufw.rs:127):grep -rn numbered crates/sysknife-daemon/src/actions/ufw.rsreturns the doc comment and nothing else.status verboseprints no rule numbers; ufw renders them only forstatus numbered.Why it matters
The documented workflow is a dead end. A planner asked to delete a firewall rule has no way to obtain the index, so it either guesses an index into a High-risk firewall deletion or tells the operator to leave SysKnife and run ufw by hand. Guessing is the worse branch: rule numbers shift as rules are added and removed, so a stale index deletes a different rule than the one the operator approved.
Scope
Either shape works:
numbered: booltoUfwStatus, so one action serves both renderings.UfwStatusNumberedread action.The existing
/usr/sbin/ufwgrant inpackaging/sysknife-sudoersalready covers the argv, so no new privilege is needed. Whichever you pick, the new capability needs itsKNOWN_ACTIONSentry and its risk level, anddocs/action-reference.mdregenerates.A story would be worth adding in the same PR, sinceCorrection, 2026-09-07 atUfwDeleteRulecurrently has none.adab560: it has one, and has since #206.tests/e2e/stories/story-125.shdrives the intent "delete ufw rule number 3". Read it before you change the action: it is the thing your change has to keep passing, and it was already there when this issue said otherwise.Tests first
Assert the argv for the numbered form, then assert that the catalogue description no longer points the planner at a command SysKnife cannot run.
Difficulty
easy. One action or one parameter, and the sudoers grant already exists.Getting started
CONTRIBUTING.md has the build and test commands. No CLA and no copyright waiver. The project is MIT.