Files
SellPL c723efb186 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>
2026-10-02 13:10:25 +00:00

24 lines
1.1 KiB
TOML

# Clippy configuration for Blocker.
#
# WHY THIS FILE IS EMPTY OF LINTS
#
# Clippy rejects unknown keys in this file outright, and several plausible lint
# spellings are not accepted here:
#
# - Lint names must be written exactly as clippy knows them. `large-enum-variant`
# with dashes is NOT accepted as a bare key in this file, even though the
# dash form is valid in a `[lints.clippy]` Cargo.toml table.
# - `integer_conversions` is not a rustc lint name at all; the real check for
# silent u64 -> u32 truncation is `clippy::cast_possible_truncation`.
#
# So all lint policy lives in `[workspace.lints]` in Cargo.toml, which is the form
# clippy actually applies during compilation. Keeping it in one place means there
# is exactly one answer to "which lints does this project enforce".
#
# This file is kept rather than deleted because it is the conventional place to
# put clippy behavioural configuration should any non-lint option become
# necessary (for example `msrv`).
# Set explicitly so clippy does not infer an MSRV from the toolchain and start
# suggesting APIs newer than the project targets.
msrv = "1.75"