harness-everything 0.29.0 → 0.30.1
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +42 -0
- package/PRIVACY.md +9 -0
- package/benchmarks/fixtures/system-one-intent-holdout-draft.json +2228 -0
- package/benchmarks/fixtures/system-one-intent-holdout.json +16509 -0
- package/benchmarks/fixtures/system-one-intent-reviews.json +2723 -0
- package/benchmarks/results/system-one/intent-ngram-v1-2026-09-24/README.md +64 -0
- package/benchmarks/results/system-one/intent-ngram-v1-2026-09-24/report.json +15177 -0
- package/benchmarks/results/system-one/labeler-holdout-check-intent-2026-09-24/report.json +2976 -0
- package/benchmarks/results/system-one/labeler-holdout-check-intent-scores-haiku-2026-09-24/report-005-steps.json +6125 -0
- package/benchmarks/results/system-one/labeler-holdout-check-intent-scores-haiku-2026-09-24/report-haiku-sonnet-escalation.json +6344 -0
- package/benchmarks/results/system-one/labeler-holdout-check-intent-scores-haiku-2026-09-24/report-scaled-margin-escalation.json +6206 -0
- package/benchmarks/results/system-one/labeler-holdout-check-intent-scores-haiku-2026-09-24/report.json +6103 -0
- package/benchmarks/results/system-one/laya-ft-2026-09-25/README.md +83 -0
- package/benchmarks/results/system-one/laya-ft-2026-09-25/report.json +71 -0
- package/benchmarks/results/system-one/laya-ft-2026-09-25/shadow-report.json +44 -0
- package/benchmarks/results/system-one/laya-zs-2026-09-25/README.md +68 -0
- package/benchmarks/results/system-one/laya-zs-2026-09-25/report.json +173 -0
- package/ci/fable-mode-test.js +16 -0
- package/ci/mechanism-34-observations.test.js +550 -0
- package/ci/mechanism-system-one-collect.test.js +16 -0
- package/ci/mechanism-system-one-intent.test.js +406 -0
- package/ci/mechanism-system-one-laya.test.js +45 -0
- package/ci/mechanism-system-one-relevance.test.js +112 -0
- package/ci/mechanism-system-one-resident.test.js +16 -0
- package/ci/system-one-laya-calibrate-test.py +65 -0
- package/ci/system-one-laya-export-test.py +155 -0
- package/ci/system-one-laya-label-test.py +201 -0
- package/ci/system-one-laya-perintent-test.py +60 -0
- package/ci/system-one-laya-provider-test.py +115 -0
- package/ci/system-one-laya-train-test.py +93 -0
- package/docs/routing.md +25 -0
- package/docs/system-one-intent-gates.md +102 -0
- package/docs/system-one-intent.md +173 -0
- package/docs/system-one-laya-training.md +38 -0
- package/docs/system-one-observations.md +206 -0
- package/docs/system-one-routing.md +25 -10
- package/docs/system-one-suggestion-gates.md +292 -0
- package/docs/system-one-training.md +56 -6
- package/docs/workflows/fable-mode.md +7 -0
- package/fable-mode/SKILL.md +1 -1
- package/fable-mode/agents/fable-orchestrator.md +4 -4
- package/fable-mode/agents/fable-worker-haiku.md +4 -4
- package/fable-mode/agents/fable-worker-sonnet.md +4 -4
- package/fable-mode/execution-guardrails/SKILL.md +5 -3
- package/fable-mode/execution-guardrails/references/guardrail-rules.md +12 -8
- package/fable-mode/fable-haiku/SKILL.md +1 -1
- package/fable-mode/fable-opus/SKILL.md +1 -1
- package/fable-mode/fable-sonnet/SKILL.md +1 -1
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/kernel-router.js +10 -0
- package/harness-everything/scripts/system-one/catalogs.js +35 -0
- package/harness-everything/scripts/system-one/contract.js +45 -1
- package/harness-everything/scripts/system-one/evaluate.js +115 -19
- package/harness-everything/scripts/system-one/intent.js +22 -0
- package/harness-everything/scripts/system-one/laya_adapter.py +141 -0
- package/harness-everything/scripts/system-one/provider.js +10 -3
- package/harness-everything/scripts/system-one/resident.js +8 -3
- package/harness-everything/scripts/system-one/router.js +1 -1
- package/hooks/hooks.json +40 -0
- package/hooks/scripts/codex-user-prompt.js +39 -0
- package/hooks/scripts/lib/observations.js +581 -0
- package/hooks/scripts/observation-hook.js +25 -0
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/hooks.json +35 -2
- package/plugins/harness-everything/hooks/scripts/codex-user-prompt.js +39 -0
- package/plugins/harness-everything/hooks/scripts/lib/observations.js +581 -0
- package/plugins/harness-everything/hooks/scripts/observation-hook.js +25 -0
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/fable-mode/SKILL.md +1 -1
- package/plugins/harness-everything/skills/fable-mode/agents/fable-orchestrator.md +4 -4
- package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-haiku.md +4 -4
- package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-sonnet.md +4 -4
- package/plugins/harness-everything/skills/fable-mode/execution-guardrails/SKILL.md +5 -3
- package/plugins/harness-everything/skills/fable-mode/execution-guardrails/references/guardrail-rules.md +12 -8
- package/plugins/harness-everything/skills/fable-mode/fable-haiku/SKILL.md +1 -1
- package/plugins/harness-everything/skills/fable-mode/fable-opus/SKILL.md +1 -1
- package/plugins/harness-everything/skills/fable-mode/fable-sonnet/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router.js +10 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/catalogs.js +35 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/contract.js +45 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/evaluate.js +115 -19
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/intent.js +22 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/laya_adapter.py +141 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/provider.js +10 -3
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/resident.js +8 -3
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/router.js +1 -1
- package/sbom.cdx.json +7 -7
- package/scripts/evaluate-system-one.js +17 -5
- package/scripts/system-one-calibrate-laya.py +165 -0
- package/scripts/system-one-calibrate.js +42 -13
- package/scripts/system-one-collect.js +7 -3
- package/scripts/system-one-compare-laya.py +156 -0
- package/scripts/system-one-corpus.js +22 -15
- package/scripts/system-one-eval-laya.js +112 -0
- package/scripts/system-one-export-laya.py +197 -0
- package/scripts/system-one-label-laya.py +243 -0
- package/scripts/system-one-label.js +163 -37
- package/scripts/system-one-observations-export.js +106 -0
- package/scripts/system-one-observations-review.js +86 -0
- package/scripts/system-one-perintent-laya.py +134 -0
- package/scripts/system-one-train-laya.py +344 -0
- package/scripts/system-one-train-ngram.py +66 -14
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// System One turn observations (docs/system-one-observations.md): label line,
|
|
4
|
+
// collector hook for Claude Code and Codex, local store, exporter, review page.
|
|
5
|
+
|
|
6
|
+
const assert = require('assert');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { spawnSync, execFileSync } = require('child_process');
|
|
11
|
+
|
|
12
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
13
|
+
const lib = require(path.join(ROOT, 'hooks/scripts/lib/observations.js'));
|
|
14
|
+
const HOOK = path.join(ROOT, 'hooks/scripts/observation-hook.js');
|
|
15
|
+
const EXPORTER = path.join(ROOT, 'scripts/system-one-observations-export.js');
|
|
16
|
+
const REVIEW = path.join(ROOT, 'scripts/system-one-observations-review.js');
|
|
17
|
+
let failures = 0;
|
|
18
|
+
function test(name, fn) {
|
|
19
|
+
try { fn(); console.log(` PASS ${name}`); } catch (error) { failures++; console.error(` FAIL ${name}\n ${error.stack.split('\n').slice(0, 3).join('\n ')}`); }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const LABEL = '<!-- harness-label {"v":1,"validity":"actionable","contextDependent":false,"tier":"tier2","intents":["fix","test"],"workflow":"iterative-single","skills":["tdd"]} -->';
|
|
23
|
+
|
|
24
|
+
test('label line parses and validates', () => {
|
|
25
|
+
const parsed = lib.parseLabelLine(`Done.\n\n${LABEL}\n`);
|
|
26
|
+
assert.deepStrictEqual(parsed.label, { v: 1, validity: 'actionable', contextDependent: false, tier: 'tier2',
|
|
27
|
+
intents: ['fix', 'test'], workflow: 'iterative-single', skills: ['tdd'] });
|
|
28
|
+
assert.strictEqual(lib.parseLabelLine('no label here').label, null);
|
|
29
|
+
assert.strictEqual(lib.parseLabelLine('no label here').reason, 'label-missing');
|
|
30
|
+
assert.strictEqual(lib.parseLabelLine('<!-- harness-label {bad json} -->').reason, 'label-unparseable');
|
|
31
|
+
const unknown = lib.parseLabelLine('<!-- harness-label {"v":1,"validity":"actionable","contextDependent":false,"tier":"tier9","intents":[],"workflow":null,"skills":[]} -->');
|
|
32
|
+
assert.strictEqual(unknown.label, null);
|
|
33
|
+
assert.strictEqual(unknown.reason, 'label-invalid');
|
|
34
|
+
const tooMany = lib.parseLabelLine('<!-- harness-label {"v":1,"validity":"actionable","contextDependent":false,"tier":null,"intents":["fix","test","docs","plan"],"workflow":null,"skills":[]} -->');
|
|
35
|
+
assert.strictEqual(tooMany.reason, 'label-invalid', 'at most three intents');
|
|
36
|
+
const two = lib.parseLabelLine(`${LABEL.replace('tier2', 'tier1')}\nmore\n${LABEL}`);
|
|
37
|
+
assert.strictEqual(two.label.tier, 'tier2', 'the last label line wins');
|
|
38
|
+
const invalid = lib.parseLabelLine('<!-- harness-label {"v":1,"validity":"invalid","contextDependent":true,"tier":null,"intents":[],"workflow":null,"skills":[]} -->');
|
|
39
|
+
assert.strictEqual(invalid.label.validity, 'invalid');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('redaction removes email addresses and home paths', () => {
|
|
43
|
+
const home = os.homedir();
|
|
44
|
+
const out = lib.redact(`mail me at a.b@example.com, file ${home}/proj/x.js and C:\\Users\\someone\\x`);
|
|
45
|
+
assert(!out.includes('a.b@example.com') && out.includes('<email>'));
|
|
46
|
+
assert(!out.includes(home) && out.includes('<home>'));
|
|
47
|
+
assert(!out.includes('C:\\Users\\someone') && out.includes('<home>'));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('commands are classified without keeping their text', () => {
|
|
51
|
+
assert.strictEqual(lib.classifyCommand('git status'), 'git');
|
|
52
|
+
assert.strictEqual(lib.classifyCommand('gh pr view 12'), 'gh');
|
|
53
|
+
assert.strictEqual(lib.classifyCommand('npm test'), 'test');
|
|
54
|
+
assert.strictEqual(lib.classifyCommand('pytest -k slow'), 'test');
|
|
55
|
+
assert.strictEqual(lib.classifyCommand('npm run build'), 'build');
|
|
56
|
+
assert.strictEqual(lib.classifyCommand('pip install requests'), 'package');
|
|
57
|
+
assert.strictEqual(lib.classifyCommand('ls -la'), 'shell');
|
|
58
|
+
assert.strictEqual(lib.classifyCommand('cd x && git push'), 'git', 'the first recognizable segment decides');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('tier comes from the self-report, owner review overrides, behavior only flags contradictions', () => {
|
|
62
|
+
assert.strictEqual(typeof lib.breadthTier, 'undefined', 'no file-count tier rule');
|
|
63
|
+
assert.deepStrictEqual(lib.labelTier({ tier: 'tier2' }, null), { tier: 'tier2', source: 'self-report' });
|
|
64
|
+
assert.deepStrictEqual(lib.labelTier({ tier: 'tier2' }, { tier: 'tier3' }), { tier: 'tier3', source: 'owner' });
|
|
65
|
+
assert.deepStrictEqual(lib.labelTier(null, null), { tier: null, source: null });
|
|
66
|
+
assert.deepStrictEqual(lib.contradictions({ tier: 'tier1', validity: 'actionable' }, { filesWritten: 2 }), ['tier1-with-writes']);
|
|
67
|
+
assert.deepStrictEqual(lib.contradictions({ tier: 'tier3', validity: 'actionable' }, { filesWritten: 0 }), [], 'a tier3 plan with no writes is not a contradiction');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
function sandbox() {
|
|
71
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'obs-'));
|
|
72
|
+
const store = path.join(dir, 'store');
|
|
73
|
+
const repo = path.join(dir, 'repo');
|
|
74
|
+
fs.mkdirSync(repo);
|
|
75
|
+
execFileSync('git', ['init', '-q', repo]);
|
|
76
|
+
const env = { ...process.env, HARNESS_OBSERVATIONS_DIR: store, HARNESS_STATE_ROOT: path.join(dir, 'state') };
|
|
77
|
+
delete env.HARNESS_OBSERVATIONS;
|
|
78
|
+
// Fixtures choose their own host; never inherit the invoking agent's.
|
|
79
|
+
for (const key of ['CLAUDE_PLUGIN_ROOT', 'CLAUDECODE', 'CLAUDE_CODE_ENTRYPOINT', 'CLAUDE', 'CODEX_HOME', 'CODEX_THREAD_ID']) delete env[key];
|
|
80
|
+
return { dir, store, repo, env };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function hook(env, payload) {
|
|
84
|
+
const r = spawnSync('node', [HOOK], { input: typeof payload === 'string' ? payload : JSON.stringify(payload), env, encoding: 'utf8', timeout: 5000 });
|
|
85
|
+
assert.strictEqual(r.status, 0, `hook exit ${r.status}: ${r.stderr}`);
|
|
86
|
+
assert.strictEqual(r.stdout, '', 'the hook never writes to stdout (it would enter the model context)');
|
|
87
|
+
return r;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function records(store) {
|
|
91
|
+
const file = path.join(store, 'observations-index.json');
|
|
92
|
+
if (!fs.existsSync(file)) return [];
|
|
93
|
+
const index = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
94
|
+
assert.strictEqual(index.schemaVersion, 1);
|
|
95
|
+
return index.records;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
test('Claude Code turn: prompt, behavior and label are recorded', () => {
|
|
99
|
+
const s = sandbox();
|
|
100
|
+
const base = { session_id: 'sess-a', cwd: s.repo };
|
|
101
|
+
s.env.CLAUDE_PLUGIN_ROOT = ROOT;
|
|
102
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: `fix the parser, contact x@y.io at ${os.homedir()}/p` });
|
|
103
|
+
hook(s.env, { ...base, hook_event_name: 'PostToolUse', tool_name: 'Edit', tool_input: { file_path: path.join(s.repo, 'a.js') } });
|
|
104
|
+
hook(s.env, { ...base, hook_event_name: 'PostToolUse', tool_name: 'Write', tool_input: { file_path: path.join(s.repo, 'b.js') }, tool_response: { type: 'create' } });
|
|
105
|
+
hook(s.env, { ...base, hook_event_name: 'PostToolUse', tool_name: 'Bash', tool_input: { command: 'npm test' } });
|
|
106
|
+
hook(s.env, { ...base, hook_event_name: 'PostToolUse', tool_name: 'Skill', tool_input: { skill: 'harness-everything:tdd' } });
|
|
107
|
+
hook(s.env, { ...base, hook_event_name: 'Stop', last_assistant_message: `Fixed.\n${LABEL}` });
|
|
108
|
+
const recs = records(s.store);
|
|
109
|
+
assert.strictEqual(recs.length, 1);
|
|
110
|
+
const r = recs[0];
|
|
111
|
+
assert.strictEqual(r.source, 'observation');
|
|
112
|
+
assert.strictEqual(r.status, 'active');
|
|
113
|
+
assert.strictEqual(r.host, 'claude');
|
|
114
|
+
assert.strictEqual(r.writer.sessionId, 'sess-a');
|
|
115
|
+
assert(Date.parse(r.validUntil) - Date.parse(r.observedAt) >= 179 * 86400000);
|
|
116
|
+
assert.deepStrictEqual(
|
|
117
|
+
{ w: r.behavior.filesWritten, repos: r.behavior.reposWritten, created: r.behavior.filesCreated, test: r.behavior.commands.test },
|
|
118
|
+
{ w: 2, repos: 1, created: 1, test: 1 });
|
|
119
|
+
assert.deepStrictEqual(r.behavior.skills, ['tdd']);
|
|
120
|
+
assert.strictEqual(r.selfReport.tier, 'tier2');
|
|
121
|
+
assert(!JSON.stringify(r).includes('fix the parser'), 'the index holds no prompt text');
|
|
122
|
+
const text = JSON.parse(fs.readFileSync(path.join(s.store, 'text', `${r.contentSha256}.json`), 'utf8'));
|
|
123
|
+
assert(text.prompt.startsWith('fix the parser') && text.prompt.includes('<email>') && text.prompt.includes('<home>'));
|
|
124
|
+
assert(Array.isArray(r.scope.taskTerms) && r.scope.taskTerms.includes('parser'));
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('second turn keeps the previous final message; missing label is recorded, not fatal', () => {
|
|
128
|
+
const s = sandbox();
|
|
129
|
+
const base = { session_id: 'sess-b', cwd: s.repo };
|
|
130
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'run the tests' });
|
|
131
|
+
hook(s.env, { ...base, hook_event_name: 'Stop', last_assistant_message: 'All 12 tests pass. Want me to commit?' });
|
|
132
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'yes' });
|
|
133
|
+
hook(s.env, { ...base, hook_event_name: 'Stop', last_assistant_message: `Committed.\n${LABEL.replace('"actionable","contextDependent":false', '"invalid","contextDependent":true')}` });
|
|
134
|
+
const recs = records(s.store);
|
|
135
|
+
assert.strictEqual(recs.length, 2);
|
|
136
|
+
assert.strictEqual(recs[0].selfReport, null);
|
|
137
|
+
assert.strictEqual(recs[0].selfReportReason, 'label-missing');
|
|
138
|
+
const text = JSON.parse(fs.readFileSync(path.join(s.store, 'text', `${recs[1].contentSha256}.json`), 'utf8'));
|
|
139
|
+
assert.strictEqual(text.prompt, 'yes');
|
|
140
|
+
assert(text.previous.includes('Want me to commit?'));
|
|
141
|
+
assert.strictEqual(recs[1].selfReport.contextDependent, true);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('Codex turn: final message from the rollout transcript, apply_patch counted', () => {
|
|
145
|
+
const s = sandbox();
|
|
146
|
+
const rollout = path.join(s.dir, 'rollout.jsonl');
|
|
147
|
+
fs.writeFileSync(rollout, [
|
|
148
|
+
{ type: 'response_item', payload: { type: 'message', role: 'assistant', content: [{ type: 'output_text', text: 'earlier' }] } },
|
|
149
|
+
{ type: 'event_msg', payload: { type: 'task_complete', turn_id: 't1', last_agent_message: `Added the flag.\n${LABEL.replace('"tier2"', '"tier3"')}` } },
|
|
150
|
+
].map(x => JSON.stringify(x)).join('\n') + '\n');
|
|
151
|
+
const base = { session_id: 'sess-c', cwd: s.repo, transcript_path: rollout };
|
|
152
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'add a --json flag' });
|
|
153
|
+
hook(s.env, { ...base, hook_event_name: 'PostToolUse', tool_name: 'apply_patch', tool_input: { command: `*** Begin Patch\n*** Add File: ${s.repo}/flag.js\n+x\n*** Update File: ${s.repo}/cli.js\n@@\n-a\n+b\n*** End Patch` } });
|
|
154
|
+
hook(s.env, { ...base, hook_event_name: 'Stop' });
|
|
155
|
+
const [r] = records(s.store);
|
|
156
|
+
assert.strictEqual(r.host, 'codex');
|
|
157
|
+
assert.strictEqual(r.behavior.filesWritten, 2);
|
|
158
|
+
assert.strictEqual(r.behavior.filesCreated, 1);
|
|
159
|
+
assert.strictEqual(r.selfReport.tier, 'tier3');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('Claude transcript fallback when the Stop payload has no message', () => {
|
|
163
|
+
const s = sandbox();
|
|
164
|
+
const transcript = path.join(s.dir, 'claude.jsonl');
|
|
165
|
+
fs.writeFileSync(transcript, [
|
|
166
|
+
{ type: 'user', message: { role: 'user', content: 'x' } },
|
|
167
|
+
{ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: `ok\n${LABEL}` }] } },
|
|
168
|
+
].map(x => JSON.stringify(x)).join('\n') + '\n');
|
|
169
|
+
const base = { session_id: 'sess-d', cwd: s.repo, transcript_path: transcript };
|
|
170
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'x' });
|
|
171
|
+
hook(s.env, { ...base, hook_event_name: 'Stop' });
|
|
172
|
+
assert.strictEqual(records(s.store)[0].selfReport.tier, 'tier2');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('opt-out writes nothing; malformed input fails open', () => {
|
|
176
|
+
const s = sandbox();
|
|
177
|
+
const off = { ...s.env, HARNESS_OBSERVATIONS: 'off' };
|
|
178
|
+
hook(off, { session_id: 'z', cwd: s.repo, hook_event_name: 'UserPromptSubmit', prompt: 'hello' });
|
|
179
|
+
hook(off, { session_id: 'z', cwd: s.repo, hook_event_name: 'Stop', last_assistant_message: LABEL });
|
|
180
|
+
assert(!fs.existsSync(s.store) || records(s.store).length === 0);
|
|
181
|
+
hook(s.env, '{not json');
|
|
182
|
+
hook(s.env, '');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
function writeFixtureStore(s) {
|
|
186
|
+
const base = { cwd: s.repo };
|
|
187
|
+
const turns = [
|
|
188
|
+
{ sid: 'e1', prompt: 'git status', label: LABEL.replace('"tier2"', '"tier1"'), tools: [] },
|
|
189
|
+
{ sid: 'e2', prompt: 'fix the flaky parser test', label: LABEL, tools: [['Edit', 'a.js'], ['Edit', 'b.js']] },
|
|
190
|
+
{ sid: 'e3', prompt: 'add a csv export command', label: LABEL.replace('"tier2"', '"tier3"').replace('["fix","test"]', '["feature"]'), tools: [['Edit', 'c.js']] },
|
|
191
|
+
{ sid: 'e4', prompt: 'go', label: LABEL.replace('"actionable","contextDependent":false', '"invalid","contextDependent":true'), tools: [] },
|
|
192
|
+
];
|
|
193
|
+
for (const t of turns) {
|
|
194
|
+
hook(s.env, { ...base, session_id: t.sid, hook_event_name: 'UserPromptSubmit', prompt: t.prompt });
|
|
195
|
+
for (const [tool, file] of t.tools) hook(s.env, { ...base, session_id: t.sid, hook_event_name: 'PostToolUse', tool_name: tool, tool_input: { file_path: path.join(s.repo, file) } });
|
|
196
|
+
hook(s.env, { ...base, session_id: t.sid, hook_event_name: 'Stop', last_assistant_message: `done\n${t.label}` });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
test('exporter derives labels, splits by time and reports agreement', () => {
|
|
201
|
+
const s = sandbox();
|
|
202
|
+
writeFixtureStore(s);
|
|
203
|
+
const out = path.join(s.dir, 'export');
|
|
204
|
+
const r = spawnSync('node', [EXPORTER, '--store', s.store, '--out', out], { encoding: 'utf8', env: s.env });
|
|
205
|
+
assert.strictEqual(r.status, 0, r.stderr);
|
|
206
|
+
const prompts = fs.readFileSync(path.join(out, 'prompts-observed.jsonl'), 'utf8').trim().split('\n').map(JSON.parse);
|
|
207
|
+
const labels = fs.readFileSync(path.join(out, 'labels-observed.jsonl'), 'utf8').trim().split('\n').map(JSON.parse);
|
|
208
|
+
assert.strictEqual(prompts.length, 4);
|
|
209
|
+
assert(prompts.every(p => p.source === 'observation' && ['train', 'validation'].includes(p.split) && p.family));
|
|
210
|
+
const byText = Object.fromEntries(prompts.map(p => [p.text, labels.find(l => l.id === p.id)]));
|
|
211
|
+
assert.strictEqual(byText['git status'].tier, 'tier1');
|
|
212
|
+
assert.strictEqual(byText['fix the flaky parser test'].tier, 'tier2');
|
|
213
|
+
assert.strictEqual(byText['add a csv export command'].tier, 'tier3');
|
|
214
|
+
assert.strictEqual(byText['add a csv export command'].tierSource, 'self-report');
|
|
215
|
+
assert.strictEqual(byText['go'].validity, 'invalid');
|
|
216
|
+
assert.strictEqual(prompts.filter(p => p.split === 'validation').length, 1, 'newest 15%, at least one row');
|
|
217
|
+
const report = JSON.parse(fs.readFileSync(path.join(out, 'export-report.json'), 'utf8'));
|
|
218
|
+
assert.strictEqual(report.records, 4);
|
|
219
|
+
assert.strictEqual(report.contradictions['tier1-with-writes'] || 0, 0);
|
|
220
|
+
assert('routerVsSelfTier' in report.agreement && !('selfVsBehaviorTier' in report.agreement));
|
|
221
|
+
assert(labels.every(l => !('breadthTier' in l)), 'labels carry no file-count tier');
|
|
222
|
+
assert(!fs.readFileSync(path.join(out, 'export-report.json'), 'utf8').includes('parser'), 'the report holds no text');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('review page lists disagreements blind and owner decisions override', () => {
|
|
226
|
+
const s = sandbox();
|
|
227
|
+
writeFixtureStore(s);
|
|
228
|
+
const html = path.join(s.dir, 'review.html');
|
|
229
|
+
const r = spawnSync('node', [REVIEW, '--store', s.store, '--out', html, '--all'], { encoding: 'utf8', env: s.env });
|
|
230
|
+
assert.strictEqual(r.status, 0, r.stderr);
|
|
231
|
+
const page = fs.readFileSync(html, 'utf8');
|
|
232
|
+
assert(page.includes('add a csv export command') && page.includes('harness-observation-review'));
|
|
233
|
+
assert(!/tier3|self-report/.test(page.replace(/<script[\s\S]*?<\/script>/g, '').replace(/<option[\s\S]*?<\/option>/g, '')), 'blind: no labels shown outside the controls');
|
|
234
|
+
const review = path.join(s.dir, 'decisions.json');
|
|
235
|
+
const id = records(s.store).find(x => x.selfReport && x.selfReport.tier === 'tier3').id;
|
|
236
|
+
fs.writeFileSync(review, JSON.stringify({ schemaVersion: 1, review: 'harness-observation-review', decisions: [{ id, tier: 'tier2', validity: 'actionable' }] }));
|
|
237
|
+
const out = path.join(s.dir, 'export2');
|
|
238
|
+
const e = spawnSync('node', [EXPORTER, '--store', s.store, '--out', out, '--owner-review', review], { encoding: 'utf8', env: s.env });
|
|
239
|
+
assert.strictEqual(e.status, 0, e.stderr);
|
|
240
|
+
const labels = fs.readFileSync(path.join(out, 'labels-observed.jsonl'), 'utf8').trim().split('\n').map(JSON.parse);
|
|
241
|
+
const l = labels.find(x => x.id === id);
|
|
242
|
+
assert.strictEqual(l.tier, 'tier2');
|
|
243
|
+
assert.strictEqual(l.tierSource, 'owner');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
function rollout(s, name, rows) {
|
|
247
|
+
const file = path.join(s.dir, name);
|
|
248
|
+
fs.writeFileSync(file, rows.map(x => JSON.stringify(x)).join('\n') + '\n');
|
|
249
|
+
return file;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
test('transcript fallback reads only the current turn', () => {
|
|
253
|
+
const s = sandbox();
|
|
254
|
+
const tier1 = `Status.\n${LABEL.replace('"tier2"', '"tier1"')}`;
|
|
255
|
+
const tier3 = `Built it.\n${LABEL.replace('"tier2"', '"tier3"')}`;
|
|
256
|
+
const previous = [
|
|
257
|
+
{ type: 'event_msg', payload: { type: 'task_started', turn_id: 't1' } },
|
|
258
|
+
{ type: 'response_item', payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: 'status?' }] } },
|
|
259
|
+
{ type: 'event_msg', payload: { type: 'task_complete', turn_id: 't1', last_agent_message: tier1 } },
|
|
260
|
+
{ type: 'event_msg', payload: { type: 'task_started', turn_id: 't2' } },
|
|
261
|
+
{ type: 'response_item', payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: 'build it' }] } },
|
|
262
|
+
];
|
|
263
|
+
const cases = [
|
|
264
|
+
['current final answer, no task_complete yet', [...previous,
|
|
265
|
+
{ type: 'response_item', payload: { type: 'message', role: 'assistant', phase: 'commentary', content: [{ type: 'output_text', text: `working\n${LABEL.replace('"tier2"', '"tier1"')}` }] } },
|
|
266
|
+
{ type: 'response_item', payload: { type: 'message', role: 'assistant', phase: 'final_answer', content: [{ type: 'output_text', text: tier3 }] } }], 'tier3'],
|
|
267
|
+
['no current final response', [...previous,
|
|
268
|
+
{ type: 'response_item', payload: { type: 'message', role: 'assistant', phase: 'commentary', content: [{ type: 'output_text', text: 'working' }] } }], null],
|
|
269
|
+
['previous task_complete without a current task_started in view', [
|
|
270
|
+
{ type: 'event_msg', payload: { type: 'task_complete', turn_id: 't1', last_agent_message: tier1 } }], null],
|
|
271
|
+
];
|
|
272
|
+
cases.forEach(([name, rows, tier], k) => {
|
|
273
|
+
const file = rollout(s, `r${k}.jsonl`, rows);
|
|
274
|
+
const base = { session_id: `turn-${k}`, cwd: s.repo, transcript_path: file, turn_id: 't2' };
|
|
275
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'build it' });
|
|
276
|
+
hook(s.env, { ...base, hook_event_name: 'Stop' });
|
|
277
|
+
const r = records(s.store).find(x => x.writer.sessionId === `turn-${k}`);
|
|
278
|
+
assert.strictEqual(r.selfReport ? r.selfReport.tier : null, tier, name);
|
|
279
|
+
if (tier === null) assert.strictEqual(r.selfReportReason, 'final-message-unavailable', name);
|
|
280
|
+
});
|
|
281
|
+
const claude = rollout(s, 'claude-multi.jsonl', [
|
|
282
|
+
{ type: 'user', message: { role: 'user', content: 'status?' } },
|
|
283
|
+
{ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: tier1 }] } },
|
|
284
|
+
{ type: 'user', message: { role: 'user', content: [{ type: 'text', text: 'build it' }] } },
|
|
285
|
+
{ type: 'assistant', message: { role: 'assistant', content: [{ type: 'tool_use', id: 'x', name: 'Edit', input: {} }] } },
|
|
286
|
+
{ type: 'user', message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'x', content: 'ok' }] } },
|
|
287
|
+
]);
|
|
288
|
+
const base = { session_id: 'claude-multi', cwd: s.repo, transcript_path: claude };
|
|
289
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'build it' });
|
|
290
|
+
hook(s.env, { ...base, hook_event_name: 'Stop' });
|
|
291
|
+
assert.strictEqual(records(s.store).find(x => x.writer.sessionId === 'claude-multi').selfReport, null, 'Claude: no reuse of the previous label');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test('concurrent tool hooks keep every event', () => {
|
|
295
|
+
const s = sandbox();
|
|
296
|
+
const base = { session_id: 'sess-par', cwd: s.repo };
|
|
297
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'edit many files' });
|
|
298
|
+
const payloads = Array.from({ length: 40 }, (_, k) => ({ ...base, hook_event_name: 'PostToolUse', tool_name: 'Edit', tool_input: { file_path: path.join(s.repo, `f${k}.js`) } }));
|
|
299
|
+
const script = `const { spawn } = require('child_process');
|
|
300
|
+
const payloads = ${JSON.stringify(payloads)};
|
|
301
|
+
let left = payloads.length, bad = 0;
|
|
302
|
+
for (const p of payloads) {
|
|
303
|
+
const c = spawn(process.execPath, [${JSON.stringify(HOOK)}], { stdio: ['pipe', 'ignore', 'ignore'] });
|
|
304
|
+
c.on('exit', code => { if (code !== 0) bad++; if (--left === 0) process.exit(bad ? 1 : 0); });
|
|
305
|
+
c.stdin.end(JSON.stringify(p));
|
|
306
|
+
}`;
|
|
307
|
+
const r = spawnSync(process.execPath, ['-e', script], { env: s.env, encoding: 'utf8', timeout: 60000 });
|
|
308
|
+
assert.strictEqual(r.status, 0, r.stderr);
|
|
309
|
+
hook(s.env, { ...base, hook_event_name: 'Stop', last_assistant_message: `done\n${LABEL}` });
|
|
310
|
+
const [rec] = records(s.store);
|
|
311
|
+
assert.deepStrictEqual({ calls: rec.behavior.toolCalls, files: rec.behavior.filesWritten }, { calls: 40, files: 40 });
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
function setIndex(store, fn) {
|
|
315
|
+
const file = path.join(store, 'observations-index.json');
|
|
316
|
+
const index = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
317
|
+
fn(index.records);
|
|
318
|
+
fs.writeFileSync(file, JSON.stringify(index));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
test('expired text is neither exported nor reviewed, and old session text is dropped', () => {
|
|
322
|
+
const s = sandbox();
|
|
323
|
+
const base = { session_id: 'sess-exp', cwd: s.repo };
|
|
324
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'secret old prompt' });
|
|
325
|
+
hook(s.env, { ...base, hook_event_name: 'Stop', last_assistant_message: `old final answer\n${LABEL}` });
|
|
326
|
+
setIndex(s.store, recs => { recs[0].validUntil = new Date(Date.now() - 1000).toISOString(); });
|
|
327
|
+
const out = path.join(s.dir, 'export');
|
|
328
|
+
const e = spawnSync('node', [EXPORTER, '--store', s.store, '--out', out], { encoding: 'utf8', env: s.env });
|
|
329
|
+
assert.strictEqual(e.status, 0, e.stderr);
|
|
330
|
+
assert.strictEqual(JSON.parse(fs.readFileSync(path.join(out, 'export-report.json'), 'utf8')).exported, 0);
|
|
331
|
+
const html = path.join(s.dir, 'review.html');
|
|
332
|
+
const v = spawnSync('node', [REVIEW, '--store', s.store, '--out', html, '--all'], { encoding: 'utf8', env: s.env });
|
|
333
|
+
assert.strictEqual(v.status, 0, v.stderr);
|
|
334
|
+
assert.strictEqual(JSON.parse(v.stdout).items, 0);
|
|
335
|
+
assert(!fs.readFileSync(html, 'utf8').includes('secret old prompt'));
|
|
336
|
+
const [rec] = records(s.store);
|
|
337
|
+
assert.strictEqual(rec.selfReport.tier, 'tier2', 'labels and counts survive expiry');
|
|
338
|
+
// Session state older than the retention window loses its text.
|
|
339
|
+
const sessions = path.join(s.store, 'sessions');
|
|
340
|
+
const old = (Date.now() - 181 * 86400000) / 1000;
|
|
341
|
+
for (const f of fs.readdirSync(sessions)) {
|
|
342
|
+
const file = path.join(sessions, f);
|
|
343
|
+
if (f.endsWith('.json')) {
|
|
344
|
+
const state = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
345
|
+
state.lastFinalAt = new Date(old * 1000).toISOString();
|
|
346
|
+
fs.writeFileSync(file, JSON.stringify(state));
|
|
347
|
+
}
|
|
348
|
+
fs.utimesSync(file, old, old);
|
|
349
|
+
}
|
|
350
|
+
hook(s.env, { session_id: 'other', cwd: s.repo, hook_event_name: 'UserPromptSubmit', prompt: 'x' });
|
|
351
|
+
hook(s.env, { session_id: 'other', cwd: s.repo, hook_event_name: 'Stop', last_assistant_message: 'y' });
|
|
352
|
+
const left = fs.readdirSync(sessions).map(f => fs.readFileSync(path.join(sessions, f), 'utf8')).join('\n');
|
|
353
|
+
assert(!left.includes('old final answer'), 'expired session text is deleted');
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test('expiring a record keeps a shared text blob that an active record still uses', () => {
|
|
357
|
+
const s = sandbox();
|
|
358
|
+
for (const sid of ['blob-1', 'blob-2']) {
|
|
359
|
+
if (sid === 'blob-2') setIndex(s.store, recs => { recs[0].validUntil = new Date(Date.now() - 1000).toISOString(); });
|
|
360
|
+
hook(s.env, { session_id: sid, cwd: s.repo, hook_event_name: 'UserPromptSubmit', prompt: 'same first prompt' });
|
|
361
|
+
hook(s.env, { session_id: sid, cwd: s.repo, hook_event_name: 'Stop', last_assistant_message: `ok\n${LABEL}` });
|
|
362
|
+
}
|
|
363
|
+
const recs = records(s.store);
|
|
364
|
+
assert.strictEqual(recs.length, 2);
|
|
365
|
+
assert.strictEqual(recs[0].contentSha256, recs[1].contentSha256);
|
|
366
|
+
assert(fs.existsSync(path.join(s.store, 'text', `${recs[1].contentSha256}.json`)), 'the active record keeps its text');
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test('failed tool attempts are not counted as writes or loads', () => {
|
|
370
|
+
const s = sandbox();
|
|
371
|
+
const base = { session_id: 'sess-fail', cwd: s.repo };
|
|
372
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'what does this do?' });
|
|
373
|
+
hook(s.env, { ...base, hook_event_name: 'PostToolUseFailure', tool_name: 'Edit', tool_input: { file_path: path.join(s.repo, 'a.js') }, error: 'old_string not found' });
|
|
374
|
+
hook(s.env, { ...base, hook_event_name: 'PostToolUseFailure', tool_name: 'Skill', tool_input: { skill: 'tdd' }, error: 'unknown skill' });
|
|
375
|
+
hook(s.env, { ...base, hook_event_name: 'PostToolUse', tool_name: 'apply_patch', tool_input: { command: `*** Begin Patch\n*** Update File: ${s.repo}/b.js\n@@\n-a\n+b\n*** End Patch` }, tool_response: { exit_code: 1, stderr: 'verification failed' } });
|
|
376
|
+
hook(s.env, { ...base, hook_event_name: 'PostToolUse', tool_name: 'Write', tool_input: { file_path: path.join(s.repo, 'c.js') }, tool_response: { success: false } });
|
|
377
|
+
hook(s.env, { ...base, hook_event_name: 'Stop', last_assistant_message: `It parses.\n${LABEL.replace('"tier2"', '"tier1"')}` });
|
|
378
|
+
const [r] = records(s.store);
|
|
379
|
+
assert.deepStrictEqual({ w: r.behavior.filesWritten, c: r.behavior.filesCreated, skills: r.behavior.skills },
|
|
380
|
+
{ w: 0, c: 0, skills: [] });
|
|
381
|
+
assert.deepStrictEqual({ f: r.behavior.toolFailures, fw: r.behavior.failedWrites, fs: r.behavior.failedSkillLoads }, { f: 4, fw: 3, fs: 1 });
|
|
382
|
+
assert.deepStrictEqual(lib.contradictions(r.selfReport, r.behavior), []);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test('owner review overrides context dependence, explicit false included', () => {
|
|
386
|
+
const s = sandbox();
|
|
387
|
+
const base = { cwd: s.repo };
|
|
388
|
+
hook(s.env, { ...base, session_id: 'cd-1', hook_event_name: 'UserPromptSubmit', prompt: 'continue' });
|
|
389
|
+
hook(s.env, { ...base, session_id: 'cd-1', hook_event_name: 'Stop', last_assistant_message: `ok\n${LABEL.replace('"contextDependent":false', '"contextDependent":true')}` });
|
|
390
|
+
hook(s.env, { ...base, session_id: 'cd-2', hook_event_name: 'UserPromptSubmit', prompt: 'rename foo to bar in util.js' });
|
|
391
|
+
hook(s.env, { ...base, session_id: 'cd-2', hook_event_name: 'Stop', last_assistant_message: 'no label' });
|
|
392
|
+
const [a, b] = records(s.store);
|
|
393
|
+
const review = path.join(s.dir, 'decisions.json');
|
|
394
|
+
fs.writeFileSync(review, JSON.stringify({ schemaVersion: 1, review: 'harness-observation-review', decisions: [
|
|
395
|
+
{ id: a.id, contextDependent: false }, { id: b.id, validity: 'actionable', contextDependent: false, tier: 'tier2' }] }));
|
|
396
|
+
const out = path.join(s.dir, 'export');
|
|
397
|
+
const e = spawnSync('node', [EXPORTER, '--store', s.store, '--out', out, '--owner-review', review], { encoding: 'utf8', env: s.env });
|
|
398
|
+
assert.strictEqual(e.status, 0, e.stderr);
|
|
399
|
+
const labels = fs.readFileSync(path.join(out, 'labels-observed.jsonl'), 'utf8').trim().split('\n').map(JSON.parse);
|
|
400
|
+
assert.strictEqual(labels.find(l => l.id === a.id).contextDependent, false);
|
|
401
|
+
assert.deepStrictEqual((({ validity, contextDependent, tier }) => ({ validity, contextDependent, tier }))(labels.find(l => l.id === b.id)),
|
|
402
|
+
{ validity: 'actionable', contextDependent: false, tier: 'tier2' });
|
|
403
|
+
fs.writeFileSync(review, JSON.stringify({ schemaVersion: 1, review: 'harness-observation-review', decisions: [{ id: a.id, contextDependent: 'no' }] }));
|
|
404
|
+
const bad = spawnSync('node', [EXPORTER, '--store', s.store, '--out', out, '--owner-review', review], { encoding: 'utf8', env: s.env });
|
|
405
|
+
assert.notStrictEqual(bad.status, 0, 'invalid owner decisions are rejected');
|
|
406
|
+
const html = path.join(s.dir, 'review.html');
|
|
407
|
+
assert.strictEqual(spawnSync('node', [REVIEW, '--store', s.store, '--out', html], { encoding: 'utf8', env: s.env }).status, 0);
|
|
408
|
+
assert(fs.readFileSync(html, 'utf8').includes('contextDependent'), 'the review page records context dependence');
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
function agePending(store, sessionId, days) {
|
|
412
|
+
const file = path.join(store, 'sessions', `${require('crypto').createHash('sha256').update(sessionId).digest('hex').slice(0, 32)}.json`);
|
|
413
|
+
const state = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
414
|
+
const then = new Date(Date.now() - days * 86400000).toISOString();
|
|
415
|
+
state.pending.startedAt = then;
|
|
416
|
+
state.pending.previousAt = then;
|
|
417
|
+
state.lastFinalAt = then;
|
|
418
|
+
fs.writeFileSync(file, JSON.stringify(state)); // mtime stays fresh: the capture time must decide
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
test('resuming an expired unfinished turn does not renew its text', () => {
|
|
422
|
+
for (const resume of ['UserPromptSubmit', 'Stop']) {
|
|
423
|
+
const s = sandbox();
|
|
424
|
+
const sid = `resume-${resume}`;
|
|
425
|
+
const base = { session_id: sid, cwd: s.repo };
|
|
426
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'first MARKER-PREV-TURN' });
|
|
427
|
+
hook(s.env, { ...base, hook_event_name: 'Stop', last_assistant_message: 'answer MARKER-PREVIOUS' });
|
|
428
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'unfinished MARKER-PENDING' });
|
|
429
|
+
agePending(s.store, sid, 181);
|
|
430
|
+
if (resume === 'Stop') hook(s.env, { ...base, hook_event_name: 'Stop', last_assistant_message: `late\n${LABEL}` });
|
|
431
|
+
else hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'new prompt' });
|
|
432
|
+
const rec = records(s.store)[1];
|
|
433
|
+
assert.strictEqual(rec.status, 'expired', resume);
|
|
434
|
+
assert(Date.parse(rec.validUntil) <= Date.now(), `${resume}: the deadline is not renewed`);
|
|
435
|
+
assert(!fs.existsSync(path.join(s.store, 'text', `${rec.contentSha256}.json`)), `${resume}: no fresh blob for expired text`);
|
|
436
|
+
const out = path.join(s.dir, 'export');
|
|
437
|
+
assert.strictEqual(spawnSync('node', [EXPORTER, '--store', s.store, '--out', out], { encoding: 'utf8', env: s.env }).status, 0);
|
|
438
|
+
const exported = fs.readFileSync(path.join(out, 'prompts-observed.jsonl'), 'utf8');
|
|
439
|
+
assert(!/MARKER-PENDING|MARKER-PREVIOUS/.test(exported), `${resume}: expired text is not exported`);
|
|
440
|
+
const html = path.join(s.dir, 'review.html');
|
|
441
|
+
spawnSync('node', [REVIEW, '--store', s.store, '--out', html, '--all'], { encoding: 'utf8', env: s.env });
|
|
442
|
+
assert(!/MARKER-PENDING|MARKER-PREVIOUS/.test(fs.readFileSync(html, 'utf8')), `${resume}: expired text is not reviewed`);
|
|
443
|
+
const sessions = fs.readdirSync(path.join(s.store, 'sessions')).map(f => { try { return fs.readFileSync(path.join(s.store, 'sessions', f), 'utf8'); } catch (_) { return ''; } }).join('');
|
|
444
|
+
assert(!/MARKER-PENDING|MARKER-PREVIOUS/.test(sessions), `${resume}: session state drops expired text`);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test('resuming an unfinished turn inside the window keeps its text and original deadline', () => {
|
|
449
|
+
const s = sandbox();
|
|
450
|
+
const base = { session_id: 'resume-fresh', cwd: s.repo };
|
|
451
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'unfinished MARKER-KEEP' });
|
|
452
|
+
agePending(s.store, 'resume-fresh', 10);
|
|
453
|
+
hook(s.env, { ...base, hook_event_name: 'UserPromptSubmit', prompt: 'next' });
|
|
454
|
+
const [rec] = records(s.store);
|
|
455
|
+
assert.strictEqual(rec.status, 'active');
|
|
456
|
+
const deadline = Date.parse(rec.validUntil) - Date.now();
|
|
457
|
+
assert(deadline > 169 * 86400000 && deadline < 171 * 86400000, 'validUntil counts from capture, not from recovery');
|
|
458
|
+
const text = JSON.parse(fs.readFileSync(path.join(s.store, 'text', `${rec.contentSha256}.json`), 'utf8'));
|
|
459
|
+
assert(text.prompt.includes('MARKER-KEEP'));
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test('module and runner prefixes are classified by what they run', () => {
|
|
463
|
+
assert.strictEqual(lib.classifyCommand('python3 -m pytest test_calc.py -v'), 'test');
|
|
464
|
+
assert.strictEqual(lib.classifyCommand('python -m pip install requests'), 'package');
|
|
465
|
+
assert.strictEqual(lib.classifyCommand('npx jest --watch=false'), 'test');
|
|
466
|
+
assert.strictEqual(lib.classifyCommand('uv run pytest'), 'test');
|
|
467
|
+
assert.strictEqual(lib.classifyCommand('poetry run pytest -q'), 'test');
|
|
468
|
+
assert.strictEqual(lib.classifyCommand('pnpm exec vitest run'), 'test');
|
|
469
|
+
assert.strictEqual(lib.classifyCommand('python3 script.py'), 'shell');
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
test('the label contract is the router output\'s last section and covers every turn', () => {
|
|
473
|
+
const out = spawnSync('node', [path.join(ROOT, 'harness-everything/scripts/kernel-router.js'), 'What does README.md contain?'], { encoding: 'utf8' }).stdout;
|
|
474
|
+
const sections = out.split(/\n(?==> )/);
|
|
475
|
+
const last = sections[sections.length - 1];
|
|
476
|
+
assert(/^=> TURN LABEL LINE/.test(last) && last.includes('harness-label'), 'the label contract is printed last');
|
|
477
|
+
assert(/EVERY TURN/.test(last) && /null only when validity is invalid/.test(last));
|
|
478
|
+
const status = sections.find(x => x.startsWith('=> USER-VISIBLE HARNESS STATUS CONTRACT'));
|
|
479
|
+
assert(status && !status.includes('harness-label'), 'not scoped under the non-trivial Harness Status contract');
|
|
480
|
+
assert(/^14\. \*\*The turn label line is mandatory on every turn/m.test(fs.readFileSync(path.join(ROOT, 'AGENTS.md'), 'utf8')));
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test('router/self agreement counts only turns where the router chose a tier', () => {
|
|
484
|
+
const s = sandbox();
|
|
485
|
+
fs.mkdirSync(path.join(s.store, 'text'), { recursive: true });
|
|
486
|
+
const now = Date.now();
|
|
487
|
+
const recs = [['unclassified', 'tier1'], ['tier2', 'tier2'], ['tier3', 'tier2']].map(([router, self], k) => {
|
|
488
|
+
const sha = `${k}`.repeat(64);
|
|
489
|
+
fs.writeFileSync(path.join(s.store, 'text', `${sha}.json`), JSON.stringify({ prompt: `p${k}`, previous: '' }));
|
|
490
|
+
return { id: `r${k}`, source: 'observation', status: 'active', observedAt: new Date(now - (3 - k) * 1000).toISOString(),
|
|
491
|
+
validUntil: new Date(now + 86400000).toISOString(), contentSha256: sha, host: 'claude', turn: 1, writer: { sessionId: `s${k}` },
|
|
492
|
+
router: { tier: router }, behavior: lib.emptyBehavior(),
|
|
493
|
+
selfReport: { v: 1, validity: 'actionable', contextDependent: false, tier: self, intents: [], workflow: null, skills: [] } };
|
|
494
|
+
});
|
|
495
|
+
fs.writeFileSync(path.join(s.store, 'observations-index.json'), JSON.stringify({ schemaVersion: 1, records: recs }));
|
|
496
|
+
const out = path.join(s.dir, 'export');
|
|
497
|
+
const e = spawnSync('node', [EXPORTER, '--store', s.store, '--out', out], { encoding: 'utf8', env: s.env });
|
|
498
|
+
assert.strictEqual(e.status, 0, e.stderr);
|
|
499
|
+
const report = JSON.parse(fs.readFileSync(path.join(out, 'export-report.json'), 'utf8'));
|
|
500
|
+
assert.deepStrictEqual(report.agreement, { routerVsSelfTier: 0.5, routerVsSelfPairs: 2 });
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
test('label slips seen live: unknown intents dropped, unterminated final line accepted', () => {
|
|
504
|
+
const unknown = lib.parseLabelLine('ok\n<!-- harness-label {"v":1,"validity":"actionable","contextDependent":false,"tier":"tier1","intents":["lookup","git"],"workflow":null,"skills":[]} -->');
|
|
505
|
+
assert.strictEqual(unknown.reason, null);
|
|
506
|
+
assert.deepStrictEqual(unknown.label.intents, ['git']);
|
|
507
|
+
assert.deepStrictEqual(unknown.label.unknownIntents, ['lookup']);
|
|
508
|
+
const open = lib.parseLabelLine('M README.md\n\n<!-- harness-label {"v":1,"validity":"actionable","contextDependent":false,"tier":"tier1","intents":["git"],"workflow":null,"skills":[]}');
|
|
509
|
+
assert.strictEqual(open.reason, null, 'a final label line missing its closing --> is accepted');
|
|
510
|
+
assert.strictEqual(open.label.tier, 'tier1');
|
|
511
|
+
const middle = lib.parseLabelLine('<!-- harness-label {"v":1,"validity":"actionable","contextDependent":false,"tier":"tier1","intents":[],"workflow":null,"skills":[]}\nmore text');
|
|
512
|
+
assert.strictEqual(middle.label, null, 'an unterminated line is accepted only at the end');
|
|
513
|
+
assert.strictEqual(lib.parseLabelLine('<!-- harness-label {"v":1,"validity":"actionable","contextDependent":false,"tier":"tier9","intents":[],"workflow":null,"skills":[]} -->').reason, 'label-invalid');
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
test('Codex router hook returns its contract as JSON additionalContext', () => {
|
|
517
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(ROOT, 'plugins/harness-everything/hooks/hooks.json'), 'utf8')).hooks;
|
|
518
|
+
const commands = manifest.UserPromptSubmit.flatMap(g => g.hooks.map(h => h.command));
|
|
519
|
+
assert(commands.some(c => c.includes('codex-user-prompt.js')), 'the Codex manifest wraps the router');
|
|
520
|
+
assert(!commands.some(c => c.includes('kernel-router.js')), 'plain router stdout never reaches a Codex model');
|
|
521
|
+
for (const script of ['hooks/scripts/codex-user-prompt.js', 'plugins/harness-everything/hooks/scripts/codex-user-prompt.js']) {
|
|
522
|
+
const s = sandbox();
|
|
523
|
+
const r = spawnSync('node', [path.join(ROOT, script)], { input: JSON.stringify({ session_id: 'cx', turn_id: 't1', hook_event_name: 'UserPromptSubmit', prompt: 'git status', cwd: s.repo }), env: s.env, encoding: 'utf8', timeout: 10000 });
|
|
524
|
+
assert.strictEqual(r.status, 0, `${script}: ${r.stderr}`);
|
|
525
|
+
const out = JSON.parse(r.stdout);
|
|
526
|
+
assert.strictEqual(out.hookSpecificOutput.hookEventName, 'UserPromptSubmit', script);
|
|
527
|
+
assert(out.hookSpecificOutput.additionalContext.includes('TURN LABEL LINE'), script);
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test('hooks are registered for both hosts and the contract asks for the label line', () => {
|
|
532
|
+
// Claude Code (canonical) has PostToolUseFailure; the Codex plugin manifest has no such event.
|
|
533
|
+
for (const [file, events] of [['hooks/hooks.json', ['UserPromptSubmit', 'PostToolUse', 'PostToolUseFailure', 'Stop']],
|
|
534
|
+
['plugins/harness-everything/hooks/hooks.json', ['UserPromptSubmit', 'PostToolUse', 'Stop']]]) {
|
|
535
|
+
const hooks = JSON.parse(fs.readFileSync(path.join(ROOT, file), 'utf8')).hooks;
|
|
536
|
+
for (const event of events) {
|
|
537
|
+
const commands = (hooks[event] || []).flatMap(entry => entry.hooks.map(h => h.command));
|
|
538
|
+
assert(commands.some(c => c.includes('observation-hook.js')), `${file} ${event}`);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const router = spawnSync('node', [path.join(ROOT, 'harness-everything/scripts/kernel-router.js'), 'refactor the logger module across the repo'], { encoding: 'utf8' });
|
|
542
|
+
assert(router.stdout.includes('harness-label'), 'the router contract asks for the label line');
|
|
543
|
+
assert(fs.readFileSync(path.join(ROOT, 'AGENTS.md'), 'utf8').includes('harness-label'));
|
|
544
|
+
assert(fs.readFileSync(path.join(ROOT, 'PRIVACY.md'), 'utf8').includes('HARNESS_OBSERVATIONS=off'));
|
|
545
|
+
const telemetry = fs.readFileSync(path.join(ROOT, 'hooks/scripts/lib/telemetry.js'), 'utf8');
|
|
546
|
+
assert(/'prompt'/.test(telemetry) && /'transcript'/.test(telemetry), 'telemetry stays content-free');
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
if (failures) { console.error(`${failures} observation test(s) failed`); process.exit(1); }
|
|
550
|
+
console.log('system-one observation tests passed');
|
|
@@ -123,6 +123,22 @@ test('S1-C103 prompts that near-duplicate a holdout prompt are dropped', () => {
|
|
|
123
123
|
assert.ok(collect.jaccard('abcdef', 'abcdef') === 1 && collect.jaccard('abc', 'xyz') === 0);
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
+
test('S1-C107 every holdout guards against leakage: the defaults cover the tier and intent holdouts, and --holdout repeats', () => {
|
|
127
|
+
assert.deepEqual(collect.defaultHoldouts().map(p => path.relative(root, p).split(path.sep).join('/')),
|
|
128
|
+
['benchmarks/fixtures/system-one-holdout.json', 'benchmarks/fixtures/system-one-intent-holdout.json']);
|
|
129
|
+
const { home, appdata } = fakeHome();
|
|
130
|
+
const out = path.join(home, 'out');
|
|
131
|
+
const a = path.join(home, 'a.json'); const b = path.join(home, 'b.json');
|
|
132
|
+
fs.writeFileSync(a, JSON.stringify({ schemaVersion: 1, cases: [{ request: { context: 'Commit all changes.' } }] }));
|
|
133
|
+
fs.writeFileSync(b, JSON.stringify({ schemaVersion: 1, cases: [{ request: { context: 'Run npm test and report failures.' } }] }));
|
|
134
|
+
try {
|
|
135
|
+
const r = spawnSync(process.execPath, [path.join(root, 'scripts/system-one-collect.js'), '--home', home, '--appdata', appdata, '--out', out, '--holdout', a, '--holdout', b],
|
|
136
|
+
{ encoding: 'utf8', cwd: root });
|
|
137
|
+
assert.equal(r.status, 0, r.stderr);
|
|
138
|
+
assert.equal(JSON.parse(r.stdout).dropped.leak, 2);
|
|
139
|
+
} finally { fs.rmSync(home, { recursive: true, force: true }); }
|
|
140
|
+
});
|
|
141
|
+
|
|
126
142
|
test('S1-C104 splits are stable per family and near 15% validation', () => {
|
|
127
143
|
assert.equal(collect.splitFor('family-a'), collect.splitFor('family-a'));
|
|
128
144
|
const families = Array.from({ length: 2000 }, (_, i) => `session-${i}`);
|