disk-panic-openenv / server /graders.py
yashppawar's picture
Restructure to flat template layout; add pyproject + uv.lock; pass openenv validate
30c52ad verified
"""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,
}