|
| 1 | +// SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | += Rust/SPARK Stance — ambientops |
| 3 | +:toc: macro |
| 4 | +:toclevels: 2 |
| 5 | + |
| 6 | +toc::[] |
| 7 | + |
| 8 | +== Summary |
| 9 | + |
| 10 | +This monorepo's application logic is largely Rust (≈168 `.rs` files |
| 11 | +across `personal-sysadmin`, `czech-file-knife`, `contracts-rust`, |
| 12 | +`emergency-room/rust`, and other crates). Per the estate language policy |
| 13 | +(`standards/rhodium-standard-repositories/spec/LANGUAGE-POLICY.adoc` |
| 14 | +§Terminology), *"Rust" means "Rust/SPARK"*: Rust is a primary |
| 15 | +implementation language, but the project is *designed to admit |
| 16 | +SPARK/Ada verification modules* across a stable, Idris2-typed boundary, |
| 17 | +with Zig as the FFI layer. Rust here is never the ABI/API/FFI layer. |
| 18 | + |
| 19 | +Unlike a bare-Rust crate, ambientops *already* satisfies the structural |
| 20 | +requirements of that policy and goes further than most estate repos: |
| 21 | + |
| 22 | +* An Idris2 ABI seam exists (`src/abi/{Types,Layout,Foreign}.idr`, plus |
| 23 | + per-component `*/src/abi/` seams) with no dangerous patterns in the |
| 24 | + ABI layer — see `PROOF-NEEDS.md` and `ABI-FFI-README.md`. |
| 25 | +* A Zig FFI layer is present (`*/ffi/zig/`, `*/build.zig`). |
| 26 | +* Real, *non-theatrical* SPARK/Ada exists in |
| 27 | + `total-update/ada/dnfinition/` with a dedicated `spark` build mode in |
| 28 | + `dnfinition.gpr` (`BUILD_MODE=spark`) and proven subprogram contracts |
| 29 | + on the safety-critical specs. |
| 30 | + |
| 31 | +This document is the project's compliance stance. The audit (2026-05-18) |
| 32 | +flagged ambientops as "`SPARK_Mode On`, zero contracts, no stance doc". |
| 33 | +That is **inaccurate on contracts** (see the coverage matrix below — the |
| 34 | +safety-critical specs are heavily contracted) and **correct on the |
| 35 | +stance doc** (this file was missing). The one genuine contract gap and |
| 36 | +one deliberate `SPARK_Mode (Off)` are documented honestly under |
| 37 | +<<Honest gaps>>. |
| 38 | + |
| 39 | +== SPARK contract coverage (`total-update/ada/dnfinition/src`) |
| 40 | + |
| 41 | +Contracts in SPARK live on the package *spec* (`.ads`), not the body |
| 42 | +(`.adb`); body files carrying zero `Pre/Post` is correct, not theatre. |
| 43 | +Spec-level coverage: |
| 44 | + |
| 45 | +[cols="2,1,3",options="header"] |
| 46 | +|=== |
| 47 | +| Spec | Contracts | Assessment |
| 48 | + |
| 49 | +| `reversibility/snapshot_manager.ads` | 25 | Strong — pre/postconditions on snapshot lifecycle |
| 50 | +| `safety/safety_boundary.ads` | 15 | Strong — safety-boundary enforcement |
| 51 | +| `safety/safety_invariant.ads` | 14 | Strong — invariant preservation |
| 52 | +| `plugins/strategy_matrix.ads` | 5 | Adequate |
| 53 | +| `platform/platform_detection.ads` | 4 | Adequate |
| 54 | +| `backends/backend_guix.ads` | 3 | Adequate |
| 55 | +| `backends/backend_nix.ads` | 2 | Minimal but present |
| 56 | +| `plugins/plugin_registry.ads` | 1 | Weak — registration pre-only (OWED: strengthen) |
| 57 | +| `backends/backend_interface.ads` | 0 | **OWED** — abstract interface with no `Pre'Class`/`Post'Class` (see gaps) |
| 58 | +| `reversibility/reversibility_types.ads` | n/a | Deliberate `pragma SPARK_Mode (Off)` — documented in-file (Snapshot_ID name clash); not theatre |
| 59 | +|=== |
| 60 | + |
| 61 | +== What is correctness-critical here |
| 62 | + |
| 63 | +ambientops manages host system operations (package transactions, |
| 64 | +reversibility/rollback, emergency intervention) where incorrect |
| 65 | +behaviour can destabilise the host. The proof-relevant surface, per |
| 66 | +`PROOF-NEEDS.md`: |
| 67 | + |
| 68 | +[cols="1,3",options="header"] |
| 69 | +|=== |
| 70 | +| Property | Where |
| 71 | + |
| 72 | +| Rollback atomicity (failed op rolls back completely, no partial state) |
| 73 | +| `reversibility/snapshot_manager.ads` (contracted), `reversibility_types.ads` |
| 74 | +| Reversibility-operation idempotency |
| 75 | +| `safety/safety_invariant.ads`, emergency-room components |
| 76 | +| Privilege / scope containment (no operation escalates beyond declared scope) |
| 77 | +| `safety/safety_boundary.ads` |
| 78 | +| Backend transaction soundness (install/remove/upgrade contracts) |
| 79 | +| `backends/backend_*.ads` against the `backend_interface` contract |
| 80 | +|=== |
| 81 | + |
| 82 | +== The seam |
| 83 | + |
| 84 | +[cols="1,3",options="header"] |
| 85 | +|=== |
| 86 | +| Layer | Artifact |
| 87 | + |
| 88 | +| ABI (Idris2) | `src/abi/{Types,Layout,Foreign}.idr` + per-component `*/src/abi/` |
| 89 | +| FFI (Zig) | `*/ffi/zig/`, `*/build.zig` |
| 90 | +| SPARK proof | `total-update/ada/dnfinition/` with `dnfinition.gpr` `BUILD_MODE=spark`; `gnatprove` is the discharge tool for the `.ads` contracts above |
| 91 | +|=== |
| 92 | + |
| 93 | +The Rust crates sit on the application side of this seam; they are never |
| 94 | +the ABI/FFI boundary, satisfying §"Rust is never the ABI". |
| 95 | + |
| 96 | +[#honest-gaps] |
| 97 | +== Honest gaps |
| 98 | + |
| 99 | +* *`backend_interface.ads` (OWED).* This is a pure abstract interface |
| 100 | + (`type Package_Manager_Backend is interface;`, ~20 `is abstract` |
| 101 | + primitives: `Install`, `Remove`, `Upgrade`, `Search`, …). It carries |
| 102 | + no class-wide contracts, so `gnatprove` cannot propagate obligations |
| 103 | + to the `backend_guix`/`backend_nix` implementations. Adding |
| 104 | + `Pre'Class`/`Post'Class` to the safety-relevant primitives (notably |
| 105 | + `Install`/`Remove`/`Upgrade` result states and `Is_Available` |
| 106 | + preconditions) is the principal remaining SPARK proof obligation. |
| 107 | + Tracked under `standards#124` / `#127`. |
| 108 | +* *`plugin_registry.ads` (weak).* One contract only; strengthen |
| 109 | + registration post-state. Lower priority than the interface. |
| 110 | +* *`reversibility_types.ads` is deliberately `SPARK_Mode (Off)`.* The |
| 111 | + in-file comment documents the reason (a record-component / type-name |
| 112 | + clash on `Rollback_Result.Snapshot_ID`); SPARK is re-enabled once a |
| 113 | + genuine proof obligation justifies resolving the clash. This is an |
| 114 | + honest, documented deferral — explicitly *not* the "theatre" pattern. |
| 115 | +* *`.claude/CLAUDE.md` language drift.* Any stale ReScript/Tauri/Deno |
| 116 | + table is superseded by the estate policy and this stance doc; reconcile |
| 117 | + separately. |
| 118 | + |
| 119 | +== References |
| 120 | + |
| 121 | +* `standards/rhodium-standard-repositories/spec/LANGUAGE-POLICY.adoc` |
| 122 | + §Terminology, §"Rust is never the ABI" |
| 123 | +* `PROOF-NEEDS.md` (this repo) — Idris2 proof obligations and priorities |
| 124 | +* `ABI-FFI-README.md` (this repo) — ABI/FFI architecture |
| 125 | +* Estate exemplar stance: `proof-of-work/RUST-SPARK-STANCE.adoc` |
0 commit comments