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>
65 lines
2.5 KiB
TOML
65 lines
2.5 KiB
TOML
[workspace]
|
|
resolver = "2"
|
|
members = [
|
|
"core",
|
|
"tests",
|
|
"desktop/daemon",
|
|
"desktop/ui",
|
|
"desktop/platform-linux",
|
|
"desktop/platform-windows",
|
|
]
|
|
|
|
# Release profile is workspace-wide (section 5 of CLAUDE.md). Cargo rejects
|
|
# [profile.*] in member manifests, so this lives here and nowhere else.
|
|
#
|
|
# The combination is deliberate: `z` optimises for size, LTO + codegen-units=1
|
|
# let the linker drop everything unreachable, and panic="abort" removes
|
|
# unwinding tables entirely. Together they are what make the 5 MB stripped
|
|
# daemon budget (section 5) reachable. scripts/ci.sh enforces the size gate.
|
|
# Workspace-wide lints. These apply to every member crate.
|
|
#
|
|
# They are set here rather than in clippy.toml because they must apply to the
|
|
# compilation — clippy.toml only accepts bare key=value pairs, and a `[lints]`
|
|
# table there is rejected outright. crates.io-wide intent is the same: these
|
|
# encode CLAUDE.md section 0 rule 5 (every fallible OS call handled, no
|
|
# unwrap/expect outside tests) and the section 1.5.3 arithmetic care.
|
|
[workspace.lints.rust]
|
|
# Rustc lints only. NOTE: `integer_conversions` is NOT a rustc lint name and
|
|
# cargo rejects it here as an unknown lint, so the u64 -> u32 truncation concern
|
|
# is expressed as the clippy lint below, which is the check that actually exists.
|
|
unsafe_code = "forbid"
|
|
|
|
[workspace.lints.clippy]
|
|
# Panicking in a release build leaves enforcement down. Section 1.5.4 requires
|
|
# failing open with a log line, not aborting.
|
|
panic = "deny"
|
|
# `unwrap`/`expect` on an OS or I/O result is forbidden outside tests (section 0
|
|
# rule 5).
|
|
unwrap_used = "deny"
|
|
expect_used = "deny"
|
|
# Silent u64 -> u32 truncation in usage counters and monotonic deltas is a real
|
|
# bug class in this codebase, not a style issue: an overflow there would corrupt
|
|
# a lock timer, which section 1.5.3 depends on.
|
|
cast_possible_truncation = "deny"
|
|
# Stack waste in the evaluator, which runs on every focus change.
|
|
large_enum_variant = "warn"
|
|
# Clarity over cleverness; section 5 budgets are measured, not guessed.
|
|
too_many_arguments = "warn"
|
|
|
|
[profile.release]
|
|
opt-level = "z"
|
|
lto = true
|
|
codegen-units = 1
|
|
panic = "abort"
|
|
strip = true
|
|
|
|
# Debug builds keep debug assertions and overflow checks — the engine does
|
|
# arithmetic on millisecond counters and monotonic deltas, and overflow in a
|
|
# release build would corrupt a lock timer (section 1.5.3).
|
|
[profile.dev]
|
|
opt-level = 0
|
|
debug = true
|
|
|
|
# Tests are compiled without panic=abort so that should_panic works.
|
|
[profile.test]
|
|
opt-level = 1 |