c723efb186
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>
38 lines
1.6 KiB
Rust
38 lines
1.6 KiB
Rust
//! Cross-platform scenario tests (CLAUDE.md section 7).
|
|
//!
|
|
//! This crate holds the **golden decision tables**: declarative schedules,
|
|
//! inputs, and expected decisions that are run against the core engine.
|
|
//!
|
|
//! # Why this is a contract, not a test suite
|
|
//!
|
|
//! Section 7 requires that the same table runs against the core and serves as
|
|
//! the parity contract for all platforms. Android, Linux and Windows each
|
|
//! implement a mapping from a platform app identity to a `Target`, but they all
|
|
//! hand the same [`blocker_core`] engine the same schedule set and get back the
|
|
//! same decision. A platform crate runs this table against its own mapping, so
|
|
//! if Linux disagrees with Android the build fails rather than the user filing
|
|
//! a bug.
|
|
//!
|
|
//! # The rule that keeps it honest
|
|
//!
|
|
//! **A case is added before the engine changes.** If a table were written after
|
|
//! the code, it would only record current behaviour and could never catch a
|
|
//! regression — it would be a transcript, not a specification. Reviewing a PR
|
|
//! that changes engine behaviour therefore means checking that a case was added
|
|
//! or updated in the same commit.
|
|
//!
|
|
//! # Layout
|
|
//!
|
|
//! - `golden/` — the tables themselves, one JSON file per concern
|
|
//! - [`purity`] — a mechanical check that the core has not gained I/O
|
|
//!
|
|
//! The mandatory case list is in [`golden::MANDATORY_COVERAGE`].
|
|
//!
|
|
//! [`purity`] is `cfg(test)` because its checks are `#[test]` functions in this
|
|
//! library's unit-test build, not library API. They run under `cargo test`.
|
|
|
|
pub mod golden;
|
|
|
|
#[cfg(test)]
|
|
mod purity;
|