harness-everything 0.23.0 → 0.24.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.
Files changed (30) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +13 -0
  4. package/benchmarks/results/system-one/labeler-holdout-check-2026-09-23/README.md +55 -0
  5. package/benchmarks/results/system-one/labeler-holdout-check-2026-09-23/report.json +1104 -0
  6. package/ci/fixtures/system-one-resident-stub.py +3 -0
  7. package/ci/mechanism-system-one-collect.test.js +129 -0
  8. package/ci/mechanism-system-one-label.test.js +101 -0
  9. package/ci/mechanism-system-one-resident.test.js +36 -2
  10. package/ci/mechanism-system-one-router.test.js +13 -0
  11. package/ci/mechanism-system-one-train.test.js +82 -0
  12. package/ci/system-one-train-test.py +55 -0
  13. package/docs/system-one-routing.md +8 -1
  14. package/docs/system-one-training.md +121 -0
  15. package/harness-everything/SKILL.md +1 -1
  16. package/harness-everything/scripts/system-one/provider.js +12 -5
  17. package/harness-everything/scripts/system-one/router.js +1 -1
  18. package/opencode-plugin/plugin.json +1 -1
  19. package/package.json +1 -1
  20. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  21. package/plugins/harness-everything/plugin.json +1 -1
  22. package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
  23. package/plugins/harness-everything/skills/harness-everything/scripts/system-one/provider.js +12 -5
  24. package/plugins/harness-everything/skills/harness-everything/scripts/system-one/router.js +1 -1
  25. package/sbom.cdx.json +7 -7
  26. package/scripts/evaluate-system-one.js +1 -1
  27. package/scripts/system-one-calibrate.js +44 -0
  28. package/scripts/system-one-collect.js +196 -0
  29. package/scripts/system-one-label.js +144 -0
  30. package/scripts/system-one-train.py +168 -0
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env python3
2
+ """Train the System One harness-routing-v1 tier scorer (docs/system-one-training.md).
3
+
4
+ Run with the System One venv Python (torch + pinned cua_s1). Data handling is stdlib-only so it is
5
+ tested without torch; torch and cua_s1 are imported only inside train().
6
+ """
7
+ import argparse
8
+ import hashlib
9
+ import json
10
+ from pathlib import Path
11
+ import random
12
+ import sys
13
+
14
+ sys.dont_write_bytecode = True
15
+ REPO = Path(__file__).resolve().parents[1]
16
+ DEFAULT_HOLDOUT = REPO / 'benchmarks' / 'fixtures' / 'system-one-holdout.json'
17
+
18
+
19
+ def load_jsonl(path):
20
+ return [json.loads(line) for line in Path(path).read_text(encoding='utf-8').splitlines() if line.strip()]
21
+
22
+
23
+ def sha256_file(path):
24
+ return hashlib.sha256(Path(path).read_bytes()).hexdigest()
25
+
26
+
27
+ def catalog_from_holdout(path):
28
+ """The fixed tier catalog, taken from the evaluation corpus so training and inference match."""
29
+ corpus = json.loads(Path(path).read_text(encoding='utf-8'))
30
+ return [{'id': o['id'], 'text': o['text']} for o in corpus['cases'][0]['request']['options']]
31
+
32
+
33
+ def label_index(gold, catalog):
34
+ target = 'unclassified' if gold is None else gold
35
+ for i, option in enumerate(catalog):
36
+ if option['id'] == target:
37
+ return i
38
+ raise ValueError(f'unknown gold label: {gold!r}')
39
+
40
+
41
+ def join(prompts, labels, overrides):
42
+ """Labeled examples in prompt order; owner overrides replace LLM labels; duplicates are an error."""
43
+ gold = {}
44
+ for row in labels:
45
+ if row['id'] in gold:
46
+ raise ValueError(f'duplicate label for {row["id"]}')
47
+ gold[row['id']] = row['gold']
48
+ for row in overrides:
49
+ gold[row['id']] = row['gold']
50
+ return [{'id': p['id'], 'split': p['split'], 'text': p['text'], 'gold': gold[p['id']]} for p in prompts if p['id'] in gold]
51
+
52
+
53
+ def make_batches(lengths, token_budget):
54
+ """Length-bucketed batches: each batch's size times its longest item stays within the budget."""
55
+ order = sorted(range(len(lengths)), key=lambda i: (lengths[i], i))
56
+ batches, current, longest = [], [], 0
57
+ for i in order:
58
+ longest_if_added = max(longest, lengths[i])
59
+ if current and (len(current) + 1) * longest_if_added > token_budget:
60
+ batches.append(current)
61
+ current, longest_if_added = [], lengths[i]
62
+ current.append(i)
63
+ longest = longest_if_added
64
+ if current:
65
+ batches.append(current)
66
+ return batches
67
+
68
+
69
+ def train(args):
70
+ import torch
71
+ from cua_s1.model import ChoiceExample, make_system, save_checkpoint
72
+
73
+ random.seed(args.seed)
74
+ torch.manual_seed(args.seed)
75
+ torch.set_num_threads(max(1, args.threads))
76
+ data_dir = Path(args.data_dir)
77
+ catalog = catalog_from_holdout(args.holdout)
78
+ overrides_path = data_dir / 'owner-overrides.jsonl'
79
+ examples = join(load_jsonl(data_dir / 'prompts.jsonl'), load_jsonl(data_dir / 'labels.jsonl'),
80
+ load_jsonl(overrides_path) if overrides_path.exists() else [])
81
+ options = tuple(o['text'] for o in catalog)
82
+ split = {name: [e for e in examples if e['split'] == name] for name in ('train', 'validation')}
83
+ config = {'encoder': 'tinyx', 'width': 128, 'rank': 128, 'layers': 2, 'heads': 4, 'dropout': 0.1,
84
+ 'context_tokens': args.context_tokens, 'option_tokens': 96}
85
+ model, collator = make_system(config, 'cpu')
86
+
87
+ def to_examples(rows):
88
+ return [ChoiceExample(context=r['text'], options=options, label=label_index(r['gold'], catalog)) for r in rows]
89
+
90
+ def lengths(rows):
91
+ return [min(len(r['text'].encode('utf-8')), args.context_tokens) or 1 for r in rows]
92
+
93
+ train_x, val_x = to_examples(split['train']), to_examples(split['validation'])
94
+ train_batches = make_batches(lengths(split['train']), args.token_budget)
95
+ val_batches = make_batches(lengths(split['validation']), args.token_budget)
96
+ optimizer = torch.optim.AdamW(model.parameters(), lr=args.lr, weight_decay=0.01)
97
+
98
+ def evaluate():
99
+ model.eval()
100
+ nll, correct, probs = 0.0, 0, [None] * len(val_x)
101
+ with torch.inference_mode():
102
+ for batch in val_batches:
103
+ tensors = collator([val_x[i] for i in batch])
104
+ logits = model(tensors)
105
+ nll += torch.nn.functional.cross_entropy(logits, tensors['labels'], reduction='sum').item()
106
+ p = logits.softmax(-1)
107
+ correct += (p.argmax(-1) == tensors['labels']).sum().item()
108
+ for row, i in enumerate(batch):
109
+ probs[i] = p[row].tolist()
110
+ n = max(1, len(val_x))
111
+ return {'nll': nll / n, 'accuracy': correct / n}, probs
112
+
113
+ history, best = [], None
114
+ for epoch in range(1, args.epochs + 1):
115
+ model.train()
116
+ order = list(range(len(train_batches)))
117
+ random.shuffle(order)
118
+ total = 0.0
119
+ for b in order:
120
+ tensors = collator([train_x[i] for i in train_batches[b]])
121
+ loss = torch.nn.functional.cross_entropy(model(tensors), tensors['labels'])
122
+ optimizer.zero_grad()
123
+ loss.backward()
124
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
125
+ optimizer.step()
126
+ total += loss.item() * len(train_batches[b])
127
+ metrics, probs = evaluate()
128
+ history.append({'epoch': epoch, 'trainLoss': total / max(1, len(train_x)), **metrics})
129
+ print(json.dumps(history[-1]), flush=True)
130
+ if best is None or metrics['nll'] < best['metrics']['nll']:
131
+ best = {'epoch': epoch, 'metrics': metrics, 'probs': probs,
132
+ 'state': {k: v.detach().clone() for k, v in model.state_dict().items()}}
133
+
134
+ model.load_state_dict(best['state'])
135
+ out = Path(args.out)
136
+ out.mkdir(parents=True, exist_ok=True)
137
+ datasets = {name: sha256_file(data_dir / name) for name in ('prompts.jsonl', 'labels.jsonl')}
138
+ if overrides_path.exists():
139
+ datasets['owner-overrides.jsonl'] = sha256_file(overrides_path)
140
+ metadata = {'domain': 'harness-routing-v1', 'bestEpoch': best['epoch'], 'validation': best['metrics'],
141
+ 'datasets': datasets, 'seed': args.seed, 'catalog': [o['id'] for o in catalog]}
142
+ save_checkpoint(out / 'harness-routing-v1.safetensors', model, config, metadata)
143
+ with (out / 'val-scores.jsonl').open('w', encoding='utf-8') as fh:
144
+ for row, p in zip(split['validation'], best['probs']):
145
+ fh.write(json.dumps({'id': row['id'], 'gold': row['gold'], 'probs': p}) + '\n')
146
+ counts = {name: len(rows) for name, rows in split.items()}
147
+ report = {'schemaVersion': 1, 'config': config, 'counts': counts, 'bestEpoch': best['epoch'], 'history': history,
148
+ 'datasets': datasets, 'seed': args.seed}
149
+ (out / 'train-report.json').write_text(json.dumps(report, indent=2) + '\n', encoding='utf-8')
150
+ print(json.dumps({'counts': counts, 'bestEpoch': best['epoch'], 'validation': best['metrics']}))
151
+
152
+
153
+ def main(argv=None):
154
+ parser = argparse.ArgumentParser(description=__doc__)
155
+ parser.add_argument('--data-dir', required=True)
156
+ parser.add_argument('--out', required=True)
157
+ parser.add_argument('--holdout', default=str(DEFAULT_HOLDOUT))
158
+ parser.add_argument('--epochs', type=int, default=12)
159
+ parser.add_argument('--lr', type=float, default=1e-3)
160
+ parser.add_argument('--seed', type=int, default=7)
161
+ parser.add_argument('--threads', type=int, default=4)
162
+ parser.add_argument('--context-tokens', type=int, default=1024)
163
+ parser.add_argument('--token-budget', type=int, default=16384)
164
+ train(parser.parse_args(argv))
165
+
166
+
167
+ if __name__ == '__main__':
168
+ main()