harness-everything 0.30.8 → 0.30.10
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/.github/workflows/ci.yml +31 -0
- package/CHANGELOG.md +21 -0
- package/README.md +1 -1
- package/benchmarks/results/live-host/codex-2026-10-13/README.md +55 -0
- package/benchmarks/results/live-host/codex-2026-10-13/observation-records.json +88 -0
- package/ci/doc-capability-consistency.test.js +11 -1
- package/ci/fixtures/system-one-resident-stub.py +7 -1
- package/ci/mechanism-2al-codex-hooks-compat.test.js +19 -0
- package/ci/mechanism-2am-observations-session-import.test.js +221 -0
- package/ci/mechanism-2ao-plugin-sync.test.js +129 -4
- package/ci/mechanism-2w-platform-compatibility.test.js +12 -0
- package/ci/mechanism-34-observations.test.js +64 -0
- package/ci/mechanism-system-one-resident.test.js +70 -3
- package/docs/platform-capabilities.md +8 -8
- package/docs/platform-compatibility.json +5 -5
- package/docs/system-one-observations.md +35 -8
- package/hooks/scripts/lib/observations.js +88 -12
- 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/scripts/lib/observations.js +88 -12
- package/plugins/harness-everything/plugin.json +1 -1
- package/sbom.cdx.json +7 -7
- package/scripts/codex-hooks-compat.js +39 -14
- package/scripts/plugin-sync.js +100 -29
- package/scripts/system-one-observations-export.js +29 -2
- package/scripts/system-one-observations-import-codex-sessions.js +307 -0
- package/scripts/system-one-observations-review.js +11 -1
|
@@ -14,14 +14,18 @@ const {
|
|
|
14
14
|
} = require('../scripts/plugin-sync');
|
|
15
15
|
|
|
16
16
|
const ROOT = path.resolve(__dirname, '..');
|
|
17
|
+
const codexHooksCompat = require('../scripts/codex-hooks-compat');
|
|
17
18
|
|
|
18
|
-
function fakeHost({ claudeInstalled = false, codexInstalled = false, claudeMarketplace = true, codexMarketplace = true, unknownClaudePluginState = false, unknownClaudeYesFlag = false, unknownClaudeJsonFlag = false, clapCodexJsonFlag = false, missingCodexPluginCommand = false } = {}) {
|
|
19
|
+
function fakeHost({ claudeInstalled = false, codexInstalled = false, codexPluginSourcePath = path.join(ROOT, 'plugins', 'harness-everything'), codexPluginSourceType = 'local', codexAvailableOnly = false, claudeMarketplace = true, codexMarketplace = true, unknownClaudePluginState = false, unknownClaudeYesFlag = false, unknownClaudeJsonFlag = false, clapCodexJsonFlag = false, missingCodexPluginCommand = false, codexHooksError = null } = {}) {
|
|
19
20
|
const calls = [];
|
|
21
|
+
const codexHookInstalls = [];
|
|
20
22
|
const state = {
|
|
21
23
|
claudeInstalled,
|
|
22
24
|
codexInstalled,
|
|
23
25
|
claudeMarketplace,
|
|
24
|
-
codexMarketplace
|
|
26
|
+
codexMarketplace,
|
|
27
|
+
codexPluginSourcePath,
|
|
28
|
+
codexAvailableOnly
|
|
25
29
|
};
|
|
26
30
|
const runner = (command, args) => {
|
|
27
31
|
calls.push({ command, args: [...args] });
|
|
@@ -93,13 +97,29 @@ function fakeHost({ claudeInstalled = false, codexInstalled = false, claudeMarke
|
|
|
93
97
|
}
|
|
94
98
|
if (joined.startsWith('plugin marketplace upgrade')) return { status: 0, stdout: '{}', stderr: '' };
|
|
95
99
|
if (joined.includes('plugin list --marketplace harness-everything --json') || joined === 'plugin list --json') {
|
|
96
|
-
|
|
100
|
+
const source = state.codexPluginSourcePath ? {
|
|
101
|
+
source: codexPluginSourceType,
|
|
102
|
+
...(codexPluginSourceType === 'git-subdir' ? { url: 'https://github.com/dyphn1/Harness-everything.git' } : {}),
|
|
103
|
+
path: state.codexPluginSourcePath
|
|
104
|
+
} : null;
|
|
105
|
+
if (state.codexAvailableOnly) {
|
|
106
|
+
return { status: 0, stdout: JSON.stringify({ installed: [], available: [{
|
|
107
|
+
pluginId: 'harness-everything@harness-everything',
|
|
108
|
+
name: 'harness-everything',
|
|
109
|
+
marketplaceName: 'harness-everything',
|
|
110
|
+
installed: false,
|
|
111
|
+
source
|
|
112
|
+
}] }), stderr: '' };
|
|
113
|
+
}
|
|
114
|
+
const plugin = state.codexInstalled ? [{ name: 'harness-everything', marketplace: 'harness-everything', ...(source ? { source } : {}) }] : [];
|
|
115
|
+
return { status: 0, stdout: JSON.stringify({ plugins: plugin }), stderr: '' };
|
|
97
116
|
}
|
|
98
117
|
if (joined === 'plugin list --marketplace harness-everything' || joined === 'plugin list') {
|
|
99
118
|
return { status: 0, stdout: state.codexInstalled ? 'harness-everything@harness-everything\n' : '', stderr: '' };
|
|
100
119
|
}
|
|
101
120
|
if (joined.startsWith('plugin add')) {
|
|
102
121
|
state.codexInstalled = true;
|
|
122
|
+
state.codexAvailableOnly = false;
|
|
103
123
|
return { status: 0, stdout: '{}', stderr: '' };
|
|
104
124
|
}
|
|
105
125
|
}
|
|
@@ -110,7 +130,13 @@ function fakeHost({ claudeInstalled = false, codexInstalled = false, claudeMarke
|
|
|
110
130
|
return {
|
|
111
131
|
calls,
|
|
112
132
|
runner,
|
|
113
|
-
commandAvailable: () => true
|
|
133
|
+
commandAvailable: () => true,
|
|
134
|
+
codexHookInstalls,
|
|
135
|
+
codexHooksInstall: options => {
|
|
136
|
+
codexHookInstalls.push(options);
|
|
137
|
+
if (codexHooksError) throw new Error(codexHooksError);
|
|
138
|
+
return { status: codexInstalled ? 'updated' : 'installed', entries: 8, trustChanged: false };
|
|
139
|
+
}
|
|
114
140
|
};
|
|
115
141
|
}
|
|
116
142
|
|
|
@@ -176,6 +202,17 @@ assert.strictEqual(findPlugin({ plugins: [{ id: 'harness-everything@harness-ever
|
|
|
176
202
|
assert.strictEqual(result.failed, 0);
|
|
177
203
|
assert.strictEqual(result.results[0].action, 'install');
|
|
178
204
|
assert.strictEqual(callsFor(fake.calls, 'codex', 'plugin add').length, 1, 'absent Codex plugin must install');
|
|
205
|
+
assert.strictEqual(fake.codexHookInstalls.length, 1, 'install must also refresh the Codex compatibility hooks');
|
|
206
|
+
assert.strictEqual(result.results[0].compatHooks.status, 'installed');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
{
|
|
210
|
+
const fake = fakeHost({ codexAvailableOnly: true });
|
|
211
|
+
const result = runHost('codex', fake);
|
|
212
|
+
assert.strictEqual(result.failed, 0, 'an available-only entry must be installed and verified after its state changes');
|
|
213
|
+
assert.strictEqual(result.results[0].action, 'install', 'installed:false must not make an available plugin appear installed');
|
|
214
|
+
assert.strictEqual(callsFor(fake.calls, 'codex', 'plugin add').length, 1, 'available-only entries must take the plugin add path');
|
|
215
|
+
assert.strictEqual(fake.codexHookInstalls.length, 1, 'hooks refresh only after the post-add listing verifies an installed local source');
|
|
179
216
|
}
|
|
180
217
|
|
|
181
218
|
{
|
|
@@ -185,8 +222,82 @@ assert.strictEqual(findPlugin({ plugins: [{ id: 'harness-everything@harness-ever
|
|
|
185
222
|
assert.strictEqual(result.results[0].action, 'update');
|
|
186
223
|
assert.strictEqual(callsFor(fake.calls, 'codex', 'plugin marketplace upgrade').length, 1, 'installed Codex plugin must upgrade its marketplace');
|
|
187
224
|
assert.strictEqual(callsFor(fake.calls, 'codex', 'plugin add').length, 0, 'installed Codex plugin must not be re-added');
|
|
225
|
+
assert.strictEqual(fake.codexHookInstalls.length, 1, 'marketplace update must refresh the copied Codex hooks');
|
|
226
|
+
assert.strictEqual(result.results[0].compatHooks.status, 'updated');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
{
|
|
230
|
+
const installedSource = path.join(os.tmpdir(), 'newer-marketplace-plugin');
|
|
231
|
+
const fake = fakeHost({ codexInstalled: true, codexPluginSourcePath: installedSource });
|
|
232
|
+
const result = runHost('codex', fake);
|
|
233
|
+
assert.strictEqual(result.failed, 0);
|
|
234
|
+
assert.strictEqual(fake.codexHookInstalls[0].sourcePluginRoot, installedSource,
|
|
235
|
+
'compatibility hooks must be copied from the installed marketplace plugin, not this older package checkout');
|
|
236
|
+
assert.notStrictEqual(fake.codexHookInstalls[0].sourcePluginRoot, codexHooksCompat.SOURCE_PLUGIN_ROOT);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
{
|
|
240
|
+
const fake = fakeHost({ codexInstalled: true, codexPluginSourcePath: null });
|
|
241
|
+
const result = runHost('codex', fake);
|
|
242
|
+
assert.strictEqual(result.failed, 1, 'sync must fail visibly when it cannot identify the installed plugin source');
|
|
243
|
+
assert.strictEqual(fake.codexHookInstalls.length, 0, 'sync must not overwrite compatibility hooks from an unverified local package');
|
|
244
|
+
assert.match(result.results[0].error, /source/i);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
{
|
|
248
|
+
const staleCheckout = path.join(ROOT, 'plugins', 'harness-everything');
|
|
249
|
+
const fake = fakeHost({ codexInstalled: true, codexPluginSourcePath: staleCheckout, codexPluginSourceType: 'git-subdir' });
|
|
250
|
+
const result = runHost('codex', fake);
|
|
251
|
+
assert.strictEqual(result.failed, 1, 'a tagged git-subdir source must not be treated as a local plugin directory');
|
|
252
|
+
assert.strictEqual(fake.codexHookInstalls.length, 0, 'an unsupported source variant must not copy hooks from the stale package checkout');
|
|
253
|
+
assert.match(result.results[0].error, /git-subdir|source/i);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
{
|
|
257
|
+
const fake = fakeHost({
|
|
258
|
+
codexInstalled: true,
|
|
259
|
+
codexPluginSourcePath: 'plugins/harness-everything',
|
|
260
|
+
codexPluginSourceType: 'git-subdir'
|
|
261
|
+
});
|
|
262
|
+
const result = runHost('codex', fake);
|
|
263
|
+
assert.strictEqual(result.failed, 1, 'a repository-relative git-subdir path must not be resolved against the sync process CWD');
|
|
264
|
+
assert.strictEqual(fake.codexHookInstalls.length, 0, 'a git-subdir URL and relative path must not be passed to the local compatibility installer');
|
|
265
|
+
assert.match(result.results[0].error, /git-subdir|source/i);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
for (const sourceType of ['git', 'npm', 'remote', 'unknown']) {
|
|
269
|
+
const fake = fakeHost({ codexInstalled: true, codexPluginSourcePath: path.join(ROOT, 'plugins', 'harness-everything'), codexPluginSourceType: sourceType });
|
|
270
|
+
const result = runHost('codex', fake);
|
|
271
|
+
assert.strictEqual(result.failed, 1, `the ${sourceType} source variant must fail closed`);
|
|
272
|
+
assert.strictEqual(fake.codexHookInstalls.length, 0, `the ${sourceType} source variant must not run the compatibility installer`);
|
|
188
273
|
}
|
|
189
274
|
|
|
275
|
+
{
|
|
276
|
+
const fake = fakeHost({ codexInstalled: true, codexPluginSourcePath: 'plugins/harness-everything' });
|
|
277
|
+
const result = runHost('codex', fake);
|
|
278
|
+
assert.strictEqual(result.failed, 1, 'a relative local source path is not a verified installed plugin directory');
|
|
279
|
+
assert.strictEqual(fake.codexHookInstalls.length, 0, 'a relative source path must not be copied from the sync process CWD');
|
|
280
|
+
assert.match(result.results[0].error, /absolute|source/i);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
{
|
|
284
|
+
const codexHome = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-plugin-sync-codex-home-'));
|
|
285
|
+
try {
|
|
286
|
+
const fake = fakeHost({ codexInstalled: true });
|
|
287
|
+
const result = sync({ host: 'codex', codexHome }, {
|
|
288
|
+
runner: fake.runner,
|
|
289
|
+
commandAvailable: fake.commandAvailable,
|
|
290
|
+
log: () => {},
|
|
291
|
+
warn: () => {},
|
|
292
|
+
});
|
|
293
|
+
assert.strictEqual(result.failed, 0);
|
|
294
|
+
assert.strictEqual(result.results[0].compatHooks.status, 'installed');
|
|
295
|
+
const state = codexHooksCompat.status({ codexHome });
|
|
296
|
+
assert(state.installed && state.healthy, 'plugin-sync default path installs the actual packaged compatibility hooks');
|
|
297
|
+
} finally {
|
|
298
|
+
fs.rmSync(codexHome, { recursive: true, force: true });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
190
301
|
{
|
|
191
302
|
const fake = fakeHost({ codexInstalled: false, codexMarketplace: false, clapCodexJsonFlag: true });
|
|
192
303
|
const result = runHost('codex', fake);
|
|
@@ -210,6 +321,20 @@ assert.strictEqual(findPlugin({ plugins: [{ id: 'harness-everything@harness-ever
|
|
|
210
321
|
assert.ok(!upgradeCalls[1].args.includes('--json'), 'Codex upgrade retry must omit unsupported --json');
|
|
211
322
|
}
|
|
212
323
|
|
|
324
|
+
{
|
|
325
|
+
const fake = fakeHost({ codexInstalled: true, codexHooksError: 'user hook config drift' });
|
|
326
|
+
const result = runHost('codex', fake);
|
|
327
|
+
assert.strictEqual(result.failed, 1, 'a failed compatibility-hook refresh must be visible');
|
|
328
|
+
assert.match(result.results[0].error, /compatibility hooks/i);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
{
|
|
332
|
+
const fake = fakeHost({ codexInstalled: true });
|
|
333
|
+
const result = sync({ host: 'codex', dryRun: true }, { ...fake, log: () => {}, warn: () => {} });
|
|
334
|
+
assert.strictEqual(result.results[0].compatHooks.status, 'dry-run');
|
|
335
|
+
assert.strictEqual(fake.codexHookInstalls.length, 0, 'dry-run must not install or rewrite Codex hooks');
|
|
336
|
+
}
|
|
337
|
+
|
|
213
338
|
{
|
|
214
339
|
const fake = fakeHost({ codexMarketplace: false, missingCodexPluginCommand: true, clapCodexJsonFlag: true });
|
|
215
340
|
const result = runHost('codex', fake);
|
|
@@ -85,6 +85,18 @@ for (const platform of matrix.platforms) {
|
|
|
85
85
|
assert.ok(compliance.totalEdits > 0);
|
|
86
86
|
assert.ok(fs.statSync(path.join(session, 'edit-state.json')).size > 0);
|
|
87
87
|
assert.ok(fs.statSync(path.join(session, 'zoom-out-report.md')).size > 0);
|
|
88
|
+
} else if (platform.id === 'codex') {
|
|
89
|
+
const capability = platform.capabilities.liveHostVerification;
|
|
90
|
+
assert.strictEqual(capability.status, 'Partial');
|
|
91
|
+
assert.strictEqual(capability.evidence, 'benchmarks/results/live-host/codex-2026-10-13/README.md');
|
|
92
|
+
const artifactRoot = path.dirname(path.join(ROOT, capability.evidence));
|
|
93
|
+
for (const file of ['README.md', 'observation-records.json']) {
|
|
94
|
+
assert.ok(fs.statSync(path.join(artifactRoot, file)).size > 0, `Missing Codex evidence: ${file}`);
|
|
95
|
+
}
|
|
96
|
+
const evidence = JSON.parse(fs.readFileSync(path.join(artifactRoot, 'observation-records.json'), 'utf8'));
|
|
97
|
+
assert.strictEqual(evidence.format, 'harness-codex-live-observation-evidence-v1');
|
|
98
|
+
assert.strictEqual(evidence.turns.length, 2, 'Codex artifact must preserve both consecutive turns');
|
|
99
|
+
assert.ok(evidence.turns.every(turn => turn.host === 'codex' && turn.selfReportReason === null));
|
|
88
100
|
} else {
|
|
89
101
|
assert.strictEqual(platform.capabilities.liveHostVerification.status, 'Unknown', `${platform.id}: no preserved live evidence`);
|
|
90
102
|
}
|
|
@@ -58,6 +58,45 @@ test('commands are classified without keeping their text', () => {
|
|
|
58
58
|
assert.strictEqual(lib.classifyCommand('cd x && git push'), 'git', 'the first recognizable segment decides');
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
test('detailed appends distinguish duplicates and bounded lock timeouts', () => {
|
|
62
|
+
const s = sandbox();
|
|
63
|
+
const record = {
|
|
64
|
+
id: 'append-detail-one',
|
|
65
|
+
host: 'codex',
|
|
66
|
+
turn: 1,
|
|
67
|
+
writer: { sessionId: 'append-detail-session' },
|
|
68
|
+
contentSha256: 'append-detail-content-one',
|
|
69
|
+
};
|
|
70
|
+
const options = { dedupeById: true, dedupeByTurn: true };
|
|
71
|
+
assert.deepStrictEqual(lib.appendRecordDetailed(s.store, record, { prompt: 'first' }, options), { status: 'appended' });
|
|
72
|
+
assert.strictEqual(lib.appendRecord(s.store, record, { prompt: 'first' }, options), false,
|
|
73
|
+
'the legacy boolean wrapper still reports a duplicate as false');
|
|
74
|
+
assert.deepStrictEqual(lib.appendRecordDetailed(s.store, {
|
|
75
|
+
...record,
|
|
76
|
+
id: 'append-detail-ambiguous',
|
|
77
|
+
captureMethod: 'session-backfill',
|
|
78
|
+
writer: { sessionId: 'append-detail-session', turnId: 'turn-one' },
|
|
79
|
+
contentSha256: 'different-content-for-legacy-turn',
|
|
80
|
+
}, { prompt: 'uncertain match' }, options), { status: 'ambiguous' },
|
|
81
|
+
'a partial legacy live-turn match is checked under the append lock and remains distinct from an exact duplicate');
|
|
82
|
+
|
|
83
|
+
const lockPath = path.join(s.store, '.index.lock');
|
|
84
|
+
fs.writeFileSync(lockPath, 'held by deterministic fixture');
|
|
85
|
+
const blocked = lib.appendRecordDetailed(s.store, {
|
|
86
|
+
...record,
|
|
87
|
+
id: 'append-detail-two',
|
|
88
|
+
turn: 2,
|
|
89
|
+
writer: { sessionId: 'append-detail-session', turnId: 'turn-two' },
|
|
90
|
+
contentSha256: 'append-detail-content-two',
|
|
91
|
+
}, { prompt: 'second' }, { ...options, lockTimeoutMs: 25 });
|
|
92
|
+
assert.deepStrictEqual(blocked, { status: 'lock-timeout' }, 'lock contention is distinct from a duplicate');
|
|
93
|
+
assert.strictEqual(lib.markSessionBackfillBehaviorUnobserved(s.store, { lockTimeoutMs: 25 }), null,
|
|
94
|
+
'migration reports lock acquisition failure consistently');
|
|
95
|
+
assert.strictEqual(JSON.parse(fs.readFileSync(path.join(s.store, 'observations-index.json'), 'utf8')).records.length, 1,
|
|
96
|
+
'a timed out append leaves no durable index record');
|
|
97
|
+
fs.unlinkSync(lockPath);
|
|
98
|
+
});
|
|
99
|
+
|
|
61
100
|
test('tier comes from the self-report, owner review overrides, behavior only flags contradictions', () => {
|
|
62
101
|
assert.strictEqual(typeof lib.breadthTier, 'undefined', 'no file-count tier rule');
|
|
63
102
|
assert.deepStrictEqual(lib.labelTier({ tier: 'tier2' }, null), { tier: 'tier2', source: 'self-report' });
|
|
@@ -222,6 +261,31 @@ test('exporter derives labels, splits by time and reports agreement', () => {
|
|
|
222
261
|
assert(!fs.readFileSync(path.join(out, 'export-report.json'), 'utf8').includes('parser'), 'the report holds no text');
|
|
223
262
|
});
|
|
224
263
|
|
|
264
|
+
test('exporter can select post-cutoff rows with self-reported labels for training', () => {
|
|
265
|
+
const s = sandbox();
|
|
266
|
+
writeFixtureStore(s);
|
|
267
|
+
const indexFile = path.join(s.store, 'observations-index.json');
|
|
268
|
+
const index = JSON.parse(fs.readFileSync(indexFile, 'utf8'));
|
|
269
|
+
index.records[0].observedAt = '2026-09-26T23:59:59.000Z';
|
|
270
|
+
index.records.push({ ...index.records[1], id: 'missing-self-report', selfReport: null });
|
|
271
|
+
fs.writeFileSync(indexFile, JSON.stringify(index));
|
|
272
|
+
const out = path.join(s.dir, 'filtered-export');
|
|
273
|
+
const r = spawnSync('node', [EXPORTER, '--store', s.store, '--out', out, '--since', '2026-09-27', '--self-report-only'], {
|
|
274
|
+
encoding: 'utf8',
|
|
275
|
+
env: s.env,
|
|
276
|
+
});
|
|
277
|
+
assert.strictEqual(r.status, 0, r.stderr);
|
|
278
|
+
const prompts = fs.readFileSync(path.join(out, 'prompts-observed.jsonl'), 'utf8').trim().split('\n').map(JSON.parse);
|
|
279
|
+
const labels = fs.readFileSync(path.join(out, 'labels-observed.jsonl'), 'utf8').trim().split('\n').map(JSON.parse);
|
|
280
|
+
assert.strictEqual(prompts.length, 3);
|
|
281
|
+
assert.strictEqual(labels.length, 3);
|
|
282
|
+
assert(labels.every(label => label.validity && label.intents.length >= 0), 'only self-reported turns are included');
|
|
283
|
+
const report = JSON.parse(fs.readFileSync(path.join(out, 'export-report.json'), 'utf8'));
|
|
284
|
+
assert.strictEqual(report.since, '2026-09-27');
|
|
285
|
+
assert.strictEqual(report.selfReportOnly, true);
|
|
286
|
+
assert.strictEqual(report.skippedBeforeSince, 1);
|
|
287
|
+
assert.strictEqual(report.skippedUnlabeled, 1);
|
|
288
|
+
});
|
|
225
289
|
test('review page lists disagreements blind and owner decisions override', () => {
|
|
226
290
|
const s = sandbox();
|
|
227
291
|
writeFixtureStore(s);
|
|
@@ -29,10 +29,57 @@ function fixture(modelId = 'stub', extra = {}) {
|
|
|
29
29
|
const deps = { command: m => { spawns.push(m.modelId); return [python, [stub, '--serve']]; } };
|
|
30
30
|
return { dir, manifest, manifestPath, spawns, deps };
|
|
31
31
|
}
|
|
32
|
+
// State-file unlink precedes process exit. Windows cleanup must wait for the
|
|
33
|
+
// captured resident PID, not merely for the disappearance of its state file.
|
|
34
|
+
function pidAlive(pid) {
|
|
35
|
+
try { process.kill(pid, 0); return true; }
|
|
36
|
+
catch (err) { return err.code === 'EPERM'; }
|
|
37
|
+
}
|
|
32
38
|
function cleanup(f) {
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
const { state } = resident.stateFiles(f.manifest, f.manifestPath);
|
|
40
|
+
const server = resident.readState(state);
|
|
41
|
+
try { resident.stop(f.manifest, f.manifestPath); } catch (_) { /* best effort after an assertion failure */ }
|
|
42
|
+
const deadline = Date.now() + 7500;
|
|
43
|
+
while ((fs.existsSync(state) || (process.platform === 'win32' && server && pidAlive(server.pid)))
|
|
44
|
+
&& Date.now() < deadline) sleep(25);
|
|
45
|
+
assert.equal(fs.existsSync(state), false, 'resident state must disappear before fixture removal');
|
|
46
|
+
if (process.platform === 'win32' && server) {
|
|
47
|
+
assert.equal(pidAlive(server.pid), false, `detached resident PID ${server.pid} survived shutdown`);
|
|
48
|
+
}
|
|
49
|
+
fs.rmSync(f.dir, { recursive: true, force: true, maxRetries: 20, retryDelay: 50 });
|
|
50
|
+
}
|
|
51
|
+
// Windows Node 23 (nodejs/node#56645) and Node 24 before the fix in
|
|
52
|
+
// 24.20.0 (nodejs/node#61999) can abort during libuv/process shutdown.
|
|
53
|
+
// Preserve RS13's actual evaluator subprocess on unaffected runtimes.
|
|
54
|
+
function windowsNodeShutdownIssue(platform, version) {
|
|
55
|
+
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
|
|
56
|
+
if (platform !== 'win32' || !parts) return false;
|
|
57
|
+
const major = Number(parts[1]);
|
|
58
|
+
if (major === 23) {
|
|
59
|
+
return 'Windows Node 23.x has a known process-shutdown assertion (nodejs/node#56645); use Node 22 or Node 24.20+';
|
|
60
|
+
}
|
|
61
|
+
if (major === 24 && Number(parts[2]) < 20) {
|
|
62
|
+
return 'Windows Node 24 < 24.20.0 lacks upstream shutdown fix nodejs/node#61999; use Node 24.20+ or Node 22';
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
35
65
|
}
|
|
66
|
+
const nodeShutdownIssue = windowsNodeShutdownIssue(process.platform, process.versions.node);
|
|
67
|
+
|
|
68
|
+
test('S1-RS00 Windows Node 23 and 24 shutdown compatibility boundary', () => {
|
|
69
|
+
for (const version of ['23.0.0', '23.4.0', '23.6.0', '23.11.1']) {
|
|
70
|
+
assert.match(windowsNodeShutdownIssue('win32', version), /Node 23/);
|
|
71
|
+
assert.equal(windowsNodeShutdownIssue('linux', version), false);
|
|
72
|
+
assert.equal(windowsNodeShutdownIssue('darwin', version), false);
|
|
73
|
+
}
|
|
74
|
+
for (const version of ['24.0.0', '24.15.0', '24.18.0', '24.19.9']) {
|
|
75
|
+
assert.match(windowsNodeShutdownIssue('win32', version), /Node 24/);
|
|
76
|
+
assert.equal(windowsNodeShutdownIssue('linux', version), false);
|
|
77
|
+
}
|
|
78
|
+
for (const version of ['22.16.0', '24.20.0', '24.21.0', '25.0.0', '26.0.0', 'bogus']) {
|
|
79
|
+
assert.equal(windowsNodeShutdownIssue('win32', version), false, version);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
36
83
|
// A client view whose state file names the live server with a wrong token. The server polls only its own
|
|
37
84
|
// state file and exits once that file stops naming it, so rewriting it would race the wrong-token request.
|
|
38
85
|
function wrongTokenView(f) {
|
|
@@ -295,7 +342,7 @@ test('S1-RS12 the evaluator times the async path: no worker thread per warm scor
|
|
|
295
342
|
} finally { cleanup(f); }
|
|
296
343
|
});
|
|
297
344
|
|
|
298
|
-
test('S1-RS13 manifest acceptance thresholds are validated, carried by scored results and applied by the evaluator', async () => {
|
|
345
|
+
test('S1-RS13 manifest acceptance thresholds are validated, carried by scored results and applied by the evaluator', { skip: nodeShutdownIssue }, async () => {
|
|
299
346
|
const acceptance = { minConfidence: 0.6, minMargin: 0.3 };
|
|
300
347
|
const base = fixture();
|
|
301
348
|
try {
|
|
@@ -344,3 +391,23 @@ test('S1-RS14 resident transport names its adapter and stays inside the scripts
|
|
|
344
391
|
assert.throws(() => resident.defaultCommand({ ...f.manifest, adapter: '../evil.py' }));
|
|
345
392
|
} finally { cleanup(f); }
|
|
346
393
|
});
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
// The test server keeps its PID alive for 500 ms after state-file unlink.
|
|
397
|
+
// A state-only teardown passes too early, whereas PID-aware cleanup cannot.
|
|
398
|
+
test('S1-RS15 repeated teardown waits for resident PID and removes every fixture', () => {
|
|
399
|
+
for (let round = 0; round < 6; round++) {
|
|
400
|
+
const f = fixture('stub-exit-linger');
|
|
401
|
+
let pid;
|
|
402
|
+
try {
|
|
403
|
+
assert.equal(resident.ensureReady(f.manifest, f.manifestPath, 20000, f.deps), true);
|
|
404
|
+
const state = resident.readState(resident.stateFiles(f.manifest, f.manifestPath).state);
|
|
405
|
+
assert.ok(state && state.pid > 0);
|
|
406
|
+
pid = state.pid;
|
|
407
|
+
} finally { cleanup(f); }
|
|
408
|
+
assert.equal(fs.existsSync(f.dir), false, `fixture remained after teardown ${round}`);
|
|
409
|
+
if (process.platform === 'win32') {
|
|
410
|
+
assert.equal(pidAlive(pid), false, `resident PID ${pid} leaked in teardown ${round}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
});
|
|
@@ -15,7 +15,7 @@ Statuses are intentionally explicit:
|
|
|
15
15
|
- `Unsupported`: official host documentation explicitly rules out the capability.
|
|
16
16
|
- `Unknown`: the capability or its current host behavior was not established.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Codex and OpenCode are `Partial` in `liveHostVerification`. The Codex artifact verifies user-hook compatibility capture for two consecutive turns on one Windows Codex CLI host; native plugin-hook mounting and other host/version combinations remain unverified. OpenCode's retained evidence supports project-scope `.js` loading and edit/verification state on OpenCode 1.18.31 (macOS), not durable hard enforcement. No fresh artifacts exist yet for the other surfaces, managed-workspace import, or public OpenAI approval.
|
|
19
19
|
|
|
20
20
|
### Routing contract vs. host evidence
|
|
21
21
|
|
|
@@ -27,7 +27,7 @@ This is a **repository/agent contract**, not an automatic upgrade to any platfor
|
|
|
27
27
|
|
|
28
28
|
| Platform surface | Standalone skills | Plugin support | Hooks/lifecycle | Project scope | Global scope | Current evidence boundary |
|
|
29
29
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
30
|
-
| Codex | `Supported` | `Mechanism verified` | `
|
|
30
|
+
| Codex | `Supported` | `Mechanism verified` | `Partial`: user-hook compatibility capture is live-verified for `UserPromptSubmit` and `Stop` on one Windows Codex CLI host; native plugin hooks remain mechanism-verified | `.agents/skills/`, `AGENTS.md` | `~/.agents/skills/` | [Live evidence](../benchmarks/results/live-host/codex-2026-10-13/README.md) verifies two consecutive turns on Codex CLI 0.162.1; native plugin mounting, other lifecycle events, and other versions remain unverified. |
|
|
31
31
|
| ChatGPT / OpenAI Plugin | `Unknown` for a repository-local standalone path | `Mechanism verified` | `Partial`: local Work/Codex hooks are separate from ordinary Chat | Managed workspace marketplace | Not established | The public Skills-only bundle is mechanically reproducible; import, approval, and live behavior remain `Unknown`. |
|
|
32
32
|
| Claude Code | `Supported` | `Supported` | `Mechanism verified` for Harness configuration | `.claude/skills/` | `~/.claude/skills/` | Native Claude surfaces and the state-specific plugin sync command are mechanically checked; fresh-host execution is not preserved here. |
|
|
33
33
|
| OpenCode | `Supported` | `Partial` live-host loading for the project-scope, `.js`-filename install path on OpenCode 1.18.31 (macOS); other install paths remain `Mechanism verified` | `Partial` live-host evidence for project-scope `.js` loading and edit/verification state; hard lock reported only, with no retained blocked-tool trace and a post-reset final snapshot | `.opencode/skills/`, `.agents/skills/`, `.opencode/plugins/` | `~/.config/opencode/skills/`, `~/.agents/skills/`, `~/.config/opencode/plugins/` | [Retained evidence](../benchmarks/results/live-host/opencode-2026-09-16/README.md): loading and state effects verified in one host session; final snapshot post-reset; hard lock only an interactive observation; reflection operator-seeded, then agent-rewritten; no raw transcript or behavioral-effectiveness claim. `.mjs` auto-discovery broken (issue #127); global scope, npm-package installation, and other host versions remain unverified. |
|
|
@@ -68,9 +68,9 @@ The advisory installer text generated for Codex/Cursor/Copilot/Continue/Hermes c
|
|
|
68
68
|
Codex has two paths that must not be collapsed:
|
|
69
69
|
|
|
70
70
|
1. The general `--codex` installer writes advisory `AGENTS.md` plus repo-scoped skills under `.agents/skills/`. Its generated instructions require reading/evaluating every router-suggested skill before omission, but this remains instruction-governed.
|
|
71
|
-
2. The local OpenAI plugin at `plugins/harness-everything/` packages the skills plus lifecycle hooks for session start, prompt routing, supported local tool calls, subagent lifecycle, and stop verification. Those hooks are mechanically checked
|
|
71
|
+
2. The local OpenAI plugin at `plugins/harness-everything/` packages the skills plus lifecycle hooks for session start, prompt routing, supported local tool calls, subagent lifecycle, and stop verification. Those hooks are mechanically checked. A separate live run verifies the user-hook compatibility fallback for prompt/stop observation only; native plugin hook mounting remains unverified.
|
|
72
72
|
|
|
73
|
-
The package
|
|
73
|
+
The package remains subject to the host’s hook review/trust flow. The retained Codex CLI session proves that the selected user-level observation hooks were trusted and fired on one Windows setup; it does not prove that the model followed read-before-skip or that another ChatGPT/Codex installation loaded the same hooks.
|
|
74
74
|
|
|
75
75
|
### Native plugin install/update synchronization
|
|
76
76
|
|
|
@@ -98,13 +98,13 @@ The command detects Claude Code and Codex independently, ensures the Harness mar
|
|
|
98
98
|
|
|
99
99
|
Mutating commands are issued with the current option set and retried without any option the host rejects, covering both commander wording (`unknown option '--json'`) and clap wording (`unexpected argument '--json' found`), so older Claude and Codex builds stay supported.
|
|
100
100
|
|
|
101
|
-
The installed branch is intentionally an update branch even when the current release is already the newest version; the host command may report “already latest.” Codex marketplace upgrade is the current native refresh operation; the
|
|
101
|
+
The installed branch is intentionally an update branch even when the current release is already the newest version; the host command may report “already latest.” Codex marketplace upgrade is the current native refresh operation. After successful Codex plugin install/update and verification, `plugin-sync.js` reads the installed plugin's tagged JSON source. It refreshes compatibility hooks only for `source: "local"` with an absolute filesystem path; `git`, `git-subdir`, `npm`, `remote`, unknown or missing variants, and relative paths fail closed before the installer runs. It does not reinterpret a repository subdirectory relative to the sync process working directory. An ownership/configuration error also makes Codex sync report failure. The installer does not alter trust state or private `config.toml`. Deterministic tests cover this source-shape policy; live Codex marketplace source reporting/sync remains unverified. The separate compatibility fallback has one retained live dispatch artifact on Windows, as recorded in the matrix.
|
|
102
102
|
|
|
103
103
|
On Windows, the synchronizer resolves `codex`/`claude` the same way an interactive shell does: it walks `PATH` directories in order and tries `PATHEXT` extensions within each directory before moving on, so directory order — not which candidate happens to be a directly launchable `.exe` — decides the match. This matters because Node's non-shell `spawnSync` cannot execute a `.cmd`/`.bat` shim directly and, left to its own resolution, can silently prefer an unrelated, older standalone `.exe` elsewhere on `PATH` over a newer npm-installed shim with no error to signal it. Only the one case Windows genuinely requires an interpreter for — a resolved `.cmd`/`.bat` target — is run through `ComSpec`, using the already-resolved absolute path rather than re-resolving the bare command name.
|
|
104
104
|
|
|
105
105
|
### Explicit Codex user-hook compatibility fallback
|
|
106
106
|
|
|
107
|
-
Current Codex documentation supports
|
|
107
|
+
Current Codex documentation supports lifecycle hooks for manually installed Codex desktop plugins and requires users to review and trust non-managed hook definitions. The retained codex-cli 0.157.1 evidence records plugin_hooks as removed, so the user-hook compatibility installer remains necessary for that CLI path. Harness provides the compatibility path alongside the native plugin package:
|
|
108
108
|
|
|
109
109
|
```bash
|
|
110
110
|
harness codex-hooks install
|
|
@@ -112,11 +112,11 @@ harness codex-hooks status
|
|
|
112
112
|
harness codex-hooks uninstall
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
-
The
|
|
115
|
+
The installer copies the current packaged runtime into `$CODEX_HOME/harness-everything/compat-hooks/` and merges absolute-path entries into `$CODEX_HOME/hooks.json`. harness codex-hooks status reports `inactive` when the compatibility hooks are absent, or `configured-unverified` when their files are healthy; it cannot read Codex trust state. A successful Codex run of plugin-sync.js installs or refreshes this copy automatically. The installer does **not** modify `config.toml`, trust hashes, approval policy, or hook enablement. Codex must still show/review/trust the resulting non-managed hooks through its normal `/hooks` flow.
|
|
116
116
|
|
|
117
117
|
Ownership is deliberately strict: update/uninstall removes only exact entries recorded in the Harness compatibility manifest. If an installed Harness entry was edited after installation, the operation fails closed rather than overwriting or deleting that user-modified entry. Unrelated pre-existing and later-added user hooks are preserved.
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
The compatibility path registers hook commands in Codex user configuration and covers the missing plugin-mount route where user hooks dispatch. It does not repair a Codex carrier that fails to dispatch `PreToolUse` at all; those host-level execution-path limitations remain a live compatibility boundary.
|
|
120
120
|
|
|
121
121
|
The public OpenAI Skills-only plugin is narrower again. Its ZIP contains the packaged `skills/` tree and referenced files, but not the local `.codex-plugin` lifecycle hooks. Public submission readiness is therefore a package/form contract, not evidence of public-directory approval or live hook execution.
|
|
122
122
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "harness-platform-compatibility-v1",
|
|
3
|
-
"last_reviewed": "2026-
|
|
3
|
+
"last_reviewed": "2026-10-13",
|
|
4
4
|
"status_vocabulary": {
|
|
5
5
|
"Supported": "An official or native mechanism exists and the Harness behavior is verified.",
|
|
6
6
|
"Mechanism verified": "Harness package, adapter, or installer checks pass, but a real host run is not confirmed.",
|
|
@@ -109,13 +109,13 @@
|
|
|
109
109
|
"standaloneSkills": {"status": "Supported", "paths": {"project": [".agents/skills"], "global": ["~/.agents/skills"]}},
|
|
110
110
|
"pluginSupport": {"status": "Mechanism verified", "paths": {"manifest": ["plugin.json", ".codex-plugin/plugin.json"]}},
|
|
111
111
|
"bundledSkills": {"status": "Mechanism verified", "notes": "The local package contains a root skills/ tree."},
|
|
112
|
-
"hooksLifecycle": {"status": "
|
|
112
|
+
"hooksLifecycle": {"status": "Partial", "notes": "Codex documents plugin-bundled hooks for manually installed desktop plugins, and the local package is mechanism-tested. The plugin intentionally does not declare unsupported PostToolUseFailure; Codex uses documented PostToolUse semantics instead. Harness also provides a user-config compatibility installer that copies the packaged runtime and merges exact-owned entries into $CODEX_HOME/hooks.json without changing trust/config.toml; successful plugin sync refreshes this copy. Live evidence verifies the UserPromptSubmit and Stop observation handlers through the compatibility fallback on one Windows Codex CLI 0.162.1 host; native plugin-bundled hook mounting and other lifecycle events remain unverified."},
|
|
113
113
|
"projectScope": {"status": "Mechanism verified", "paths": {"skills": [".agents/skills"], "instructions": ["AGENTS.md"]}},
|
|
114
114
|
"globalScope": {"status": "Mechanism verified", "paths": {"skills": ["~/.agents/skills"]}},
|
|
115
|
-
"installUninstallSymmetry": {"status": "Mechanism verified", "notes": "General Harness installer ownership is mechanism-tested. The optional codex-hooks compatibility fallback separately has exact-entry update/uninstall ownership tests and preserves unrelated user hooks
|
|
115
|
+
"installUninstallSymmetry": {"status": "Mechanism verified", "notes": "General Harness installer ownership is mechanism-tested. The optional codex-hooks compatibility fallback separately has exact-entry update/uninstall ownership tests and preserves unrelated user hooks. Live evidence confirms trusted dispatch for the two observation handlers on one Windows Codex CLI host; native plugin mounting and other scopes remain unverified."},
|
|
116
116
|
"assetsReferences": {"status": "Mechanism verified", "notes": "Packaged skill references are checked and remain inside the package."},
|
|
117
|
-
"updateSync": {"status": "Mechanism verified", "notes": "scripts/plugin-sync.js detects installed state and uses marketplace upgrade for an installed Codex plugin;
|
|
118
|
-
"liveHostVerification": {"status": "
|
|
117
|
+
"updateSync": {"status": "Mechanism verified", "notes": "scripts/plugin-sync.js detects installed state and uses marketplace upgrade for an installed Codex plugin; after successful install/update verification it refreshes the local codex-hooks compatibility runtime and hooks.json entries. Unsupported CLI options are dropped and retried, and a Codex build without codex plugin list/add is reported as an actionable capability-boundary skip. Live marketplace source reporting and plugin-sync refresh remain unverified; the separate compatibility fallback dispatch is live-verified as recorded in the evidence artifact."},
|
|
118
|
+
"liveHostVerification": {"status": "Partial", "evidence": "benchmarks/results/live-host/codex-2026-10-13/README.md", "notes": "The preserved artifact verifies two consecutive observation captures through the user-hook compatibility fallback on Codex CLI 0.162.1 for Windows, including current-turn labels. Native plugin mounting, marketplace source reporting, other lifecycle events, and other host/version combinations remain unverified."}
|
|
119
119
|
}
|
|
120
120
|
},
|
|
121
121
|
{
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Status: **accepted**, 2026-09-27. Owner decisions are recorded below.
|
|
4
4
|
Implementation: `hooks/scripts/observation-hook.js`,
|
|
5
|
-
`hooks/scripts/lib/observations.js`, `scripts/system-one-observations-export.js`
|
|
6
|
-
|
|
5
|
+
`hooks/scripts/lib/observations.js`, `scripts/system-one-observations-export.js`, `scripts/system-one-observations-import-codex-sessions.js` and
|
|
6
|
+
`scripts/system-one-observations-review.js`; tests in
|
|
7
7
|
`ci/mechanism-34-observations.test.js`. Issue
|
|
8
8
|
[#233](https://github.com/dyphn1/Harness-everything/issues/233).
|
|
9
9
|
|
|
@@ -43,7 +43,7 @@ A turn runs from `UserPromptSubmit` to `Stop`. Its record:
|
|
|
43
43
|
| `prompt` | `UserPromptSubmit` | stored in the private text store only (see Storage) |
|
|
44
44
|
| `previous` | the last assistant message before the prompt | truncated to 2 KB; text store only |
|
|
45
45
|
| `router` | kernel-router output | lexical tier, strategy, suggested skills; System One shadow scores when present |
|
|
46
|
-
| `behavior` | `PostToolUse`, `PostToolUseFailure` | counts and categories, never arguments: files written, distinct repositories written, files created, commands by class (git, gh, test, build, package, shell), skills loaded or read, subagents started. Failed attempts are counted separately (`toolFailures`, `failedWrites`, `failedSkillLoads`) and never count as a write or a load |
|
|
46
|
+
| `behavior` | `PostToolUse`, `PostToolUseFailure` | counts and categories, never arguments: files written, distinct repositories written, files created, commands by class (git, gh, test, build, package, shell), skills loaded or read, subagents started. Failed attempts are counted separately (`toolFailures`, `failedWrites`, `failedSkillLoads`) and never count as a write or a load. `null` means the capture source did not observe tool events; it is not a zero count. |
|
|
47
47
|
| `workflow` | `workflow-run.json` / `workflow-disposition` | selected and confirmed strategy |
|
|
48
48
|
| `selfReport` | label line in the final message | see below; `null` if missing or invalid |
|
|
49
49
|
| `derived` | exporter | labels computed from the fields above |
|
|
@@ -98,6 +98,12 @@ purposes:
|
|
|
98
98
|
- **Future features.** The counters can train or check later models
|
|
99
99
|
without re-reading transcripts.
|
|
100
100
|
|
|
101
|
+
Session backfill reads prompts and final labels, but does not reconstruct
|
|
102
|
+
tool events or file writes. Its `behavior` is therefore `null`. Exports count
|
|
103
|
+
observed and unobserved behavior separately and derive contradictions only
|
|
104
|
+
from observed behavior. The default review page includes unobserved records
|
|
105
|
+
and identifies the evidence gap.
|
|
106
|
+
|
|
101
107
|
**Owner review.** Missing labels, contradictions, and turns where the
|
|
102
108
|
router's tier and the self-reported tier differ go to a blind review page.
|
|
103
109
|
The owner decides validity, context dependence and tier; each decision
|
|
@@ -157,11 +163,16 @@ warning sign. The review page samples the turns where they differ.
|
|
|
157
163
|
it wrote no label; the same text as JSON reached the model and all five
|
|
158
164
|
test turns carried a label. The Codex manifest therefore runs the router
|
|
159
165
|
through `hooks/scripts/codex-user-prompt.js`, which wraps its output.
|
|
160
|
-
-
|
|
161
|
-
`
|
|
162
|
-
|
|
163
|
-
|
|
166
|
+
- The retained `codex-cli 0.157.1` evidence found plugin hooks removed
|
|
167
|
+
(`plugin_hooks`); the local CLI path therefore uses the user-hook
|
|
168
|
+
compatibility installer. Successful `plugin:hosts` runs now install or
|
|
169
|
+
refresh that copied runtime and its `hooks.json` entries. The installer
|
|
170
|
+
does not change trust state; Codex still requires hook review/trust in
|
|
171
|
+
`/hooks`. Status reports `inactive` when absent and
|
|
172
|
+
`configured-unverified` when the files are healthy, because this command
|
|
173
|
+
cannot read Codex trust state.
|
|
164
174
|
- The final message comes from the rollout's `task_complete` event.
|
|
175
|
+
- Fresh live-host verification on Windows (Codex CLI 0.162.1, 2026-10-13) exercised the user-hook compatibility fallback: only the `UserPromptSubmit` and `Stop` observation hooks were individually reviewed/trusted. Two consecutive real turns produced `host: "codex"` records with distinct current `turn_id` values and their respective valid self-reports. The sanitized record excerpt and scope limits are preserved in [Codex live-host evidence](../benchmarks/results/live-host/codex-2026-10-13/README.md). This does not verify native plugin-bundled hooks, other lifecycle events, or tool behavior capture.
|
|
165
176
|
- The final message must belong to the current turn. A transcript scan
|
|
166
177
|
stops at the start of the current turn (Codex `task_started` or a user
|
|
167
178
|
message; a Claude user prompt) and a Codex `task_complete` must carry the
|
|
@@ -174,18 +185,34 @@ warning sign. The review page samples the turns where they differ.
|
|
|
174
185
|
- A missing field records `null` with a reason code. Collection must never
|
|
175
186
|
block, slow down, or fail a turn; each hook has a short time budget.
|
|
176
187
|
|
|
188
|
+
## Session backfill
|
|
189
|
+
|
|
190
|
+
To recover self-reported labels written before the observation hooks were active, run:
|
|
191
|
+
|
|
192
|
+
node scripts/system-one-observations-import-codex-sessions.js --since 2026-09-27
|
|
193
|
+
|
|
194
|
+
The importer scans local Codex rollout files and pairs each completed turn with its first user text item. It reads the final label from `task_complete.last_agent_message`; when that field is absent or not a string, the last assistant `response_item` can provide it unless that item is marked `commentary`. When a string completion has no label and the last assistant item is not marked `commentary` and has a valid label, their bodies are compared after `obs.stripLabels` removes label comments and trims surrounding whitespace. A match lets the assistant label provide the missing label; a difference reports `ambiguousFinalMessage`, skips the turn, and clears previous-message context rather than choosing one representation. An explicitly unlabeled completion never inherits a commentary or earlier assistant label, and an invalid completion label is not replaced by the assistant label.
|
|
195
|
+
|
|
196
|
+
The importer accepts only valid label lines, applies the same email/home-path redaction, and appends idempotently to the private observations store. Backfill and live hook records deduplicate by `(host, writer.sessionId, writer.turnId)`. For older live records without a turn ID, an exact turn-and-text match is skipped; a partial match is counted as ambiguous and skipped to avoid silently duplicating training data. Duplicate and ambiguous legacy live matches are checked atomically under the store lock. A bounded lock timeout is reported as a persistence failure, not as a duplicate, and gives the importer a nonzero exit status; migration lock timeouts are reported the same way. Existing backfill behavior placeholders are migrated to `null`. Reports contain counts and generic error descriptions without printing session text. The cutoff defaults to the date the label contract was accepted. The importer never uploads or writes session content into the repository.
|
|
197
|
+
|
|
177
198
|
## Export
|
|
178
199
|
|
|
179
200
|
`scripts/system-one-observations-export.js` writes to the private data
|
|
180
201
|
repository:
|
|
181
202
|
|
|
182
203
|
- `prompts-observed.jsonl`: `{id, family, source: "observation", split, text}`
|
|
183
|
-
- `labels-observed.jsonl`: `{id, validity, contextDependent, tier, tierSource, intents, workflow, skills}`
|
|
204
|
+
- `labels-observed.jsonl`: `{id, validity, contextDependent, tier, tierSource, intents, workflow, skills, behaviorEvidence, contradictions}`
|
|
184
205
|
|
|
185
206
|
`family` groups near-duplicate prompts. The split is by time: the newest 15%
|
|
186
207
|
is validation. The existing holdouts stay frozen and separate. Observed data
|
|
187
208
|
may later seed a new holdout, but only after owner review.
|
|
188
209
|
|
|
210
|
+
For a training candidate restricted to labels from the accepted label-line date, use an output directory outside the repository:
|
|
211
|
+
|
|
212
|
+
node scripts/system-one-observations-export.js --out <private-output-dir> --since 2026-09-27 --self-report-only
|
|
213
|
+
|
|
214
|
+
This keeps unlabeled observations out of the candidate and leaves existing model and holdout directories untouched.
|
|
215
|
+
|
|
189
216
|
## Phases
|
|
190
217
|
|
|
191
218
|
1. **Contract.** Define the label-line format in `AGENTS.md` rule 11 and in
|