585fd30440
Establishes the cargo workspace and the shared `blocker-core` crate. The engine is deliberately written before any platform code, because CLAUDE.md section 2 makes it the single source of decision truth and M1 acceptance requires it to be pure. **Workspace** — five crates plus a test crate, zero external dependencies. Section 3 requires every crate to justify its binary-size cost, so M0 adds none. [profile.release] is workspace-only (cargo rejects it in members) and carries the section 5 settings that make the 5 MB stripped budget reachable. **Purity contract** — `evaluate(&EngineInput) -> Decision` reads no clock, no disk and no OS API; everything arrives in the input. `tests/purity.rs` enforces this mechanically by asserting blocker-core has zero dependencies, so a stray I/O reach-in fails the build rather than silently making the golden tables useless as a parity contract. Decisions worth calling out: - **Monotonic time is a separate type from wall time.** Section 1.5.3 requires a system date change never to shorten a lock. Keeping them apart makes that structural rather than a rule to remember. - **`start > end` means overnight.** No `crosses_midnight` flag: the comparison already encodes it and a second field is a second way to be wrong. The weekday for a window that started yesterday evening is yesterday's, which `overnight_window_survives_past_midnight` pins down. - **`next_wakeup` is armed over every non-paused schedule, not only active ones.** Filtering to active conditions first would let the daemon sleep straight through the moment a schedule is due to start. - **Allowlist needs the installed-app inventory.** `EngineInput` carries `installed_apps`; an empty slice fails open rather than guessing. - **Wall-clock correction extends, never shortens, and clamps to the 24h cap.** Extending unbounded would satisfy anti-tamper and violate section 1.5.4. - **`cast_possible_truncation` is denied workspace-wide.** Truncating a millisecond counter would corrupt a lock timer. Verified: 22 tests pass, `cargo fmt --check` clean, `cargo clippy -D warnings` clean, stripped release daemon 0.279 MB against the 5 MB budget. Co-Authored-By: Claude Code <noreply@anthropic.com>
66 lines
2.5 KiB
Rust
66 lines
2.5 KiB
Rust
//! The never-block list (CLAUDE.md section 1.5.4).
|
|
//!
|
|
//! These targets are hardcoded, non-negotiable, and cannot be removed by any
|
|
//! user setting, by any schedule, or by any lock level. Blocking the dialler or
|
|
//! emergency services would make the device unusable, and section 1.5.4 places
|
|
//! these guardrails above every other feature in the project.
|
|
//!
|
|
//! The list is a `const`, not a setting, and [`is_never_blocked`] is consulted
|
|
//! inside the engine's target loop *after* schedule evaluation. That ordering is
|
|
//! deliberate: a schedule that names a protected target still evaluates and
|
|
//! still blocks its other targets, but the protected one can never enter the
|
|
//! blocked set. There is no flag that reverses it.
|
|
|
|
/// Android: the dialler and the emergency path.
|
|
///
|
|
/// Section 1.5.4 names phone/dialer, emergency calls and SOS, and emergency
|
|
/// alerts. Matching is by substring so a variant package name cannot slip past.
|
|
#[cfg(target_os = "android")]
|
|
pub const NEVER_BLOCK: &[&str] = &[
|
|
"com.android.dialer",
|
|
"com.android.server.telecom",
|
|
"com.android.emergency",
|
|
"com.android.phone",
|
|
"com.android.server.emergency",
|
|
"com.android.cellbroadcastreceiver",
|
|
"com.android.systemui", // holds the emergency call UI
|
|
];
|
|
|
|
/// Desktop: the OS shell and the settings basics needed to keep the machine
|
|
/// usable. A user who cannot reach their file manager or display settings has
|
|
/// lost the machine, not been disciplined.
|
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
|
pub const NEVER_BLOCK: &[&str] = &[
|
|
// Session shell / compositor.
|
|
"plasmashell",
|
|
"plasmawindowmanager",
|
|
"kwin_wayland",
|
|
"kwin_x11",
|
|
"gnome-shell",
|
|
"xfce4-session",
|
|
"explorer.exe",
|
|
// Settings and the ability to undo things.
|
|
"systemsettings",
|
|
"systemsettings5",
|
|
"systemsettings6",
|
|
"gnome-control-center",
|
|
"ksysguard",
|
|
"plasma-systemmonitor",
|
|
"dolphin",
|
|
"org.kde.plasma.systemsettings",
|
|
// Terminal is deliberately NOT here: it can undo anything. Section 1.5.4
|
|
// names the shell and Settings basics, not the terminal.
|
|
];
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "linux", target_os = "windows")))]
|
|
pub const NEVER_BLOCK: &[&str] = &[];
|
|
|
|
/// True when `target` may never be blocked.
|
|
///
|
|
/// Substring matching, case-insensitive, so `com.android.dialer` and
|
|
/// `com.android.dialer.standalone` are both covered.
|
|
pub fn is_never_blocked(target: &str) -> bool {
|
|
let needle = target.to_lowercase();
|
|
NEVER_BLOCK.iter().any(|protected| needle.contains(&protected.to_lowercase()))
|
|
}
|