@andespindola/crow-skills 0.40.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,839 @@
1
+ #!/usr/bin/env python3
2
+ """Explicit isolated native-Claude Skill Creator runtime; never a Codex equivalence claim."""
3
+ import argparse
4
+ import contextlib
5
+ import hashlib
6
+ import fcntl
7
+ import json
8
+ import math
9
+ import os
10
+ from pathlib import Path, PurePosixPath
11
+ import re
12
+ import runpy
13
+ from datetime import datetime, timezone
14
+ import selectors
15
+ import shutil
16
+ import signal
17
+ import stat
18
+ import subprocess
19
+ import sys
20
+ import tempfile
21
+ import time
22
+ import threading
23
+ import uuid
24
+
25
+ SCHEMA = 'crow-maker-creator/1'
26
+ MANIFEST_SHA256 = '002bd425fe66fba00460e4b853cf2ce9955877ca309528f98cac00973195a023'
27
+ COMMIT = '2c5958ecac7cee2b583967a2c4981d0f4a75c579'
28
+ DEPENDENCIES = ['PyYAML==6.0.3']
29
+ SCRIPTS = {name: 'scripts/' + name + '.py' for name in (
30
+ 'run_eval', 'run_loop', 'improve_description', 'quick_validate',
31
+ 'package_skill', 'aggregate_benchmark', 'generate_report')}
32
+ SCRIPTS['generate_review'] = 'eval-viewer/generate_review.py'
33
+ VENDOR = Path(__file__).absolute().parent.parent / 'vendor'
34
+
35
+ class CreatorError(ValueError):
36
+ pass
37
+
38
+ def _hash(raw):
39
+ return hashlib.sha256(raw).hexdigest()
40
+
41
+ def _path(path, missing=False):
42
+ p = Path(os.path.abspath(os.fspath(path)))
43
+ for part in [*reversed(p.parents), p]:
44
+ try:
45
+ info = part.lstat()
46
+ except FileNotFoundError:
47
+ if missing:
48
+ continue
49
+ raise CreatorError('path-unavailable') from None
50
+ if stat.S_ISLNK(info.st_mode):
51
+ raise CreatorError('symlink-path-rejected')
52
+ return p
53
+
54
+ def _read(path, limit=16*1024*1024):
55
+ p = _path(path)
56
+ fd = os.open(p.anchor, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
57
+ try:
58
+ for i, part in enumerate(p.parts[1:]):
59
+ flags = os.O_RDONLY | os.O_NOFOLLOW | getattr(os, 'O_NONBLOCK', 0)
60
+ if i < len(p.parts[1:])-1:
61
+ flags |= os.O_DIRECTORY
62
+ child = os.open(part, flags, dir_fd=fd)
63
+ os.close(fd)
64
+ fd = child
65
+ if not stat.S_ISREG(os.fstat(fd).st_mode):
66
+ raise CreatorError('nonregular-file')
67
+ chunks, remaining = [], limit+1
68
+ while remaining:
69
+ chunk = os.read(fd, min(65536, remaining))
70
+ if not chunk:
71
+ break
72
+ chunks.append(chunk)
73
+ remaining -= len(chunk)
74
+ raw = b''.join(chunks)
75
+ if len(raw) > limit:
76
+ raise CreatorError('file-too-large')
77
+ return raw
78
+ finally:
79
+ os.close(fd)
80
+
81
+ def _private_directory(path, create=False):
82
+ p = _path(path, missing=create)
83
+ if create:
84
+ # Only caller-provided final directory: parents must already be safe.
85
+ _path(p.parent)
86
+ p.mkdir(mode=0o700, exist_ok=True)
87
+ info = p.stat()
88
+ if not p.is_dir() or info.st_uid != os.getuid() or stat.S_IMODE(info.st_mode) & 0o077:
89
+ raise CreatorError('directory-must-be-owned-and-private')
90
+ return p
91
+
92
+ def _write(path, raw, mode=0o600):
93
+ p = _path(path, missing=True)
94
+ fd = os.open(p.anchor,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW)
95
+ try:
96
+ for part in p.parts[1:-1]:
97
+ child=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=fd)
98
+ os.close(fd);fd=child
99
+ out=os.open(p.name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,mode,dir_fd=fd)
100
+ with os.fdopen(out,'wb') as stream:
101
+ stream.write(raw)
102
+ finally:
103
+ os.close(fd)
104
+
105
+
106
+ def _donor_files():
107
+ raw = _read(VENDOR / 'vendor-manifest.json')
108
+ if _hash(raw) != MANIFEST_SHA256:
109
+ raise CreatorError('manifest-hash-mismatch')
110
+ manifest = json.loads(raw)
111
+ prefix = 'skills/crow-skill-maker/vendor/skill-creator/'
112
+ files = {}
113
+ for entry in manifest['files']:
114
+ copied = entry['copiedAs']
115
+ if not copied.startswith(prefix):
116
+ continue
117
+ relative = copied[len(prefix):]
118
+ parts = PurePosixPath(relative).parts
119
+ if not parts or '..' in parts or PurePosixPath(relative).is_absolute() or '\\' in relative:
120
+ raise CreatorError('manifest-path-escape')
121
+ if entry['repository'] != 'anthropics/skills' or entry['commit'] != COMMIT or relative in files:
122
+ raise CreatorError('unexpected-source-identity')
123
+ raw = _read(VENDOR / 'skill-creator' / relative)
124
+ if _hash(raw) != entry['upstreamSha256']:
125
+ raise CreatorError('donor-hash-mismatch')
126
+ files[relative] = raw
127
+ expected = set(files)
128
+ actual = set()
129
+ for directory, dirs, names in os.walk(_path(VENDOR / 'skill-creator'), followlinks=False):
130
+ for name in dirs + names:
131
+ path = Path(directory) / name
132
+ if path.is_symlink():
133
+ raise CreatorError('donor-symlink')
134
+ for name in names:
135
+ actual.add((Path(directory)/name).relative_to(VENDOR/'skill-creator').as_posix())
136
+ if actual != expected or not all(v in files for v in SCRIPTS.values()) or 'SKILL.md.source' not in files:
137
+ raise CreatorError('donor-file-set-mismatch')
138
+ return files
139
+
140
+ def _kill_group_impl(process):
141
+ try:
142
+ os.killpg(process.pid, signal.SIGTERM)
143
+ except ProcessLookupError:
144
+ pass
145
+ try:
146
+ process.wait(timeout=.5)
147
+ except subprocess.TimeoutExpired:
148
+ pass
149
+ # Leader may have exited while descendants remain; still signal its group.
150
+ try:
151
+ os.killpg(process.pid, signal.SIGKILL)
152
+ except ProcessLookupError:
153
+ pass
154
+ try:
155
+ process.wait(timeout=2)
156
+ except subprocess.TimeoutExpired:
157
+ pass
158
+
159
+ class _Cancellation(KeyboardInterrupt):
160
+ def __init__(self,signum):
161
+ self.signum=signum
162
+
163
+ class _SignalGuard:
164
+ def __init__(self):
165
+ self.depth=0;self.pending=None
166
+ def handler(self,signum,frame):
167
+ if self.depth:
168
+ if self.pending is None:self.pending=signum
169
+ else:raise _Cancellation(signum)
170
+ @contextlib.contextmanager
171
+ def deferred(self):
172
+ self.depth+=1
173
+ try:yield
174
+ finally:
175
+ self.depth-=1
176
+ if not self.depth and self.pending is not None:
177
+ signum,self.pending=self.pending,None
178
+ raise _Cancellation(signum)
179
+
180
+ @contextlib.contextmanager
181
+ def _cancellation_scope():
182
+ guard=_SignalGuard()
183
+ installed=threading.current_thread() is threading.main_thread()
184
+ previous={sig:signal.getsignal(sig) for sig in (signal.SIGTERM,signal.SIGINT)} if installed else {}
185
+ if installed:
186
+ for sig in previous:signal.signal(sig,guard.handler)
187
+ try:yield guard
188
+ finally:
189
+ for sig,handler in previous.items():signal.signal(sig,handler)
190
+
191
+ def _kill_group(process):
192
+ # Parent-only handler deferral cannot leak a blocked mask into exec. A
193
+ # second cancellation is delivered only AFTER TERM -> grace -> KILL ends.
194
+ with _cancellation_scope() as guard:
195
+ with guard.deferred():
196
+ _kill_group_impl(process)
197
+
198
+ def _spawn_owned(command,**kwargs):
199
+ process=None
200
+ try:
201
+ with _cancellation_scope() as guard:
202
+ with guard.deferred():
203
+ process=subprocess.Popen(command,start_new_session=True,**kwargs)
204
+ return process
205
+ except BaseException:
206
+ if process is not None:
207
+ try:_kill_group(process)
208
+ except _Cancellation:pass # Preserve original cancellation after cleanup.
209
+ raise
210
+
211
+
212
+ def _bounded_setup(command, timeout=300):
213
+ with _cancellation_scope():
214
+ process=None
215
+ try:
216
+ process=_spawn_owned(command,stdin=subprocess.DEVNULL,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
217
+ code = process.wait(timeout=timeout)
218
+ if code:raise CreatorError('dependency-setup-failed')
219
+ except _Cancellation:
220
+ raise
221
+ except KeyboardInterrupt:
222
+ raise _Cancellation(signal.SIGINT) from None
223
+ except subprocess.TimeoutExpired:
224
+ raise CreatorError('dependency-setup-interrupted') from None
225
+ finally:
226
+ if process is not None:_kill_group(process)
227
+
228
+
229
+ def _outside_native_host(path):
230
+ path = _path(path,missing=True)
231
+ if path == Path.home():
232
+ raise CreatorError('native-home-runtime-forbidden')
233
+ for forbidden in (Path.home()/'.claude',Path.home()/'.codex',Path.home()/'.agents',Path.home()/'.config/devin'):
234
+ if path == forbidden or forbidden in path.parents:
235
+ raise CreatorError('native-host-runtime-forbidden')
236
+ return path
237
+
238
+ def setup(runtime_root, yes=False):
239
+ if not yes:
240
+ raise CreatorError('explicit-setup-yes-required')
241
+ if sys.version_info < (3,10):
242
+ raise CreatorError('native-donor-requires-python-3.10-or-later')
243
+ files = _donor_files() # Verify everything BEFORE any write.
244
+ root = _private_directory(_outside_native_host(runtime_root), create=True)
245
+ if any(root.iterdir()):
246
+ raise CreatorError('runtime-root-must-be-empty-no-overwrite')
247
+ # A failed setup remains visibly partial, never ready or implicitly repaired.
248
+ _write(root/'setup-state.json', json.dumps({'status':'partial','dependencies':DEPENDENCIES}).encode())
249
+ creator = root/'skill-creator'
250
+ creator.mkdir(mode=0o700)
251
+ for relative, raw in files.items():
252
+ destination = creator / ('SKILL.md' if relative == 'SKILL.md.source' else relative)
253
+ destination.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
254
+ _write(destination, raw)
255
+ _bounded_setup([sys.executable, '-m', 'venv', str(root/'venv')])
256
+ python = root/'venv/bin/python'
257
+ _bounded_setup([str(python), '-m', 'pip', 'install', '--disable-pip-version-check', *DEPENDENCIES])
258
+ _write(root/'ready.json', json.dumps({'schema':SCHEMA,'commit':COMMIT,'manifestSha256':MANIFEST_SHA256,
259
+ 'dependencies':DEPENDENCIES,'pythonVersion':sys.version.split()[0],
260
+ 'files':{('SKILL.md' if k=='SKILL.md.source' else k):_hash(v) for k,v in files.items()}}).encode())
261
+ return status(root)
262
+
263
+ def status(runtime_root):
264
+ root = _private_directory(_outside_native_host(runtime_root))
265
+ ready = root/'ready.json'
266
+ if not ready.exists():
267
+ return {'schema':SCHEMA,'status':'partial' if any(root.iterdir()) else 'not-installed','exitCode':1}
268
+ meta = json.loads(_read(ready, 1024*1024))
269
+ files = _donor_files()
270
+ expected = {('SKILL.md' if k=='SKILL.md.source' else k):_hash(v) for k,v in files.items()}
271
+ if meta.get('manifestSha256') != MANIFEST_SHA256 or meta.get('commit') != COMMIT or meta.get('files') != expected or meta.get('dependencies') != DEPENDENCIES:
272
+ raise CreatorError('runtime-metadata-mismatch')
273
+ for relative, digest in expected.items():
274
+ if _hash(_read(root/'skill-creator'/relative)) != digest:
275
+ raise CreatorError('runtime-source-hash-mismatch')
276
+ # venv's interpreter symlink is created by venv itself, not donor-controlled.
277
+ if not (root/'venv/bin/python').is_file():
278
+ raise CreatorError('runtime-python-missing')
279
+ return {'schema':SCHEMA,'status':'ready','exitCode':0,'dependencies':DEPENDENCIES,
280
+ 'nativeHost':'Claude Code CLI','scripts':sorted(SCRIPTS),
281
+ 'limitations':['Native Claude trigger evaluation, not Codex host equivalence.','Runtime and workspace are isolation conventions, not a sandbox.']}
282
+
283
+ def _option(args, name):
284
+ for i, arg in enumerate(args):
285
+ if arg == name:
286
+ if i+1 >= len(args):
287
+ raise CreatorError('missing-option-value')
288
+ return args[i+1]
289
+ if arg.startswith(name+'='):
290
+ return arg.split('=',1)[1]
291
+ return None
292
+
293
+ def _arguments(script, arguments):
294
+ args = list(arguments)
295
+ if script in ('run_eval','run_loop') and _option(args,'--num-workers') is None:
296
+ args += ['--num-workers','1']
297
+ if script == 'run_loop' and _option(args,'--report') is None:
298
+ args += ['--report','none']
299
+ return args
300
+
301
+ def _guard_skill(arguments, workspace):
302
+ value = _option(arguments,'--skill-path')
303
+ if value is None:
304
+ return
305
+ path = Path(value)
306
+ if not path.is_absolute():
307
+ path = workspace/path
308
+ raw = _read(_path(path)/'SKILL.md').decode('utf-8')
309
+ lines = raw.split('\n')
310
+ if not lines or lines[0].strip() != '---':
311
+ raise CreatorError('unsafe-native-skill-name')
312
+ end = next((i for i in range(1,len(lines)) if lines[i].strip()=='---'),None)
313
+ names = [line[len('name:'):].strip().strip('"').strip("'")
314
+ for line in lines[1:end] if line.startswith('name:')] if end is not None else []
315
+ if len(names) != 1 or not re.fullmatch(r'[a-z0-9][a-z0-9-]*',names[0]):
316
+ raise CreatorError('unsafe-native-skill-name')
317
+
318
+
319
+ def _guard_io(script,args,work):
320
+ def checked(value):
321
+ p=Path(value)
322
+ return _path(p if p.is_absolute() else work/p,missing=True)
323
+ for option in ('--eval-set','--skill-path','--eval-results','--history','--results-dir',
324
+ '--output','-o','--previous-workspace','--benchmark','--static','-s','--report'):
325
+ value=_option(args,option)
326
+ if value and not (option=='--report' and value in ('none','auto')):
327
+ checked(value)
328
+ if not args or args[0].startswith('-'):
329
+ return
330
+ if script in ('package_skill','quick_validate','aggregate_benchmark','generate_report','generate_review') and args[0]!='-':
331
+ source=checked(args[0])
332
+ if script=='package_skill':
333
+ output=checked(args[1]) if len(args)>1 else work
334
+ _path(output/(source.name+'.skill'),missing=True)
335
+ if source.is_dir():
336
+ for directory,dirs,files in os.walk(source,followlinks=False):
337
+ for name in dirs+files:
338
+ if (Path(directory)/name).is_symlink():
339
+ raise CreatorError('package-source-symlink-rejected')
340
+ if script=='aggregate_benchmark':
341
+ output=_option(args,'--output') or _option(args,'-o')
342
+ target=checked(output) if output else source/'benchmark.json'
343
+ _path(target,missing=True);_path(target.with_suffix('.md'),missing=True)
344
+ if script=='generate_review':
345
+ _path(source/'feedback.json',missing=True)
346
+
347
+ def _observed(value, integer=False):
348
+ try:
349
+ if type(value) not in (int,float) or not math.isfinite(value) or value < 0:
350
+ return None
351
+ except OverflowError:
352
+ return None
353
+ return value if not integer or type(value) is int else None
354
+
355
+ def _benchmark_stats(values):
356
+ # A partial mean would silently drop missing runs. Keep the whole statistic
357
+ # unknown; coverage separately records exactly how many metrics were observed.
358
+ if not values or any(value is None for value in values):
359
+ return None
360
+ try:
361
+ mean=sum(values)/len(values)
362
+ variance=sum((x-mean)**2 for x in values)/(len(values)-1) if len(values)>1 else 0
363
+ result={'mean':round(mean,4),'stddev':round(math.sqrt(variance),4),
364
+ 'min':min(values),'max':max(values),'n':len(values)}
365
+ return result if all(math.isfinite(value) for value in result.values()) else None
366
+ except (OverflowError,ValueError):
367
+ return None
368
+
369
+ def corrected_benchmark(directory,skill_name='',skill_path=''):
370
+ """Creator-owned adapter. Retains donor artifact shape, not its invented metrics."""
371
+ directory=Path(directory)
372
+ search=directory/'runs' if (directory/'runs').is_dir() else directory
373
+ rows=[];issues=[]
374
+ def data(path,required=False):
375
+ if not path.exists():
376
+ if required:issues.append('missing-grading-file')
377
+ return {}
378
+ try:
379
+ item=json.loads(_read(path))
380
+ if not isinstance(item,dict):raise ValueError()
381
+ return item
382
+ except (CreatorError,OSError,ValueError):
383
+ issues.append('unreadable-or-invalid-artifact')
384
+ return {}
385
+ for index,eval_dir in enumerate(sorted(search.glob('eval-*'))):
386
+ if not eval_dir.is_dir():continue
387
+ metadata=data(eval_dir/'eval_metadata.json')
388
+ fallback=int(eval_dir.name[5:]) if eval_dir.name[5:].isdigit() else index
389
+ eval_id=metadata.get('eval_id',fallback)
390
+ if type(eval_id) is not int:eval_id=fallback
391
+ for config in sorted(eval_dir.iterdir()):
392
+ if not config.is_dir():continue
393
+ for run_dir in sorted(config.glob('run-*')):
394
+ if not run_dir.is_dir():continue
395
+ grading=data(run_dir/'grading.json',True)
396
+ timing=data(run_dir/'timing.json')
397
+ grade_timing=grading.get('timing',{})
398
+ grade_timing=grade_timing if isinstance(grade_timing,dict) else {}
399
+ metrics=grading.get('execution_metrics',{})
400
+ metrics=metrics if isinstance(metrics,dict) else {}
401
+ summary=grading.get('summary',{})
402
+ summary=summary if isinstance(summary,dict) else {}
403
+ elapsed=_observed(grade_timing.get('total_duration_seconds'))
404
+ if elapsed is None:elapsed=_observed(timing.get('total_duration_seconds'))
405
+ tokens=_observed(timing.get('total_tokens'),True)
406
+ if tokens is None:tokens=_observed(metrics.get('total_tokens'),True)
407
+ rate=_observed(summary.get('pass_rate'))
408
+ if rate is not None and rate>1:rate=None
409
+ result={'pass_rate':rate,'passed':_observed(summary.get('passed'),True),
410
+ 'failed':_observed(summary.get('failed'),True),'total':_observed(summary.get('total'),True),
411
+ 'time_seconds':elapsed,'tokens':tokens,
412
+ 'output_chars':_observed(metrics.get('output_chars'),True),
413
+ 'tool_calls':_observed(metrics.get('total_tool_calls'),True),
414
+ 'errors':_observed(metrics.get('errors_encountered'),True)}
415
+ number=run_dir.name[4:]
416
+ rows.append({'eval_id':eval_id,'configuration':config.name,
417
+ 'run_number':int(number) if number.isdigit() else run_dir.name,'result':result,
418
+ 'expectations':grading.get('expectations',[]),'notes':[]})
419
+ configs=sorted({row['configuration'] for row in rows})
420
+ evals=sorted({row['eval_id'] for row in rows})
421
+ counts={config:sum(row['configuration']==config for row in rows) for config in configs}
422
+ cells={str(e):{config:sum(row['eval_id']==e and row['configuration']==config for row in rows)
423
+ for config in configs} for e in evals}
424
+ cell_values=[count for item in cells.values() for count in item.values()]
425
+ uniform=cell_values[0] if cell_values and len(set(cell_values))==1 else None
426
+ summary={};coverage={}
427
+ for config in configs:
428
+ selected=[row for row in rows if row['configuration']==config]
429
+ summary[config]={};coverage[config]={}
430
+ for metric in ('pass_rate','time_seconds','tokens','output_chars'):
431
+ values=[row['result'][metric] for row in selected]
432
+ summary[config][metric]=_benchmark_stats(values)
433
+ observed=sum(value is not None for value in values)
434
+ coverage[config][metric]={'observed':observed,'missing':len(values)-observed}
435
+ delta={}
436
+ for metric,digits in (('pass_rate',2),('time_seconds',1),('tokens',0),('output_chars',0)):
437
+ a=summary[configs[0]][metric] if configs else None
438
+ b=summary[configs[1]][metric] if len(configs)>1 else None
439
+ delta[metric]=format(a['mean']-b['mean'],'+.'+str(digits)+'f') if a and b else 'unknown'
440
+ summary['delta']=delta
441
+ complete=bool(rows) and not issues and all(counts['missing']==0 for item in coverage.values()
442
+ for metric,counts in item.items() if metric in ('pass_rate','time_seconds','tokens')) and all(
443
+ summary[config][metric] is not None for config in configs for metric in ('pass_rate','time_seconds','tokens'))
444
+ return {'metadata':{'skill_name':skill_name or '<skill-name>','skill_path':skill_path or '<path/to/skill>',
445
+ 'executor_model':None,'analyzer_model':None,'timestamp':datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'),
446
+ 'evals_run':evals,'runs_per_configuration':uniform,'observed_run_counts':counts,
447
+ 'observed_counts_by_eval':cells,'adapter':'crow-creator-truthful-benchmark/1'},
448
+ 'runs':rows,'run_summary':summary,'coverage':{'complete':complete,'metrics':coverage,'issues':issues},
449
+ 'notes':['Creator-owned corrected aggregation; donor source remains unchanged.',
450
+ 'Unknown metrics are null, never zero. Output characters are not tokens.',
451
+ 'Supplied grading and token fields are input evidence, not independent behavior/provider attestations.']}
452
+
453
+ def corrected_benchmark_markdown(benchmark):
454
+ metadata=benchmark['metadata'];summary=benchmark['run_summary']
455
+ configs=[key for key in summary if key!='delta']
456
+ lines=['# Skill Benchmark: '+metadata['skill_name'],'',
457
+ '**Model**: unknown (not inferred from artifacts)',
458
+ '**Date**: '+metadata['timestamp'],
459
+ '**Observed runs**: '+('; '.join(key+': '+str(value) for key,value in metadata['observed_run_counts'].items()) or 'none'),
460
+ '**Coverage**: '+('complete for reported metrics' if benchmark['coverage']['complete'] else 'incomplete; missing metrics remain unknown'),'',
461
+ '| Metric | '+' | '.join(key.replace('_',' ').title() for key in configs)+' | Delta |',
462
+ '|---|'+'---|'*(len(configs)+1)]
463
+ for metric,label in (('pass_rate','Pass Rate'),('time_seconds','Time (s)'),('tokens','Tokens'),('output_chars','Output Characters')):
464
+ values=[]
465
+ for config in configs:
466
+ item=summary[config][metric]
467
+ if item is None:values.append('unknown')
468
+ elif metric=='pass_rate':values.append(format(item['mean']*100,'.0f')+'% (n='+str(item['n'])+')')
469
+ else:values.append(format(item['mean'],'.4g')+' (n='+str(item['n'])+')')
470
+ lines.append('| '+label+' | '+' | '.join(values)+' | '+summary['delta'][metric]+' |')
471
+ lines += ['','## Notes','']+['- '+note for note in benchmark['notes']]
472
+ return '\n'.join(lines)
473
+
474
+ def _aggregate_entry(target,arguments):
475
+ namespace=runpy.run_path(target)
476
+ namespace['main'].__globals__.update(generate_benchmark=corrected_benchmark,
477
+ generate_markdown=corrected_benchmark_markdown)
478
+ sys.argv=[target,*arguments]
479
+ namespace['main']()
480
+ return 0
481
+
482
+ def _truthful_viewer_html(html):
483
+ """Patch only reviewed pinned renderer snippets, leaving all viewer features."""
484
+ replacements = [
485
+ (' function renderBenchmark() {', ''' function renderBenchmark() {
486
+ // Crow adapter: missing observations stay unknown through rendering.
487
+ const crowKnown = v => typeof v === "number" && Number.isFinite(v) && v >= 0;
488
+ const crowMean = values => values.length && values.every(crowKnown)
489
+ ? values.reduce((a,b) => a+b, 0) / values.length : null;
490
+ const crowMeanText = (values, percent=false) => {
491
+ const mean = crowMean(values);
492
+ return mean == null || !Number.isFinite(mean) ? "unknown (incomplete)"
493
+ : (mean * (percent ? 100 : 1)).toFixed(percent ? 0 : 1) + (percent ? "%" : "");
494
+ };
495
+ '''),
496
+ ('if (!stat) return "—";', 'if (!stat || !crowKnown(stat.mean) || !crowKnown(stat.stddev)) return "unknown";'),
497
+ ('(metadata.runs_per_configuration || "?") + " runs per configuration"',
498
+ '(metadata.observed_run_counts ? escapeHtml(Object.entries(metadata.observed_run_counts).map(([k,v]) => k + ": " + v).join("; ")) + " observed runs" : (metadata.runs_per_configuration ?? "unknown") + " runs per configuration")'),
499
+ ('if (a.time_seconds || b.time_seconds) {', 'if ("time_seconds" in a || "time_seconds" in b) {'),
500
+ ('if (a.tokens || b.tokens) {', 'if ("tokens" in a || "tokens" in b) {'),
501
+ ('(delta.time_seconds ? delta.time_seconds + "s" : "—")',
502
+ '(delta.time_seconds != null && Number.isFinite(Number(delta.time_seconds)) ? delta.time_seconds + "s" : "unknown")'),
503
+ ('const hasTime = runs.some(r => r.result && r.result.time_seconds != null);',
504
+ 'const hasTime = runs.some(r => r.result && "time_seconds" in r.result);'),
505
+ ('const hasErrors = runs.some(r => r.result && r.result.errors > 0);',
506
+ 'const hasErrors = runs.some(r => r.result && "errors" in r.result);'),
507
+ ('const prClass = r.pass_rate >= 0.8 ? "benchmark-delta-positive" : r.pass_rate < 0.5 ? "benchmark-delta-negative" : "";',
508
+ 'const prClass = crowKnown(r.pass_rate) ? (r.pass_rate >= 0.8 ? "benchmark-delta-positive" : r.pass_rate < 0.5 ? "benchmark-delta-negative" : "") : "";'),
509
+ ('((r.pass_rate || 0) * 100).toFixed(0) + "% (" + (r.passed || 0) + "/" + (r.total || 0) + ")',
510
+ '(crowKnown(r.pass_rate) ? (r.pass_rate * 100).toFixed(0) + "%" : "unknown") + " (" + (crowKnown(r.passed) ? r.passed : "unknown") + "/" + (crowKnown(r.total) ? r.total : "unknown") + ")'),
511
+ ('(r.time_seconds != null ? r.time_seconds.toFixed(1) : "—")',
512
+ '(crowKnown(r.time_seconds) ? r.time_seconds.toFixed(1) : "unknown")'),
513
+ ('(r.errors || 0)', '(crowKnown(r.errors) ? r.errors : "unknown")'),
514
+ ('const rates = configRuns.map(r => (r.result || {}).pass_rate || 0);',
515
+ 'const rates = configRuns.map(r => (r.result || {}).pass_rate);'),
516
+ ('const avgRate = rates.reduce((a, b) => a + b, 0) / rates.length;', 'const avgRate = crowMean(rates);'),
517
+ ('const avgPrClass = avgRate >= 0.8 ? "benchmark-delta-positive" : avgRate < 0.5 ? "benchmark-delta-negative" : "";',
518
+ 'const avgPrClass = avgRate == null ? "" : (avgRate >= 0.8 ? "benchmark-delta-positive" : avgRate < 0.5 ? "benchmark-delta-negative" : "");'),
519
+ ('(avgRate * 100).toFixed(0) + "%', 'crowMeanText(rates, true) + "'),
520
+ ('const times = configRuns.map(r => (r.result || {}).time_seconds).filter(t => t != null);',
521
+ 'const times = configRuns.map(r => (r.result || {}).time_seconds);'),
522
+ ('(times.length ? (times.reduce((a, b) => a + b, 0) / times.length).toFixed(1) : "—")',
523
+ 'crowMeanText(times)'),
524
+ ('const cls = exp.passed ? "benchmark-delta-positive" : "benchmark-delta-negative";',
525
+ 'const cls = exp.passed === true ? "benchmark-delta-positive" : exp.passed === false ? "benchmark-delta-negative" : "";'),
526
+ ('const icon = exp.passed ? "\\u2713" : "\\u2717";',
527
+ 'const icon = exp.passed === true ? "\\u2713" : exp.passed === false ? "\\u2717" : "unknown";'),
528
+ ]
529
+ for original, replacement in replacements:
530
+ if html.count(original) != 1:
531
+ raise CreatorError('pinned-viewer-adapter-target-changed')
532
+ html=html.replace(original,replacement,1)
533
+ return html
534
+
535
+ def _viewer_namespace(target):
536
+ namespace=runpy.run_path(target)
537
+ globals_=namespace['main'].__globals__
538
+ original=globals_['generate_html']
539
+ def render(*args,**kwargs):
540
+ return _truthful_viewer_html(original(*args,**kwargs))
541
+ # Static main and served ReviewHandler share these module globals.
542
+ globals_.update(generate_html=render,_kill_port=lambda port:None)
543
+ return namespace
544
+
545
+ def _viewer_entry(target,arguments):
546
+ namespace=_viewer_namespace(target)
547
+ sys.argv=[target,*arguments]
548
+ namespace['main']()
549
+ return 0
550
+
551
+ def _event(path, value):
552
+ # Readers only glob final event suffixes; publish fully written bytes once.
553
+ temp=path.with_name('.event-'+uuid.uuid4().hex)
554
+ try:
555
+ _write(temp,json.dumps(value).encode())
556
+ os.replace(temp,path)
557
+ finally:
558
+ if temp.exists():temp.unlink()
559
+
560
+ def _traces(directory):
561
+ starts = list(directory.glob('*.start'))
562
+ done, failures, early, invalid, protocol_failures, protocol_incomplete = 0, 0, 0, 0, 0, 0
563
+ for start in starts:
564
+ path = start.with_suffix('.done')
565
+ if path.exists():
566
+ try:
567
+ item = json.loads(_read(path,4096))
568
+ if not isinstance(item,dict) or not (type(item.get('returncode')) is int or
569
+ (item.get('returncode') is None and isinstance(item.get('error'),str) and item['error'])):
570
+ raise CreatorError('invalid-final-trace')
571
+ except (OSError,CreatorError,ValueError):
572
+ invalid += 1
573
+ continue
574
+ done += 1
575
+ failures += item.get('returncode') != 0 or bool(item.get('error'))
576
+ early += bool(item.get('consumerClosedEarly'))
577
+ protocol=item.get('protocol',{})
578
+ if isinstance(protocol,dict) and protocol.get('status')=='error':protocol_failures+=1
579
+ elif not isinstance(protocol,dict) or protocol.get('status')!='success':protocol_incomplete+=1
580
+ return {'started':len(starts),'completed':done,'cliFailures':failures,'pending':len(starts)-done,
581
+ 'actualStarted':len(list(directory.glob('*.running'))),'earlyConsumerClosed':early,'invalidFinalEvents':invalid,'protocolFailures':protocol_failures,'protocolIncomplete':protocol_incomplete}
582
+
583
+ def _acquire_native_slot(directory, workers):
584
+ # Locks are scoped to this run and held until the REAL CLI exits, not until
585
+ # its disposable donor-facing proxy is deliberately killed on early output.
586
+ while True:
587
+ for index in range(workers):
588
+ fd=os.open(Path(directory)/('slot-'+str(index)),os.O_RDWR|os.O_CREAT|os.O_NOFOLLOW,0o600)
589
+ try:
590
+ fcntl.flock(fd,fcntl.LOCK_EX|fcntl.LOCK_NB)
591
+ return fd
592
+ except BlockingIOError:
593
+ os.close(fd)
594
+ time.sleep(.025)
595
+
596
+ class _ProtocolEvidence:
597
+ """Bounded stream evidence, not an OS-exit-code surrogate or behavior grader."""
598
+ def __init__(self,format_):
599
+ self.format=format_;self.buffer=b'';self.discarding=False
600
+ self.malformed=0;self.errors=0;self.successes=0;self.nonempty=False
601
+ self.max_line=4*1024*1024
602
+ def _line(self,line):
603
+ if not line.strip():return
604
+ if len(line)>self.max_line:
605
+ self.malformed+=1;return
606
+ try:
607
+ def invalid_constant(_):raise ValueError('non-json-number')
608
+ event=json.loads(line,parse_constant=invalid_constant)
609
+ except (ValueError,UnicodeError,RecursionError):
610
+ self.malformed+=1;return
611
+ if not isinstance(event,dict) or not isinstance(event.get('type'),str):
612
+ self.malformed+=1;return
613
+ if event['type']=='error':self.errors+=1
614
+ if event['type']=='result':
615
+ if event.get('is_error') is True or str(event.get('subtype','')).startswith('error') or event.get('errors'):
616
+ self.errors+=1
617
+ elif event.get('subtype')=='success' and event.get('is_error') is False:
618
+ self.successes+=1
619
+ def feed(self,raw):
620
+ self.nonempty=self.nonempty or bool(raw.strip())
621
+ if self.format!='stream-json':return
622
+ if self.discarding:
623
+ boundary=raw.find(b'\n')
624
+ if boundary<0:return
625
+ raw=raw[boundary+1:];self.discarding=False
626
+ self.buffer+=raw
627
+ while b'\n' in self.buffer:
628
+ line,_,self.buffer=self.buffer.partition(b'\n');self._line(line)
629
+ if len(self.buffer)>self.max_line:
630
+ self.malformed+=1;self.buffer=b'';self.discarding=True
631
+ def finish(self):
632
+ if self.format=='stream-json':
633
+ if self.buffer.strip():self._line(self.buffer)
634
+ self.buffer=b''
635
+ state='error' if self.errors or self.malformed or self.successes>1 else 'success' if self.successes==1 else 'incomplete'
636
+ elif self.format=='text':state='success' if self.nonempty else 'incomplete'
637
+ else:state='incomplete'
638
+ return {'format':self.format,'status':state,'terminalSuccesses':self.successes,
639
+ 'errorEvents':self.errors,'malformedEvents':self.malformed,'nonemptyText':self.nonempty}
640
+
641
+ def _supervise(real, trace):
642
+ token = uuid.uuid4().hex
643
+ start, done = Path(trace)/(token+'.start'), Path(trace)/(token+'.done')
644
+ _event(start,{})
645
+ result = {'returncode':None,'consumerClosedEarly':False}
646
+ protocol=_ProtocolEvidence(_option(sys.argv[4:],'--output-format') or 'text')
647
+ slot = None
648
+ try:
649
+ slot = _acquire_native_slot(os.environ['CROW_CREATOR_SLOTS'],int(os.environ['CROW_CREATOR_WORKERS']))
650
+ _event(Path(trace)/(token+'.running'),{})
651
+ process = subprocess.Popen([real,*sys.argv[4:]], stdout=subprocess.PIPE)
652
+ forwarding = True
653
+ while True:
654
+ raw = os.read(process.stdout.fileno(),65536)
655
+ if not raw:
656
+ break
657
+ protocol.feed(raw)
658
+ if forwarding:
659
+ try:
660
+ offset = 0
661
+ while offset < len(raw):
662
+ offset += os.write(1,raw[offset:])
663
+ except BrokenPipeError:
664
+ forwarding = False # Native donor intentionally stops early.
665
+ result['consumerClosedEarly'] = True
666
+ result['returncode'] = process.wait()
667
+ except (OSError, KeyboardInterrupt):
668
+ result['error'] = 'cli-start-or-supervisor-failure'
669
+ finally:
670
+ result['protocol']=protocol.finish()
671
+ _event(done,result)
672
+ if slot is not None:
673
+ os.close(slot)
674
+ return result['returncode'] if result['returncode'] is not None else 125
675
+
676
+ def _proxy():
677
+ real, trace = os.environ['CROW_CREATOR_REAL_CLAUDE'], os.environ['CROW_CREATOR_TRACE']
678
+ # Supervisor survives donor's deliberate kill of this proxy; caller owns group.
679
+ process = subprocess.Popen([sys.executable, str(Path(__file__).absolute()), '__supervise',real,trace,*sys.argv[2:]])
680
+ return process.wait()
681
+
682
+ def run(script, runtime_root, workspace, timeout, arguments=(), stdio=False, interactive=False):
683
+ if script not in SCRIPTS or type(timeout) not in (int,float) or not math.isfinite(timeout) or timeout <= 0:
684
+ raise CreatorError('invalid-script-or-timeout')
685
+ root = _private_directory(runtime_root)
686
+ if status(root)['status'] != 'ready':
687
+ raise CreatorError('runtime-not-ready-explicit-setup-required')
688
+ work = _private_directory(workspace, create=True)
689
+ if work == Path.home() or work == root or root in work.parents or work in root.parents:
690
+ raise CreatorError('workspace-must-be-separate')
691
+ for forbidden in (Path.home()/'.claude',Path.home()/'.codex',Path.home()/'.agents'):
692
+ if work == forbidden or forbidden in work.parents:
693
+ raise CreatorError('native-host-workspace-forbidden')
694
+ local = work/'.claude'
695
+ _private_directory(local, create=True)
696
+ commands=_private_directory(local/'commands', create=True)
697
+ if any(p.name!='commands' for p in local.iterdir()) or any(commands.iterdir()):
698
+ raise CreatorError('workspace-claude-must-be-unconfigured-and-empty')
699
+ arguments = _arguments(script,arguments)
700
+ _guard_io(script,arguments,work)
701
+ if script in ('run_eval','run_loop','improve_description'):
702
+ _guard_skill(arguments,work)
703
+ if script == 'generate_review' and not any(a in ('--help','-h') for a in arguments):
704
+ static_viewer = _option(arguments,'--static') or _option(arguments,'-s')
705
+ if not static_viewer and not interactive:
706
+ raise CreatorError('viewer-requires-static-or-explicit-interactive')
707
+ run_dir = Path(tempfile.mkdtemp(prefix='.creator-run-',dir=work))
708
+ run_dir.chmod(0o700)
709
+ trace = run_dir/'trace'; trace.mkdir(mode=0o700)
710
+ slots = run_dir/'slots'; slots.mkdir(mode=0o700)
711
+ try:
712
+ workers = int(_option(arguments,'--num-workers') or '1')
713
+ except ValueError:
714
+ raise CreatorError('num-workers-must-be-positive-integer') from None
715
+ if workers < 1:
716
+ raise CreatorError('num-workers-must-be-positive-integer')
717
+ bin_dir = run_dir/'bin'; bin_dir.mkdir(mode=0o700)
718
+ env = os.environ.copy()
719
+ env.pop('PYTHONHOME',None)
720
+ env['PYTHONPATH'] = str(root/'skill-creator')
721
+ env['PYTHONDONTWRITEBYTECODE'] = '1'
722
+ is_native = script in ('run_eval','run_loop','improve_description') and '--help' not in arguments and '-h' not in arguments
723
+ if is_native:
724
+ real = shutil.which('claude')
725
+ if not real:
726
+ raise CreatorError('claude-cli-not-found')
727
+ real = str(Path(real).resolve())
728
+ if env.get('CROW_CREATOR_PROXY') == '1' or real == str(bin_dir/'claude'):
729
+ raise CreatorError('recursive-claude-proxy')
730
+ shim = '#!'+str(root/'venv/bin/python')+'\nimport runpy,sys\nsys.argv=['+repr(str(Path(__file__).absolute()))+",'__proxy',*sys.argv[1:]]\nrunpy.run_path(sys.argv[0],run_name='__main__')\n"
731
+ _write(bin_dir/'claude',shim.encode(),0o700)
732
+ env.update(PATH=str(bin_dir)+os.pathsep+env.get('PATH',''), CROW_CREATOR_PROXY='1',
733
+ CROW_CREATOR_REAL_CLAUDE=real,CROW_CREATOR_TRACE=str(trace),
734
+ CROW_CREATOR_SLOTS=str(slots),CROW_CREATOR_WORKERS=str(workers))
735
+ target = root/'skill-creator'/SCRIPTS[script]
736
+ command = [str(root/'venv/bin/python'),str(target),*arguments]
737
+ if script == 'aggregate_benchmark':
738
+ command=[str(root/'venv/bin/python'),str(Path(__file__).absolute()),'__aggregate',str(target),*arguments]
739
+ if script == 'generate_review':
740
+ command=[str(root/'venv/bin/python'),str(Path(__file__).absolute()),'__viewer',str(target),*arguments]
741
+ stdout_path, stderr_path = run_dir/'stdout.log', run_dir/'stderr.log'
742
+ started = time.monotonic(); state = 'completed'; process = None
743
+ cancellation=None;selector=None
744
+ old_umask = os.umask(0o077)
745
+ try:
746
+ with _cancellation_scope():
747
+ try:
748
+ with os.fdopen(os.open(stdout_path,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600),'wb') as out, \
749
+ os.fdopen(os.open(stderr_path,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600),'wb') as err:
750
+ process=_spawn_owned(command,cwd=work,env=env,stdin=None if (stdio or interactive) else subprocess.DEVNULL,
751
+ stdout=subprocess.PIPE,stderr=subprocess.PIPE)
752
+ selector = selectors.DefaultSelector()
753
+ for pipe,stream,fd in ((process.stdout,out,1),(process.stderr,err,2)):
754
+ selector.register(pipe,selectors.EVENT_READ,(stream,fd))
755
+ while True:
756
+ if time.monotonic()-started >= timeout:
757
+ state = 'timeout'; break
758
+ for key,_ in selector.select(.05):
759
+ raw = os.read(key.fileobj.fileno(),65536)
760
+ if not raw:
761
+ selector.unregister(key.fileobj);continue
762
+ stream,fd = key.data;stream.write(raw);stream.flush()
763
+ if stdio or interactive:
764
+ try:os.write(fd,raw)
765
+ except BrokenPipeError:pass
766
+ if process.poll() is not None and not _traces(trace)['pending'] and not selector.get_map():
767
+ break
768
+ except KeyboardInterrupt as interruption:
769
+ state='interrupted'
770
+ cancellation=interruption.signum if isinstance(interruption,_Cancellation) else signal.SIGINT
771
+ finally:
772
+ if process is not None:
773
+ try:_kill_group(process)
774
+ except _Cancellation as late:
775
+ state='interrupted';cancellation=cancellation or late.signum
776
+ finally:
777
+ process.stdout.close();process.stderr.close()
778
+ if selector is not None:selector.close()
779
+ finally:
780
+ os.umask(old_umask)
781
+ traces = _traces(trace)
782
+ code = process.returncode if process is not None else None
783
+ if state == 'completed':
784
+ if code or traces['cliFailures'] or traces.get('protocolFailures',0):state='operational-failure'
785
+ elif traces['pending'] or traces.get('protocolIncomplete',0) or (is_native and traces['started']==0):state='incomplete-cli-evidence'
786
+ result = {'schema':SCHEMA,'status':state,'exitCode':128+cancellation if cancellation else 0 if state=='completed' else 1,
787
+ 'timedOut':state=='timeout','cancellationSignal':signal.Signals(cancellation).name if cancellation else None,
788
+ 'executionKind':'help-only' if '--help' in arguments or '-h' in arguments else ('native-Claude' if is_native else 'offline-utility'),
789
+ 'upstreamExitCode':code,'nativeHost':'Claude Code CLI' if is_native else 'offline-upstream-utility',
790
+ 'integrationAdapter':'truthful-benchmark/1' if script=='aggregate_benchmark' else None,
791
+ 'effectiveNativeWorkers':workers,'script':script,'stdoutPath':str(stdout_path),'stderrPath':str(stderr_path),'trace':traces,
792
+ 'elapsedSeconds':round(time.monotonic()-started,6),
793
+ 'limitations':['Donor output is not certified when CLI evidence fails or is incomplete.',
794
+ 'Native Claude trigger behavior is not Codex host equivalence.','Not a sandbox; explicit upstream arguments retain upstream effects.',
795
+ 'Viewer never kills an existing port occupant; process group is cleaned on completion/timeout/interrupt.']}
796
+ _write(run_dir/'result.json',json.dumps(result).encode())
797
+ return result
798
+
799
+ def main(argv=None):
800
+ argv = list(sys.argv[1:] if argv is None else argv)
801
+ if argv and argv[0] == '__viewer':
802
+ return _viewer_entry(argv[1],argv[2:])
803
+ if argv and argv[0] == '__aggregate':
804
+ return _aggregate_entry(argv[1],argv[2:])
805
+ if argv and argv[0] == '__proxy':
806
+ return _proxy()
807
+ if argv and argv[0] == '__supervise':
808
+ return _supervise(argv[1],argv[2])
809
+ donor = []
810
+ if '--' in argv:
811
+ index = argv.index('--');donor=argv[index+1:];argv=argv[:index]
812
+ parser = argparse.ArgumentParser(description=__doc__)
813
+ subs = parser.add_subparsers(dest='action',required=True)
814
+ for name in ('setup','status','run'):
815
+ sub = subs.add_parser(name)
816
+ sub.add_argument('--runtime-root',required=True)
817
+ if name=='setup':sub.add_argument('--yes',action='store_true')
818
+ if name=='run':
819
+ sub.add_argument('script',choices=sorted(SCRIPTS))
820
+ sub.add_argument('--workspace',required=True)
821
+ sub.add_argument('--timeout',type=float,required=True)
822
+ sub.add_argument('--stdio',action='store_true',help='Forward stdin and tee stdout/stderr; logs remain private.')
823
+ sub.add_argument('--interactive',action='store_true',help='Explicit viewer/browser/server operation within the caller timeout.')
824
+ args = parser.parse_args(argv)
825
+ try:
826
+ with _cancellation_scope():
827
+ if args.action=='setup':result=setup(args.runtime_root,args.yes)
828
+ elif args.action=='status':result=status(args.runtime_root)
829
+ else:result=run(args.script,args.runtime_root,args.workspace,args.timeout,donor,args.stdio,args.interactive)
830
+ except _Cancellation as cancellation:
831
+ result={'schema':SCHEMA,'status':'interrupted','exitCode':128+cancellation.signum,
832
+ 'cancellationSignal':signal.Signals(cancellation.signum).name,'timedOut':False}
833
+ except (CreatorError,OSError,ValueError,TypeError,KeyError):
834
+ result={'schema':SCHEMA,'status':'operational-error','exitCode':2,'error':'unsafe-unavailable-or-incomplete-runtime-input'}
835
+ print(json.dumps(result,sort_keys=True,ensure_ascii=True))
836
+ return result['exitCode']
837
+
838
+ if __name__=='__main__':
839
+ sys.exit(main())