sprawling-kernel 0.0.12

Every decision of a sprawling city - addresses, events, gates, plans and errors - with nothing that touches a disk.
Documentation
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-- Copyright (c) 2026 2youg1 and the sprawling contributors

/-!
# kernel::discard

规定 `kernel::discard`(`crates/kernel/src/discard.rs` 与 `crates/kernel/src/discard/` 下的 `request`、`verdict`、`forecast`):删除的还原方案、Discard 门的判定表与 exec 的预判。本文件是 `crates/kernel/Spec.lean` 的一个分部;下面每一节保留它在 kernel 规格里的标签 §8-n,别处引作 `crates/kernel/Spec.lean §8-n`。
-/

/-!
### 8-26 kernel::discard

```rust
pub enum Restoration { Tracked(Locator), Interred(Locator), Rebuildable { reason: String } }
pub struct Discard { /* paths, plan, taint, total_bytes —— 私有 */ }
impl Discard {
    /// Sole constructor (C14): restoration mandatory and scheme-checked —
    /// Tracked wants file:, Interred wants cas:, Rebuildable wants a
    /// non-empty reason; violations are E_DISCARD_IRREVERSIBLE.
    pub fn new(paths: Vec<Address>, plan: Restoration, taint: TaintSet, total_bytes: ByteLen)
        -> Result<Discard, AxError>;
    pub fn paths(&self) -> &[Address];  pub fn plan(&self) -> &Restoration;
    pub fn taint(&self) -> &TaintSet;   pub fn total_bytes(&self) -> ByteLen;
}

pub enum DiscardRequest { Planned(Discard),
                                            Unplanned { paths: Vec<Address>, taint: TaintSet, total_bytes: ByteLen } }
pub enum DenyReason { NoRestoration, Tainted }
pub enum DiscardVerdict { Allow, Deny { reason: DenyReason } }
/// The discard door's decision table, sole authority —
/// gate::discard delegates wholly and only shapes the refusal.
pub fn decide(req: &DiscardRequest) -> DiscardVerdict;

pub enum DiscardForecast { Clear, Suspected { pattern: String } }
pub fn forecast(arm: &ExecArm) -> DiscardForecast;
```

- **decide 表**:Unplanned → Deny{NoRestoration}(无还原不可构造,使 Planned 恒有 plan);Planned 且 taint 非空 → Deny{Tainted}(恒,无视规模);余 Allow。判序固定,确定可重放。
- **规模与归属不改答(D1「默认 YOLO」)**:一次 Planned 删除恒带 `Restoration`,因此恒可回滚;文件数、字节数、别人登记过的 asset 都不让删除停下来问人。删除的上界另有家:write domain 决定一个 resident 够得到哪些文件,registry 保存把 asset 放回去所需的凭据。
- **forecast 三臂预判力递减**:Program 读 `(path, args)` 整体——取 path 的末段并去掉 `.exe` 后缀,basename ∈ {rm, rmdir, del},或 git 的参数里有 `reset --hard` 或 `clean`,或 find 的参数里有 `-delete`;Python 与 Shell 只能查子串,可被混淆绕过,恒保守:两臂共用文本表(`rm `、`rmdir`、`-delete`、`git reset --hard`、`git clean`),Python 另查 `os.remove`、`shutil.rmtree`、`os.unlink` 与以写模式打开文件(`open(` 与 `'w'` 或 `"w"` 同在),Shell 另查截断重定向(含 `>` 而全文不含 `>>`)。git 检查点兜底(`storage::checkpoint`)。两张子串表是 pub(crate) 数据面。
- 同文件 `#[test]` 守 Discard 门 fail-closed:Unplanned 恒 Deny;Tainted 恒 Deny(kani 不接手,理由见 `crates/kernel/Spec.lean` §2)。
-/

namespace Kernel.Discard

/-- 删除为什么被拒,与 `kernel::DenyReason` 逐变体同名。 -/
inductive DenyReason where
  | NoRestoration
  | Tainted
  deriving DecidableEq, Repr

/-- Discard 门的答案,与 `kernel::DiscardVerdict` 逐变体同名。 -/
inductive DiscardVerdict where
  | Allow
  | Deny (reason : DenyReason)
  deriving DecidableEq, Repr

/-- 到门口的请求,与 `kernel::DiscardRequest` 逐变体同名。`Planned` 带着一份还原方案(`Discard::new` 是唯一构造点,没有还原方案拼不出来),`Unplanned` 是 exec 预判路上的请求,文本预判铸不出计划。两者都带 taint 与总字节数;路径不进判定,模型里省去。 -/
inductive DiscardRequest where
  | Planned (tainted : Bool) (total_bytes : Nat)
  | Unplanned (tainted : Bool) (total_bytes : Nat)
  deriving DecidableEq, Repr

/-- `discard::decide`,Discard 门的判定表,唯一权威;`gate::discard` 整个委派给它,只塑形拒词。判序固定:没有计划先拒,再看 taint。 -/
def decide : DiscardRequest → DiscardVerdict
  | .Unplanned _ _ => .Deny .NoRestoration
  | .Planned true _ => .Deny .Tainted
  | .Planned false _ => .Allow

/-- 没有计划的删除恒被拒。 -/
theorem an_unplanned_discard_is_always_denied (tainted : Bool) (total_bytes : Nat) :
    decide (.Unplanned tainted total_bytes) = .Deny .NoRestoration :=
  rfl

/-- 外来内容要求的删除恒被拒,不论大小。 -/
theorem a_tainted_discard_is_always_denied (total_bytes : Nat) :
    decide (.Planned true total_bytes) ≠ .Allow := by
  simp [decide]

/-- **规模不改答(D1「默认 YOLO」)。** 一次有计划的删除恒可回滚,所以文件数与字节数都不让它停下来问人。 -/
theorem size_never_changes_the_answer (request : DiscardRequest) (bytes : Nat) :
    decide (match request with
      | .Planned tainted _ => .Planned tainted bytes
      | .Unplanned tainted _ => .Unplanned tainted bytes) = decide request := by
  cases request with
  | Planned tainted _ => cases tainted <;> rfl
  | Unplanned _ _ => rfl

/-- **放行的删除都带着还原方案且不带 taint。** -/
theorem an_allowed_discard_is_planned_and_clean (request : DiscardRequest)
    (allowed : decide request = .Allow) : ∃ bytes, request = .Planned false bytes := by
  cases request with
  | Planned tainted bytes =>
    cases tainted
    · exact ⟨bytes, rfl⟩
    · simp [decide] at allowed
  | Unplanned _ _ => simp [decide] at allowed

end Kernel.Discard