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>
36 lines
1.3 KiB
TOML
36 lines
1.3 KiB
TOML
[package]
|
|
name = "blocker-tests"
|
|
version = "0.1.0"
|
|
edition = "2021"
|
|
description = "Cross-platform scenario tests: golden decision tables that are the parity contract for every platform layer."
|
|
license = "UNLICENSED"
|
|
|
|
[dependencies]
|
|
blocker-core = { path = "../core" }
|
|
|
|
# NO serde HERE — YET.
|
|
#
|
|
# The golden tables are designed as JSON (see src/golden.rs), and JSON needs
|
|
# serde/serde_json. Those crates are deliberately NOT added in M0, for two
|
|
# reasons:
|
|
#
|
|
# 1. CLAUDE.md section 0 rule 3 requires stopping to ask before adding any
|
|
# dependency not listed in section 3. serde is not on that list. It is
|
|
# transitively required by rusqlite and uniffi (both listed), which is why
|
|
# the design assumed it would be present — but "will be present later" is
|
|
# not "approved to add now".
|
|
#
|
|
# 2. The development machine has 9.7 GB free. Fetching the registry is the
|
|
# step that actually consumes disk, and the dependency-purity test needs a
|
|
# Cargo.lock to exist at all.
|
|
#
|
|
# The format is specified in src/golden.rs as the contract M1 implements against.
|
|
# The parse layer arrives in M1 alongside rusqlite, in one dependency change
|
|
# with an ADR entry. See docs/DECISIONS.md ADR-3.
|
|
|
|
[lints]
|
|
workspace = true
|
|
|
|
[lib]
|
|
name = "blocker_tests"
|
|
path = "src/lib.rs" |