* RecoveryCmd::List no longer takes a user — lists all active codes
across users, matching the daemon's ListRecoveryCodes signature.
* RecoveryCmd::Revoke <user> calls RevokeRecoveryCode; exits 1 with
a stderr message when the user has no active code.
* bus.rs gains list_recovery_codes() / revoke_recovery_code(user).
* Two new clap-parser tests cover the new subcommand shapes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lands Lane 1 of the Phase 4+5+8+15+16 parallel cycle. Phase 4 + Phase 5 must
land together because both modify the SetPolicy code path.
- daemon/src/lockout.rs — pure simulate(new_policy, registry) -> Vec<Violation>.
Iterates Required stacks, flags users with no enrolled credential of any
required method. 5 unit tests cover: optional-mode skipped, required-with-
unenrolled flagged, any-method-satisfies, empty registry, multi-stack.
- daemon/src/policy_apply.rs — PolicyApplier renders the pam-configs profile
(Default: yes when any stack requires fido2; pam_u2f.so + pam_authforge_pending
when fido2 required, only pam_authforge_pending otherwise) and runs
pam-auth-update --package. Stash-and-restore on failure: prior profile
contents are restored and pam-auth-update re-run, so a failed apply leaves
the system in its previous PAM state. 4 unit tests including a real-process
rollback test against a failing /bin/sh shim.
- daemon/src/state.rs — AppState::set_policy(p, force) returns
PolicyApplyResult. Always runs the simulator first; if violations and !force,
returns { applied: false, violations } without writing. Otherwise persists
via PolicyStore::save and invokes PolicyApplier::apply. StorageConfig grows
pam_profile_path + pam_auth_update fields (env-var driven, tests inject a
no-op /bin/sh shim into a tempdir).
- daemon/src/dbus.rs — SetPolicy signature is now (Policy, bool) -> Result.
Wire-breaking pre-alpha; CLI updated in this commit.
- cli/src/{bus,commands}.rs — set_policy takes force flag. policy set runs
with force=false and surfaces violations as a non-zero exit + stderr list
pointing the user at policy apply --force-i-know-what-im-doing. policy
apply now actually invokes pam-auth-update via the daemon.
Test count: 42 daemon (was 33; adds 5 lockout + 4 policy_apply). 13 common.
5 cli. cargo clippy --workspace --all-targets -D warnings clean.
Plan deviation: PolicyApplier::from_env() became PolicyApplier::new(profile_path,
pam_auth_update) with the env defaults moved into StorageConfig::from_env_or_defaults.
Cleaner: state owns one source of truth for env-driven path config.
Lands plan tasks C2 (Daemon proxy wrapping all 9 D-Bus methods via
Proxy::new_owned), C3 (status/list/policy-show), C4 (enroll/remove/policy-
set/apply/validate), and C5 (pending/recovery). Bundled because dispatch
needs the bus wrapper to compile cleanly under -D warnings.
Phase 4/5/12 placeholders in the dispatcher: policy apply re-saves to trigger
PolicyChanged; policy validate is a TOML round-trip; pending list and
recovery list are no-op messages until the corresponding D-Bus methods land.
5 clap-parser tests pass (was 5; same — parser shape unchanged in this
commit). Workspace clippy clean.