"""Deterministic graders for the three tasks. All return a reward in [0.0, 1.0].""" from __future__ import annotations from typing import Dict from .vfs import VFS from .scenarios import _audit_sha def grade_easy(vfs: VFS, targets: Dict[str, object]) -> float: """Reward = smoothly interpolated disk-free fraction.""" pct = vfs.usage_pct() if pct <= 60: disk_score = 1.0 elif pct >= 99: disk_score = 0.0 else: disk_score = max(0.0, (99 - pct) / 39) audit_ok = _audit_sha(vfs) == targets["audit_sha"] # If audit got touched, cap score at 0.5. return min(disk_score, 0.5) if not audit_ok else disk_score def grade_medium(vfs: VFS, targets: Dict[str, object]) -> float: pct = vfs.usage_pct() disk = 1.0 if pct < 80 else max(0.0, (99 - pct) / 19) svc = 1.0 if vfs.services.get("app") == "active" else 0.0 audit = 1.0 if _audit_sha(vfs) == targets["audit_sha"] else 0.0 score = 0.4 * disk + 0.4 * svc + 0.2 * audit return round(score, 4) def grade_hard(vfs: VFS, targets: Dict[str, object]) -> float: pct = vfs.usage_pct() disk = 1.0 if pct < 80 else max(0.0, (99 - pct) / 19) svc = 1.0 if vfs.services.get("app") == "active" else 0.0 audit = 1.0 if _audit_sha(vfs) == targets["audit_sha"] else 0.0 lr_path = targets.get("logrotate_path", "/etc/logrotate.d/app") logrotate = 0.0 if isinstance(lr_path, str) and lr_path in vfs.files: content = vfs.files[lr_path].content.decode("utf-8", errors="ignore") if "rotate" in content and "size" in content: logrotate = 1.0 elif content.strip(): logrotate = 0.5 score = 0.3 * disk + 0.3 * svc + 0.2 * audit + 0.2 * logrotate return round(score, 4) GRADERS = { "easy": grade_easy, "medium": grade_medium, "hard": grade_hard, }