feat(core): add workspace skeleton and the pure decision engine
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>
This commit is contained in:
+17
-6
@@ -1,12 +1,23 @@
|
||||
edition = "2021"
|
||||
max_width = 100
|
||||
|
||||
# Imports are grouped std / external / crate, and sorted within each group.
|
||||
imports_granularity = "Crate"
|
||||
group_imports = "StdExternalCrate"
|
||||
# NOTE ON UNSTABLE OPTIONS
|
||||
#
|
||||
# `imports_granularity`, `group_imports` and `derive_debug` are nightly-only
|
||||
# rustfmt features. rust-toolchain.toml pins stable, so listing them here emits
|
||||
# a warning per crate per file and buys nothing. They are intentionally absent
|
||||
# rather than commented out: an unrecognised key is a warning, a commented key
|
||||
# is a trap for whoever re-enables nightly.
|
||||
#
|
||||
# If this project ever moves to nightly rustfmt, add:
|
||||
# imports_granularity = "Crate"
|
||||
# group_imports = "StdExternalCrate"
|
||||
# derive_debug = false
|
||||
# as a separate commit, so the diff is auditable.
|
||||
|
||||
# We write a lot of match arms over enums and error plumbing. Keep them tight.
|
||||
# Trailing comma on match arms adds vertical noise in the evaluator, which is
|
||||
# almost entirely match expressions.
|
||||
match_block_trailing_comma = false
|
||||
|
||||
# Derive Debug on virtually everything — engine output is logged in the field.
|
||||
derive_debug = false
|
||||
# Use small heuristics instead of the unstable ones.
|
||||
use_small_heuristics = "Max"
|
||||
Reference in New Issue
Block a user