From d72993d90885d5bd06e067f3394cc1b968d244b6 Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Freeman Date: Sun, 5 Apr 2026 18:28:56 +0000 Subject: [PATCH] docs: add shell safety API, invariant reconciliation architecture, and new feature highlights - docs/api/tui.md: Document ShellDangerLevel, DangerousPattern, DEFAULT_PATTERNS, DangerousPatternDetector, ShellSafetyService, and SafetyCheckResult with full API reference, parameter tables, and usage examples - docs/architecture.md: Add Invariant Reconciliation section covering the builtin/invariant-reconciliation actor, four-scope algorithm, failure behaviour, and DI registration - README.md: Add Invariant Reconciliation, TUI shell danger detection, and UKO provenance tracking to the Highlights section ISSUES CLOSED: #3377 --- docs/api/tui.md | 74 ++++++++++++++++++++++++++++++++++++++++++-- docs/architecture.md | 40 ++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 3 deletions(-) diff --git a/docs/api/tui.md b/docs/api/tui.md index 97618ce09..b5aad6276 100644 --- a/docs/api/tui.md +++ b/docs/api/tui.md @@ -293,7 +293,8 @@ from cleveragents.tui.shell_safety import ShellDangerLevel | `HIGH` | 3 | High risk — significant, hard-to-reverse damage (e.g. `dd if=`, `mkfs`) | | `CRITICAL` | 4 | Critical risk — can destroy the system or create a fork bomb (e.g. `rm -rf /`, `:(){ :\|:& };:`) | -Numeric comparisons work naturally: `level >= ShellDangerLevel.HIGH`. +Because `ShellDangerLevel` is an `IntEnum`, numeric comparisons work +naturally: `level >= ShellDangerLevel.HIGH`. --- @@ -302,7 +303,6 @@ Numeric comparisons work naturally: `level >= ShellDangerLevel.HIGH`. ```python from cleveragents.tui.shell_safety import DangerousPattern -pattern = DangerousPattern( name="rm_rf_root", pattern=r"rm\s+-[rRfF]*\s+/", level=ShellDangerLevel.CRITICAL, @@ -324,6 +324,54 @@ Immutable frozen dataclass describing a single dangerous shell pattern. --- +### Built-in Patterns (`DEFAULT_PATTERNS`) + +```python +from cleveragents.tui.shell_safety.pattern_registry import DEFAULT_PATTERNS +``` + +The default pattern set covers the most common dangerous shell operations: + +| Name | Level | Trigger | +|------|-------|---------| +| `rm_rf_root` | CRITICAL | `rm -rf /` | +| `rm_rf_wildcard` | CRITICAL | `rm -rf /*` or similar | +| `fork_bomb` | CRITICAL | `:(){ :|:& };:` | +| `dd_if_device` | HIGH | `dd if=` | +| `mkfs` | HIGH | `mkfs` | +| `shred_device` | HIGH | `shred /dev/` or `shred --remove` | +| `chmod_777` | MEDIUM | `chmod 777` | +| `sudo_rm` | MEDIUM | `sudo rm` | +| `wget_pipe_sh` | MEDIUM | `wget … \| sh` | +| `curl_pipe_sh` | MEDIUM | `curl … \| sh` | +| `wget_pipe_bash` | MEDIUM | `wget … \| bash` | +| `curl_pipe_bash` | MEDIUM | `curl … \| bash` | +| `git_push_force` | LOW | `git push --force` | +| `chmod_recursive_permissive` | LOW | `chmod -R 6xx/7xx` | + +--- + +### `DangerousPatternDetector` + +```python +from cleveragents.tui.shell_safety import DangerousPatternDetector + +detector = DangerousPatternDetector() +warning = detector.check_first("rm -rf /") +# → DangerousCommandWarning or None +``` + +Checks a command string against all registered patterns and returns the +first match as a `DangerousCommandWarning`, or `None` if the command is safe. + +| Method | Description | +|--------|-------------| +| `check_first(command) → DangerousCommandWarning \| None` | Return the first matching warning | +| `check_all(command) → list[DangerousCommandWarning]` | Return all matching warnings | +| `add_pattern(pattern)` | Register an additional `DangerousPattern` | + +--- + ### `ShellSafetyService` ```python @@ -353,7 +401,11 @@ Application service that checks shell commands before execution. | `check_command(command)` | `SafetyCheckResult` | Check a command and return a structured result | | `is_safe(command)` | `bool` | Convenience wrapper — returns `True` if the command passes | -**`SafetyCheckResult`** attributes: +**`SafetyCheckResult`** — import path and attributes: + +```python +from cleveragents.tui.shell_safety.safety_service import SafetyCheckResult +``` | Attribute | Type | Description | |-----------|------|-------------| @@ -361,6 +413,22 @@ Application service that checks shell commands before execution. | `warning` | `DangerousCommandWarning \| None` | Warning if a dangerous pattern matched, otherwise `None` | | `allowed` | `bool` | `True` if the command is allowed to proceed | +**Example — custom block level and callback:** + +```python +from cleveragents.tui.shell_safety import ShellSafetyService, ShellDangerLevel + +def my_callback(warning): + # Show a UI prompt; return True to allow, False to block + return ask_user(f"⚠️ {warning.message}. Proceed?") + +service = ShellSafetyService( + block_level=ShellDangerLevel.HIGH, + warn_callback=my_callback, +) +result = service.check_command("sudo rm /etc/hosts") +``` + **Built-in pattern categories:** - Destructive filesystem operations (`rm -rf`, `shred`, `wipe`) diff --git a/docs/architecture.md b/docs/architecture.md index fd240f5bc..e355f0c5e 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -219,6 +219,46 @@ Key UKO capabilities: --- +## Invariant Reconciliation + +The **Invariant Reconciliation Actor** (`builtin/invariant-reconciliation`) +is automatically invoked at the start of the Strategize, Execute, and Apply +phase transitions by `PlanLifecycleService`. + +**Reconciliation algorithm** (spec §19440–19600): + +1. Collect invariants from four scopes: global, project, action, plan. +2. Group by normalised text (case-insensitive, stripped). +3. Detect conflicts between invariants at different scopes. +4. Resolve using specificity: `plan > action > project > global`. + Exception: `non_overridable` global invariants always win. +5. Record an `invariant_enforced` decision for each active invariant. +6. Return a reconciled `InvariantSet`. + +**Failure behaviour:** + +- Reconciliation failures block the phase transition with + `ReconciliationBlockedError` and emit an `INVARIANT_VIOLATED` event. +- Post-correction reconciliation runs via `CORRECTION_APPLIED` event + subscription (best-effort; does not block correction completion). + +**DI registration:** `InvariantService` is registered as a Singleton +provider in the DI container. + +```python +from cleveragents.actor.reconciliation import InvariantReconciliationActor + +actor = InvariantReconciliationActor( + invariant_service=container.invariant_service(), + decision_service=container.decision_service(), +) +result = actor.run(plan_id="...", project_name="...", action_name="...") +# result.reconciled_set — effective InvariantSet +# result.conflicts — list[ConflictRecord] with resolution details +``` + +--- + ## A2A Protocol Boundary All CLI, TUI, and server interactions cross the A2A boundary -- 2.52.0