claude-skills/engineering/skillopt-sleep/skillopt_sleep/gate.py
Claude cf6ca763ec
feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt
Verbatim copy of the stdlib-only skillopt_sleep engine + Claude Code
plugin surface (skills/hooks/commands/scripts) into
engineering/skillopt-sleep/. Gives a local agent a nightly gated
self-improvement cycle: read-only harvest of past Claude Code session
transcripts -> mine recurring tasks -> offline replay -> held-out-gated
CLAUDE.md/SKILL.md edits -> staged for explicit /skillopt-sleep adopt.
Nothing live changes without that explicit step.

The heavier skillopt training package (needs numpy/openai/azure-* +
hand-labeled benchmarks per task) was deliberately not vendored, since
it optimizes one narrow scoreable task at a time and doesn't fit this
repo's broad domain-expertise skills or no-ML-in-scripts convention.

Attribution preserved in plugin.json + LICENSE + README.md (MIT,
Microsoft Corporation / Yifan Yang), following the same verbatim-vendor
pattern already used for loop-library/. Registered as its own
marketplace plugin; headline counters in README.md/CLAUDE.md/
marketplace.json trued up via scripts/derive_counters.py --check.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TX374i2YGrjNV4Yi3AmaKS
2026-07-08 05:42:44 +00:00

50 lines
2.1 KiB
Python

"""SkillOpt-Sleep — vendored validation gate.
This is a self-contained copy of the SkillOpt validation gate so the sleep
engine has ZERO dependency on the research package (skillopt/*). The research
repo's ``skillopt.evaluation.gate`` is the reference implementation and the two
are kept behaviourally identical; vendoring keeps this open-source tool
decoupled from the paper's experiment code.
"""
from __future__ import annotations
from dataclasses import dataclass
@dataclass(frozen=True)
class GateResult:
action: str # "accept_new_best" | "accept" | "reject"
current_skill: str
current_score: float
best_skill: str
best_score: float
best_step: int
def select_gate_score(hard: float, soft: float, metric: str = "hard",
mixed_weight: float = 0.5) -> float:
"""Project (hard, soft) onto a single comparison metric."""
if metric == "hard":
return float(hard)
if metric == "soft":
return float(soft)
if metric == "mixed":
w = max(0.0, min(1.0, float(mixed_weight)))
return (1.0 - w) * float(hard) + w * float(soft)
raise ValueError(f"unknown gate metric {metric!r}; expected hard/soft/mixed")
def evaluate_gate(candidate_skill: str, cand_hard: float, current_skill: str,
current_score: float, best_skill: str, best_score: float,
best_step: int, global_step: int, *, cand_soft: float = 0.0,
metric: str = "hard", mixed_weight: float = 0.5) -> GateResult:
"""Pure gate decision: compare candidate score to current/best."""
cand_score = select_gate_score(cand_hard, cand_soft, metric, mixed_weight)
if cand_score > current_score:
if cand_score > best_score:
return GateResult("accept_new_best", candidate_skill, cand_score,
candidate_skill, cand_score, global_step)
return GateResult("accept", candidate_skill, cand_score,
best_skill, best_score, best_step)
return GateResult("reject", current_skill, current_score,
best_skill, best_score, best_step)