Repository navigation
Expand file tree
/
Copy pathcheck_skillopt_packet.py
More file actions
executable file
·278 lines (215 loc) · 9.14 KB
/
Copy pathcheck_skillopt_packet.py
File metadata and controls
executable file
·278 lines (215 loc) · 9.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#!/usr/bin/env python3
"""Validate a SkillOpt-style improvement packet for Paper-WorkFlow.
The checker is intentionally mechanical. It does not judge the quality of the
skill edit; it catches common process failures before a maintenance patch is
treated as evidence-backed:
- no held-out selection cases;
- accepted candidate score is not better than baseline;
- regression check failed;
- proposed edit count exceeds the declared budget;
- placeholders are still present.
"""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
REQUIRED_HEADINGS = [
"Candidate",
"Rollout Split",
"Reflection",
"Proposed Bounded Patch",
"Gate Decision",
"Adoption Record",
]
PLACEHOLDER_RE = re.compile(r"\b(TBD|TODO|PLACEHOLDER|REPLACE_ME)\b|<[^>\n]+>")
SCORE_RE = re.compile(r"score\s*=\s*([0-9]+(?:\.[0-9]+)?)", re.IGNORECASE)
EDIT_LINE_RE = re.compile(r"^\s*-\s*\[[ xX]\]\s*edit[_-]?\d+\b", re.MULTILINE)
class PacketError(Exception):
"""Raised for packet validation failures."""
def _heading_block(text: str, heading: str, level: int = 2) -> str:
hashes = "#" * level
pattern = re.compile(
rf"^{re.escape(hashes)}\s+{re.escape(heading)}\s*$"
rf"(?P<body>.*?)"
rf"(?=^{re.escape(hashes)}\s+|\Z)",
re.MULTILINE | re.DOTALL,
)
match = pattern.search(text)
if not match:
raise PacketError(f"missing heading: {hashes} {heading}")
return match.group("body").strip()
def _subheading_block(section: str, heading: str) -> str:
pattern = re.compile(
rf"^###\s+{re.escape(heading)}\s*$"
rf"(?P<body>.*?)"
rf"(?=^###\s+|\Z)",
re.MULTILINE | re.DOTALL,
)
match = pattern.search(section)
if not match:
raise PacketError(f"missing subheading: ### {heading}")
return match.group("body").strip()
def _parse_int(label: str, text: str) -> int:
match = re.search(rf"{re.escape(label)}\s*:\s*(\d+)", text, re.IGNORECASE)
if not match:
raise PacketError(f"missing integer field: {label}")
return int(match.group(1))
def _parse_score(label: str, text: str) -> float:
match = re.search(
rf"{re.escape(label)}\s*:\s*([0-9]+(?:\.[0-9]+)?)",
text,
re.IGNORECASE,
)
if not match:
raise PacketError(f"missing score field: {label}")
return float(match.group(1))
def _parse_choice(label: str, choices: set[str], text: str) -> str:
match = re.search(rf"{re.escape(label)}\s*:\s*([A-Za-z_-]+)", text, re.IGNORECASE)
if not match:
raise PacketError(f"missing choice field: {label}")
value = match.group(1).strip().lower().replace("_", "-")
normalized = {c.lower().replace("_", "-") for c in choices}
if value not in normalized:
raise PacketError(f"{label} must be one of {sorted(choices)}, got {match.group(1)!r}")
return value
def _rollout_count(block: str) -> int:
count = 0
for line in block.splitlines():
stripped = line.strip()
if not stripped.startswith("- ["):
continue
if "evidence=" not in stripped or not SCORE_RE.search(stripped):
continue
count += 1
return count
def validate_packet(text: str) -> list[str]:
errors: list[str] = []
if "# SkillOpt Improvement Packet" not in text:
errors.append("missing title: # SkillOpt Improvement Packet")
for heading in REQUIRED_HEADINGS:
try:
_heading_block(text, heading)
except PacketError as exc:
errors.append(str(exc))
if PLACEHOLDER_RE.search(text):
errors.append("packet still contains placeholders such as <...>, TBD, TODO, or REPLACE_ME")
try:
candidate = _heading_block(text, "Candidate")
budget = _parse_int("Edit budget L", candidate)
if budget < 0 or budget > 8:
errors.append("Edit budget L must be between 0 and 8")
except PacketError as exc:
errors.append(str(exc))
budget = 0
try:
split = _heading_block(text, "Rollout Split")
train = _subheading_block(split, "Train rollouts")
selection = _subheading_block(split, "Held-out selection rollouts")
train_count = _rollout_count(train)
selection_count = _rollout_count(selection)
if train_count < 3:
errors.append(f"need at least 3 train rollouts, found {train_count}")
if selection_count < 2:
errors.append(f"need at least 2 held-out selection rollouts, found {selection_count}")
except PacketError as exc:
errors.append(str(exc))
try:
patch = _heading_block(text, "Proposed Bounded Patch")
edit_count = len(EDIT_LINE_RE.findall(patch))
if edit_count > budget:
errors.append(f"proposed edit count {edit_count} exceeds edit budget L={budget}")
except PacketError as exc:
errors.append(str(exc))
edit_count = 0
try:
gate = _heading_block(text, "Gate Decision")
baseline = _parse_score("Baseline selection score", gate)
candidate_score = _parse_score("Candidate selection score", gate)
decision = _parse_choice("Gate decision", {"accept", "reject"}, gate)
regression = _parse_choice("Regression check", {"pass", "fail"}, gate)
if decision == "accept":
if candidate_score <= baseline:
errors.append(
f"accept requires candidate score > baseline, got {candidate_score} <= {baseline}"
)
if regression != "pass":
errors.append("accept requires Regression check: pass")
if edit_count == 0:
errors.append("accept requires at least one proposed edit")
except PacketError as exc:
errors.append(str(exc))
return errors
def _selftest() -> int:
good = """# SkillOpt Improvement Packet
## Candidate
- Target skill files: SKILL.md
- Source SkillOpt snapshot: https://github.com/microsoft/SkillOpt fc1f827
- Change scope: add a bounded update gate
- Edit budget L: 2
- Protected areas: parent repo catalog
## Rollout Split
### Train rollouts
- [ ] train-001 | status=failure | score=0.30 | evidence=logs/a.md | note=route failed
- [ ] train-002 | status=success | score=0.80 | evidence=logs/b.md | note=gate held
- [ ] train-003 | status=failure | score=0.40 | evidence=logs/c.md | note=context leak
### Held-out selection rollouts
- [ ] select-001 | status=success | score=0.70 | evidence=logs/d.md | note=route fixed
- [ ] select-002 | status=success | score=0.75 | evidence=logs/e.md | note=gate fixed
## Reflection
### Failure patterns
- Maintenance edits lacked held-out checks.
### Success patterns
- Existing mechanical validators caught schema drift.
## Proposed Bounded Patch
- [ ] edit_1 | op=append | target=SKILL.md#constraints | rationale=add held-out gate
- [ ] edit_2 | op=append | target=validate_skill.py | rationale=check packet process
## Gate Decision
- Baseline selection score: 0.62
- Candidate selection score: 0.70
- Gate decision: accept
- Regression check: pass
- Selection rubric: routing, gate integrity, validation
## Adoption Record
- Accepted files: SKILL.md, validate_skill.py
- Rejected edit memory: none
- Validation commands: python3 validate_skill.py pass
- Follow-up: parent catalog refresh later
"""
assert not validate_packet(good)
bad_train_only = good.replace(
"- [ ] select-001 | status=success | score=0.70 | evidence=logs/d.md | note=route fixed\n"
"- [ ] select-002 | status=success | score=0.75 | evidence=logs/e.md | note=gate fixed\n",
"",
)
assert any("held-out selection" in e for e in validate_packet(bad_train_only))
bad_over_budget = good.replace("Edit budget L: 2", "Edit budget L: 1")
assert any("exceeds edit budget" in e for e in validate_packet(bad_over_budget))
bad_accept = good.replace("Candidate selection score: 0.70", "Candidate selection score: 0.50")
assert any("accept requires candidate score" in e for e in validate_packet(bad_accept))
bad_regression = good.replace("Regression check: pass", "Regression check: fail")
assert any("Regression check: pass" in e for e in validate_packet(bad_regression))
print("selftest OK: SkillOpt packet checker invariants hold")
return 0
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Validate a Paper-WorkFlow SkillOpt packet.")
parser.add_argument("packet", nargs="?", help="path to a filled SKILLOPT_PACKET.md")
parser.add_argument("--selftest", action="store_true", help="run built-in checker selftests")
args = parser.parse_args(argv)
if args.selftest:
return _selftest()
if not args.packet:
parser.error("packet path is required unless --selftest is used")
path = Path(args.packet).expanduser().resolve()
if not path.exists():
print(f"FAIL: packet does not exist: {path}", file=sys.stderr)
return 1
errors = validate_packet(path.read_text(encoding="utf-8"))
if errors:
for error in errors:
print(f"FAIL: {error}", file=sys.stderr)
return 1
print(f"OK: SkillOpt packet passed: {path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())