dsh-harbor-evolution 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/index.js +50 -1
- package/lib/client.js +697 -116
- package/lib/dashboard.js +513 -59
- package/lib/evolution.js +66 -7
- package/lib/process.js +2 -1
- package/lib/service.js +99 -1
- package/lib/web.js +48 -0
- package/package.json +6 -1
- package/schemas/evaluation-result.schema.json +27 -0
- package/schemas/evaluator-observations.schema.json +51 -0
- package/schemas/ground-truth.schema.json +61 -0
- package/schemas/meta-evaluation-report.schema.json +23 -0
- package/skills/evolve-agent-with-harbor/SKILL.md +48 -4
- package/skills/evolve-agent-with-harbor/references/evaluator-upgrade.md +61 -0
- package/skills/evolve-agent-with-harbor/references/initialization.md +13 -0
package/lib/evolution.js
CHANGED
|
@@ -33,13 +33,20 @@ export function makeJobName(manifest, now = new Date()) {
|
|
|
33
33
|
return `${identity}-${suffix}`
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
async function cliJson(config, args, { allowedExitCodes = [0] } = {}) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
async function cliJson(config, args, { allowedExitCodes = [0], input } = {}) {
|
|
37
|
+
let result
|
|
38
|
+
try {
|
|
39
|
+
result = await runProcess(config.harborDshBin, args, {
|
|
40
|
+
cwd: config.projectRoot,
|
|
41
|
+
timeoutMs: config.timeoutMs,
|
|
42
|
+
allowedExitCodes,
|
|
43
|
+
input,
|
|
44
|
+
env: { ...process.env, ...(config.pythonPath ? { PYTHONPATH: config.pythonPath } : {}) },
|
|
45
|
+
})
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const detail = error?.result?.stderr?.trim().split('\n').at(-1)?.replace(/^[A-Za-z]+Error:\s*/, '')
|
|
48
|
+
throw new Error(detail || error.message)
|
|
49
|
+
}
|
|
43
50
|
try {
|
|
44
51
|
return JSON.parse(result.stdout)
|
|
45
52
|
} catch {
|
|
@@ -47,6 +54,58 @@ async function cliJson(config, args, { allowedExitCodes = [0] } = {}) {
|
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
56
|
|
|
57
|
+
export async function inspectEvaluator(config, args = {}) {
|
|
58
|
+
const stack = resolveWithin(config.projectRoot, args.stackPath ?? '.harbor/evaluation-stack.yml', 'stackPath')
|
|
59
|
+
return cliJson(config, [
|
|
60
|
+
'evaluator', 'inspect',
|
|
61
|
+
'--project-root', config.projectRoot,
|
|
62
|
+
'--stack', stack,
|
|
63
|
+
])
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function updateEvaluator(config, args) {
|
|
67
|
+
const stack = resolveWithin(config.projectRoot, args.stackPath ?? '.harbor/evaluation-stack.yml', 'stackPath')
|
|
68
|
+
if (typeof args.content !== 'string') throw new Error('content is required')
|
|
69
|
+
return cliJson(config, [
|
|
70
|
+
'evaluator', 'update',
|
|
71
|
+
'--project-root', config.projectRoot,
|
|
72
|
+
'--stack', stack,
|
|
73
|
+
'--file', String(args.filePath ?? ''),
|
|
74
|
+
'--expected-digest', String(args.expectedDigest ?? ''),
|
|
75
|
+
'--new-evaluator-version', String(args.newEvaluatorVersion ?? ''),
|
|
76
|
+
'--new-stack-version', String(args.newStackVersion ?? ''),
|
|
77
|
+
'--content-stdin',
|
|
78
|
+
], { input: args.content })
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function initializeGroundTruth(config, args) {
|
|
82
|
+
const output = resolveWithin(config.projectRoot, args.outputPath ?? '.harbor/ground-truth.json', 'outputPath')
|
|
83
|
+
return cliJson(config, [
|
|
84
|
+
'ground-truth', 'init',
|
|
85
|
+
'--project-root', config.projectRoot,
|
|
86
|
+
'--output', output,
|
|
87
|
+
'--id', String(args.groundTruthId ?? ''),
|
|
88
|
+
'--version', String(args.version ?? ''),
|
|
89
|
+
'--source-kind', String(args.sourceKind ?? ''),
|
|
90
|
+
'--source-description', String(args.sourceDescription ?? ''),
|
|
91
|
+
'--provenance', String(args.provenance ?? ''),
|
|
92
|
+
'--criteria', String(args.criteria ?? ''),
|
|
93
|
+
])
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function runMetaEvaluation(config, args) {
|
|
97
|
+
const groundTruth = resolveWithin(config.projectRoot, args.groundTruthPath ?? '.harbor/ground-truth.json', 'groundTruthPath')
|
|
98
|
+
const observations = resolveWithin(config.projectRoot, args.observationsPath, 'observationsPath')
|
|
99
|
+
const output = resolveWithin(config.projectRoot, args.outputPath ?? '.harbor/meta-evaluation-report.json', 'outputPath')
|
|
100
|
+
return cliJson(config, [
|
|
101
|
+
'meta-evaluate',
|
|
102
|
+
'--project-root', config.projectRoot,
|
|
103
|
+
'--ground-truth', groundTruth,
|
|
104
|
+
'--observations', observations,
|
|
105
|
+
'--output', output,
|
|
106
|
+
])
|
|
107
|
+
}
|
|
108
|
+
|
|
50
109
|
function strictInputs(config, args) {
|
|
51
110
|
const projectRoot = path.resolve(config.projectRoot)
|
|
52
111
|
const candidate = resolveWithin(projectRoot, args.candidatePath, 'candidatePath')
|
package/lib/process.js
CHANGED
|
@@ -6,7 +6,7 @@ export function runProcess(command, args, options = {}) {
|
|
|
6
6
|
cwd: options.cwd,
|
|
7
7
|
env: options.env ?? process.env,
|
|
8
8
|
shell: false,
|
|
9
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
9
|
+
stdio: [options.input === undefined ? 'ignore' : 'pipe', 'pipe', 'pipe'],
|
|
10
10
|
})
|
|
11
11
|
let stdout = ''
|
|
12
12
|
let stderr = ''
|
|
@@ -16,6 +16,7 @@ export function runProcess(command, args, options = {}) {
|
|
|
16
16
|
}, options.timeoutMs ?? 1_800_000)
|
|
17
17
|
child.stdout.on('data', chunk => { stdout += chunk })
|
|
18
18
|
child.stderr.on('data', chunk => { stderr += chunk })
|
|
19
|
+
if (options.input !== undefined) child.stdin.end(options.input)
|
|
19
20
|
child.on('error', error => {
|
|
20
21
|
clearTimeout(timeout)
|
|
21
22
|
reject(error)
|
package/lib/service.js
CHANGED
|
@@ -1,15 +1,51 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { access } from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
readComparison,
|
|
6
|
+
readDashboardSnapshot,
|
|
7
|
+
readDatasetPreview,
|
|
8
|
+
readEvaluatorGovernance,
|
|
9
|
+
readJobDetail,
|
|
10
|
+
readJobProgress,
|
|
11
|
+
readMetaEvaluation,
|
|
12
|
+
readTrialDetail,
|
|
13
|
+
readTrialsPage,
|
|
14
|
+
} from './dashboard.js'
|
|
2
15
|
import {
|
|
3
16
|
compareCandidates,
|
|
17
|
+
initializeGroundTruth,
|
|
4
18
|
initializeProject,
|
|
19
|
+
inspectEvaluator,
|
|
5
20
|
previewContext,
|
|
6
21
|
readEvaluation,
|
|
7
22
|
runDoctor,
|
|
8
23
|
runEvaluation,
|
|
24
|
+
runMetaEvaluation,
|
|
9
25
|
snapshot,
|
|
26
|
+
updateEvaluator,
|
|
10
27
|
validateDataset,
|
|
11
28
|
} from './evolution.js'
|
|
12
29
|
|
|
30
|
+
export async function resolveEvaluatorStackPath(config, governance, explicitPath) {
|
|
31
|
+
if (explicitPath) return explicitPath
|
|
32
|
+
const root = path.resolve(config.projectRoot)
|
|
33
|
+
const entry = governance.components?.evaluator?.entry
|
|
34
|
+
if (typeof entry !== 'string' || !entry) return undefined
|
|
35
|
+
let directory = path.dirname(path.resolve(root, entry))
|
|
36
|
+
if (directory !== root && !directory.startsWith(`${root}${path.sep}`)) return undefined
|
|
37
|
+
while (directory === root || directory.startsWith(`${root}${path.sep}`)) {
|
|
38
|
+
const candidate = path.join(directory, '.harbor', 'evaluation-stack.yml')
|
|
39
|
+
try {
|
|
40
|
+
await access(candidate)
|
|
41
|
+
return path.relative(root, candidate)
|
|
42
|
+
} catch {}
|
|
43
|
+
if (directory === root) break
|
|
44
|
+
directory = path.dirname(directory)
|
|
45
|
+
}
|
|
46
|
+
return undefined
|
|
47
|
+
}
|
|
48
|
+
|
|
13
49
|
/** One Host-side boundary shared by Agent tools and the Web dashboard. */
|
|
14
50
|
export class EvolutionService {
|
|
15
51
|
constructor(config, metadata = {}) {
|
|
@@ -30,6 +66,15 @@ export class EvolutionService {
|
|
|
30
66
|
}
|
|
31
67
|
|
|
32
68
|
result(args) {
|
|
69
|
+
const job = String(args.jobPath ?? '').split(/[\\/]/).filter(Boolean).at(-1)
|
|
70
|
+
if (args.view === 'job') return readJobDetail(this.config, { job })
|
|
71
|
+
if (args.view === 'progress') return readJobProgress(this.config, { job, since: args.since })
|
|
72
|
+
if (args.view === 'trial') {
|
|
73
|
+
if (!args.trialId) throw new Error('trialId is required when view=trial')
|
|
74
|
+
return readTrialDetail(this.config, { job, trial: args.trialId })
|
|
75
|
+
}
|
|
76
|
+
if (args.view === 'dataset') return readDatasetPreview(this.config, { job })
|
|
77
|
+
if (args.view === 'governance') return readEvaluatorGovernance(this.config, { job, compareJob: args.compareJob })
|
|
33
78
|
return readEvaluation(this.config, args)
|
|
34
79
|
}
|
|
35
80
|
|
|
@@ -64,4 +109,57 @@ export class EvolutionService {
|
|
|
64
109
|
trial(args) {
|
|
65
110
|
return readTrialDetail(this.config, args)
|
|
66
111
|
}
|
|
112
|
+
|
|
113
|
+
dataset(args) {
|
|
114
|
+
return readDatasetPreview(this.config, args)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
progress(args) {
|
|
118
|
+
return readJobProgress(this.config, args)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
comparison(args) {
|
|
122
|
+
return readComparison(this.config, args)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async governance(args) {
|
|
126
|
+
const governance = await readEvaluatorGovernance(this.config, args)
|
|
127
|
+
try {
|
|
128
|
+
const stackPath = await resolveEvaluatorStackPath(this.config, governance, args.stackPath)
|
|
129
|
+
governance.evaluatorInterface = await inspectEvaluator(this.config, { ...args, stackPath })
|
|
130
|
+
governance.editingPolicy.browserWriteEnabled = true
|
|
131
|
+
governance.editingPolicy.stackPath = governance.evaluatorInterface.stack?.path
|
|
132
|
+
governance.editingPolicy.saveBehavior = 'Update one descriptor-authorized file with optimistic concurrency and create new Evaluator and Stack identities.'
|
|
133
|
+
} catch (error) {
|
|
134
|
+
governance.evaluatorInterface = { error: error instanceof Error ? error.message : String(error) }
|
|
135
|
+
}
|
|
136
|
+
return governance
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
evaluator(args) {
|
|
140
|
+
return updateEvaluator(this.config, args)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
evaluatorInspect(args) {
|
|
144
|
+
return inspectEvaluator(this.config, args)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
groundTruthInitialize(args) {
|
|
148
|
+
return initializeGroundTruth(this.config, args)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
evaluatorMetaEvaluate(args) {
|
|
152
|
+
return runMetaEvaluation(this.config, args)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async meta(args) {
|
|
156
|
+
const governance = await readEvaluatorGovernance(this.config, args)
|
|
157
|
+
const stackPath = await resolveEvaluatorStackPath(this.config, governance, args.stackPath)
|
|
158
|
+
if (!stackPath) return readMetaEvaluation(this.config)
|
|
159
|
+
const stackDirectory = path.dirname(path.resolve(this.config.projectRoot, stackPath))
|
|
160
|
+
const evaluationRoot = path.dirname(stackDirectory)
|
|
161
|
+
return readMetaEvaluation(this.config, {
|
|
162
|
+
evaluationRoot: path.relative(this.config.projectRoot, evaluationRoot),
|
|
163
|
+
})
|
|
164
|
+
}
|
|
67
165
|
}
|
package/lib/web.js
CHANGED
|
@@ -2,6 +2,13 @@ export const DASHBOARD_ROUTE = '/_dsh/harbor-evolution/dashboard'
|
|
|
2
2
|
export const JOB_ROUTE = '/_dsh/harbor-evolution/job'
|
|
3
3
|
export const TRIALS_ROUTE = '/_dsh/harbor-evolution/trials'
|
|
4
4
|
export const TRIAL_ROUTE = '/_dsh/harbor-evolution/trial'
|
|
5
|
+
export const DATASET_ROUTE = '/_dsh/harbor-evolution/dataset'
|
|
6
|
+
export const PROGRESS_ROUTE = '/_dsh/harbor-evolution/progress'
|
|
7
|
+
export const COMPARE_ROUTE = '/_dsh/harbor-evolution/compare'
|
|
8
|
+
export const GOVERNANCE_ROUTE = '/_dsh/harbor-evolution/governance'
|
|
9
|
+
export const EVALUATOR_ROUTE = '/_dsh/harbor-evolution/evaluator'
|
|
10
|
+
export const META_ROUTE = '/_dsh/harbor-evolution/meta'
|
|
11
|
+
const MAX_MUTATION_BYTES = 256 * 1024
|
|
5
12
|
|
|
6
13
|
function sendJson(response, status, body) {
|
|
7
14
|
response.writeHead(status, {
|
|
@@ -60,6 +67,41 @@ export function createDashboardHandler(service) {
|
|
|
60
67
|
return createApiHandler(() => service.dashboard(), 'dashboard-unavailable')
|
|
61
68
|
}
|
|
62
69
|
|
|
70
|
+
export function createMutationHandler(update, code = 'update-failed') {
|
|
71
|
+
return async (request, response) => {
|
|
72
|
+
if (request.method !== 'POST') {
|
|
73
|
+
response.writeHead(405, { allow: 'POST' })
|
|
74
|
+
response.end()
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
if (!isSameOriginRequest(request)) {
|
|
78
|
+
sendJson(response, 403, { ok: false, error: { code: 'forbidden', message: 'same-origin request required' } })
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
if (!String(request.headers['content-type'] ?? '').toLowerCase().startsWith('application/json')) {
|
|
82
|
+
sendJson(response, 415, { ok: false, error: { code: 'unsupported-media-type', message: 'application/json required' } })
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const chunks = []
|
|
87
|
+
let size = 0
|
|
88
|
+
for await (const chunk of request) {
|
|
89
|
+
size += chunk.length
|
|
90
|
+
if (size > MAX_MUTATION_BYTES) {
|
|
91
|
+
sendJson(response, 413, { ok: false, error: { code: 'payload-too-large', message: 'request body is too large' } })
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))
|
|
95
|
+
}
|
|
96
|
+
const body = JSON.parse(Buffer.concat(chunks).toString('utf8'))
|
|
97
|
+
const value = await update(body)
|
|
98
|
+
sendJson(response, 200, { ok: true, value })
|
|
99
|
+
} catch (error) {
|
|
100
|
+
sendJson(response, 400, { ok: false, error: { code, message: safeError(error) } })
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
63
105
|
export function installDashboardWeb(ctx, service) {
|
|
64
106
|
if (typeof ctx.inject !== 'function') return
|
|
65
107
|
ctx.inject(['webServer'], (webCtx) => {
|
|
@@ -68,6 +110,12 @@ export function installDashboardWeb(ctx, service) {
|
|
|
68
110
|
[JOB_ROUTE, createApiHandler(args => service.job(args), 'job-unavailable')],
|
|
69
111
|
[TRIALS_ROUTE, createApiHandler(args => service.trials(args), 'trials-unavailable')],
|
|
70
112
|
[TRIAL_ROUTE, createApiHandler(args => service.trial(args), 'trial-unavailable')],
|
|
113
|
+
[DATASET_ROUTE, createApiHandler(args => service.dataset(args), 'dataset-unavailable')],
|
|
114
|
+
[PROGRESS_ROUTE, createApiHandler(args => service.progress(args), 'progress-unavailable')],
|
|
115
|
+
[COMPARE_ROUTE, createApiHandler(args => service.comparison(args), 'comparison-unavailable')],
|
|
116
|
+
[GOVERNANCE_ROUTE, createApiHandler(args => service.governance(args), 'governance-unavailable')],
|
|
117
|
+
[EVALUATOR_ROUTE, createMutationHandler(args => service.evaluator(args), 'evaluator-update-failed')],
|
|
118
|
+
[META_ROUTE, createApiHandler(args => service.meta(args), 'meta-evaluation-unavailable')],
|
|
71
119
|
]
|
|
72
120
|
for (const [route, handler] of routes) {
|
|
73
121
|
webCtx.effect(() => webCtx.webServer.register({ kind: 'exact', path: route, handler }), `harbor-evolution: ${route}`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-harbor-evolution",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "DeepSeek Harness plugin and bundled Skill for safely evolving Cordis Candidates with Harbor.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
".": "./index.js",
|
|
9
9
|
"./client": "./lib/client.js",
|
|
10
10
|
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
11
|
+
"./schemas/evaluation-result.schema.json": "./schemas/evaluation-result.schema.json",
|
|
12
|
+
"./schemas/ground-truth.schema.json": "./schemas/ground-truth.schema.json",
|
|
13
|
+
"./schemas/evaluator-observations.schema.json": "./schemas/evaluator-observations.schema.json",
|
|
14
|
+
"./schemas/meta-evaluation-report.schema.json": "./schemas/meta-evaluation-report.schema.json",
|
|
11
15
|
"./package.json": "./package.json"
|
|
12
16
|
},
|
|
13
17
|
"bin": {
|
|
@@ -18,6 +22,7 @@
|
|
|
18
22
|
"lib/",
|
|
19
23
|
"bin/",
|
|
20
24
|
"skills/",
|
|
25
|
+
"schemas/",
|
|
21
26
|
"cordis.patch.yml",
|
|
22
27
|
"README.md",
|
|
23
28
|
"LICENSE"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Harbor DSH Evaluator Result v1",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"required": ["schema_version", "protocol", "criteria"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schema_version": { "const": 1 },
|
|
9
|
+
"protocol": { "const": "evaluation-result/v1" },
|
|
10
|
+
"criteria": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"minItems": 1,
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"additionalProperties": false,
|
|
16
|
+
"required": ["id", "score", "reason", "recommendation"],
|
|
17
|
+
"properties": {
|
|
18
|
+
"id": { "type": "string", "minLength": 1 },
|
|
19
|
+
"score": { "enum": [0, 0.5, 1] },
|
|
20
|
+
"reason": { "type": "string", "minLength": 1 },
|
|
21
|
+
"recommendation": { "type": "string", "minLength": 1 },
|
|
22
|
+
"evidence_refs": { "type": "array", "items": { "type": "string" } }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Evaluator Observations v1",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["schema_version", "protocol", "evaluator", "repeat_policy", "observations"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"schema_version": { "const": 1 },
|
|
8
|
+
"protocol": { "const": "evaluator-observations/v1" },
|
|
9
|
+
"evaluator": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"required": ["id", "version"],
|
|
12
|
+
"properties": {
|
|
13
|
+
"id": { "type": "string", "minLength": 1 },
|
|
14
|
+
"version": { "type": "string", "minLength": 1 },
|
|
15
|
+
"digest": { "type": "string" }
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"repeat_policy": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"required": ["repeats"],
|
|
21
|
+
"properties": {
|
|
22
|
+
"repeats": { "type": "integer", "minimum": 1 },
|
|
23
|
+
"seed_policy": { "type": "string" }
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"observations": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"minItems": 1,
|
|
29
|
+
"items": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"required": ["case_id", "repeat", "criteria"],
|
|
32
|
+
"properties": {
|
|
33
|
+
"case_id": { "type": "string", "minLength": 1 },
|
|
34
|
+
"repeat": { "type": "integer", "minimum": 1 },
|
|
35
|
+
"criteria": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"minItems": 1,
|
|
38
|
+
"items": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"required": ["id", "score"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"id": { "type": "string", "minLength": 1 },
|
|
43
|
+
"score": { "enum": [0, 0.5, 1] }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Evaluator Ground Truth v1",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"required": ["schema_version", "protocol", "ground_truth_id", "version", "source", "criteria", "cases"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schema_version": { "const": 1 },
|
|
9
|
+
"protocol": { "const": "ground-truth/v1" },
|
|
10
|
+
"ground_truth_id": { "type": "string", "minLength": 1 },
|
|
11
|
+
"version": { "type": "string", "minLength": 1 },
|
|
12
|
+
"source": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"required": ["kind", "description", "provenance", "independent_of_candidate"],
|
|
15
|
+
"properties": {
|
|
16
|
+
"kind": { "enum": ["human", "programmatic", "consensus", "model", "external"] },
|
|
17
|
+
"description": { "type": "string", "minLength": 1 },
|
|
18
|
+
"provenance": { "type": "string", "minLength": 1 },
|
|
19
|
+
"independent_of_candidate": { "const": true }
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"criteria": {
|
|
23
|
+
"type": "array",
|
|
24
|
+
"minItems": 1,
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"required": ["id", "label"],
|
|
28
|
+
"properties": {
|
|
29
|
+
"id": { "type": "string", "minLength": 1 },
|
|
30
|
+
"label": { "type": "string", "minLength": 1 }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"cases": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"items": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"required": ["id", "artifact_ref", "criteria"],
|
|
39
|
+
"properties": {
|
|
40
|
+
"id": { "type": "string", "minLength": 1 },
|
|
41
|
+
"artifact_ref": { "type": "string", "minLength": 1 },
|
|
42
|
+
"badcase": { "type": "boolean" },
|
|
43
|
+
"criteria": {
|
|
44
|
+
"type": "array",
|
|
45
|
+
"minItems": 1,
|
|
46
|
+
"items": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"required": ["id", "score", "weight", "reason"],
|
|
49
|
+
"properties": {
|
|
50
|
+
"id": { "type": "string", "minLength": 1 },
|
|
51
|
+
"score": { "enum": [0, 0.5, 1] },
|
|
52
|
+
"weight": { "type": "number", "exclusiveMinimum": 0 },
|
|
53
|
+
"reason": { "type": "string", "minLength": 1 }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Evaluator Meta-Evaluation Report v1",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["schema_version", "protocol", "ground_truth", "evaluator", "coverage", "metrics", "disagreements"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"schema_version": { "const": 1 },
|
|
8
|
+
"protocol": { "const": "meta-evaluation-report/v1" },
|
|
9
|
+
"ground_truth": { "type": "object" },
|
|
10
|
+
"evaluator": { "type": "object" },
|
|
11
|
+
"coverage": { "type": "object" },
|
|
12
|
+
"metrics": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"required": ["esf", "sce", "rcr"],
|
|
15
|
+
"properties": {
|
|
16
|
+
"esf": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
17
|
+
"sce": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
18
|
+
"rcr": { "type": ["number", "null"], "minimum": 0, "maximum": 1 }
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"disagreements": { "type": "array" }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: evolve-agent-with-harbor
|
|
3
|
-
description: Architect, initialize,
|
|
3
|
+
description: Architect, initialize, run, diagnose, compare, and safely improve a DeepSeek Harness business Agent with Harbor Trial Lifecycle, Score Validity, Evidence Provenance, Evaluation Stack, Context v2, Architecture Doctor, and explicit Promotion Gate. Use for Harbor setup, Agent self-evolution, vertical-search evaluation loops, running Job inspection, failed Trial diagnosis, Candidate optimization, evaluator governance or meta-evaluation, and promotion decisions.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Evolve Agent With Harbor
|
|
@@ -21,7 +21,8 @@ Treat Harbor as the experiment boundary. Deployment, CI/CD, and Champion replace
|
|
|
21
21
|
- **Diagnostic**: investigate failures without making a promotion claim.
|
|
22
22
|
- **Promotion**: run a `promotion-eligible` Job and apply the deterministic Gate.
|
|
23
23
|
- **Evolve**: baseline → diagnose → one controlled change → regression Job → Gate.
|
|
24
|
-
- **Meta-evaluate**: improve an Evaluator/Judge against independently maintained
|
|
24
|
+
- **Meta-evaluate**: improve an Evaluator/Judge against independently maintained, provenance-bearing GT.
|
|
25
|
+
- **Govern**: inspect Evaluator/Rubric/Judge source and identities; preview whether a change requires a fresh baseline.
|
|
25
26
|
|
|
26
27
|
Do not turn an inspection or diagnostic request into Agent mutation or deployment.
|
|
27
28
|
|
|
@@ -49,6 +50,8 @@ Require these before every Job:
|
|
|
49
50
|
- `.harbor/evaluation-stack.yml` with all eight roles, Judge identity, and Evaluation Contract.
|
|
50
51
|
- Evaluation Context v2 preview.
|
|
51
52
|
|
|
53
|
+
Require `input_integrity`, `agent_completed`, `integration_valid`, `renderer_valid`, `judge_completed`, and `artifact_schema_valid` in the Trial validity contract. Specify which failures are hard requirements. Never infer that a numeric raw verifier reward is a valid Candidate quality score.
|
|
54
|
+
|
|
52
55
|
Before a formal Job, call in order:
|
|
53
56
|
|
|
54
57
|
1. `harbor_candidate_snapshot`
|
|
@@ -98,7 +101,15 @@ Never cherry-pick stochastic runs. Apply the accepted repeat/seed policy symmetr
|
|
|
98
101
|
|
|
99
102
|
### Diagnose before changing
|
|
100
103
|
|
|
101
|
-
Use `harbor_eval_result`
|
|
104
|
+
Use `harbor_eval_result` to reopen evidence without guessing local artifact paths: default `view=summary`, `view=job` for capabilities and stage artifacts, `view=dataset` for Agent-visible instructions, `view=progress` while running, `view=trial` with a returned `trialId` for the generated output and sanitized evidence, and `view=governance` for Evaluator/Rubric/Judge source and upgrade impact. Inspect in this order:
|
|
105
|
+
|
|
106
|
+
1. Confirm every Dataset item reached a terminal Trial state. Running, queued, cancelled, or missing Trials are not quality evidence.
|
|
107
|
+
2. Check `score.valid` and every validity requirement. Display an invalid score as `—`, never `0`.
|
|
108
|
+
3. Inspect evidence provenance. Keep `Real Renderer`, `ACP Agent Output Fallback`, raw transport evidence, Judge explanation, and deterministic diagnosis distinct.
|
|
109
|
+
4. Inspect findings, recommendations, user-visible output, criteria, and timing.
|
|
110
|
+
5. Classify the owning layer before proposing a mutation.
|
|
111
|
+
|
|
112
|
+
Treat `raw_rewards` as audit-only when `score.valid=false`. Aggregate and compare only valid quality scores. Inspect Trial assessments and classify each failure as:
|
|
102
113
|
|
|
103
114
|
- Candidate capability or policy.
|
|
104
115
|
- Tool-call, invalid search, citation, or output-contract failure.
|
|
@@ -108,6 +119,15 @@ Use `harbor_eval_result` only to reopen a stable Job summary. Inspect Trial asse
|
|
|
108
119
|
|
|
109
120
|
Do not optimize the Candidate around broken evaluation infrastructure. Never leak holdout answers or GT into Candidate prompts, skills, tools, or memory.
|
|
110
121
|
|
|
122
|
+
Use the formal terminal states precisely:
|
|
123
|
+
|
|
124
|
+
- `candidate-quality-failed`: valid execution reached evaluation, but a Candidate-owned hard requirement failed.
|
|
125
|
+
- `infrastructure-error`: dependency, sandbox, permission, transport, timeout, or runtime failure; no Candidate quality score.
|
|
126
|
+
- `evaluation-error`: Renderer/Judge/Verifier did not complete; no Candidate quality score.
|
|
127
|
+
- `cancelled`: preserve the attempt and do not score it.
|
|
128
|
+
|
|
129
|
+
For retry or resume, retain the old attempt and create a new attempt. Never replace an assessment or event history in place.
|
|
130
|
+
|
|
111
131
|
### Propose one controlled change
|
|
112
132
|
|
|
113
133
|
Require every optimization hypothesis to include:
|
|
@@ -129,15 +149,37 @@ Call `harbor_context_preview`; establish a fresh baseline if needed. Run the Can
|
|
|
129
149
|
|
|
130
150
|
Never bypass `INFRASTRUCTURE_EXCEPTION_PRESENT`, `ARTIFACT_SCHEMA_INVALID`, Dataset/Stack/Rubric/Judge mismatch, or non-regression failures.
|
|
131
151
|
|
|
152
|
+
A `diagnostic` Job must never invoke Gate. Reading the Workbench, generating a Reporter summary, or producing a non-reward Optimization Report also must not promote, deploy, publish, or replace the Champion. Gate remains a separate, explicit comparison action.
|
|
153
|
+
|
|
154
|
+
## Govern evaluator changes
|
|
155
|
+
|
|
156
|
+
Read `references/evaluator-upgrade.md` whenever the user asks to improve, replace, align, calibrate, debug, or explain an Evaluator, Rubric, Judge, reward, or meta-evaluation loop.
|
|
157
|
+
|
|
158
|
+
Use the Workbench Governance view to read component identity, source, Rubric, Judge parameters, Contract, and Context impact. Before any Evaluator/Rubric/Judge edit:
|
|
159
|
+
|
|
160
|
+
1. Show the current source and proposed diff.
|
|
161
|
+
2. State which reward semantics change.
|
|
162
|
+
3. Create a new component and Stack version; never overwrite historical identity.
|
|
163
|
+
4. Establish a fresh baseline when a reward-affecting digest or Judge identity changes.
|
|
164
|
+
5. Run meta-evaluation against independently maintained GT when aligning the Evaluator itself.
|
|
165
|
+
|
|
166
|
+
Saving a new identity does not automatically launch an evaluation or Gate.
|
|
167
|
+
|
|
168
|
+
An Evaluator implementation must use `harbor-dsh-evaluator/v1`. It may declare `kind=script` or `kind=llm-as-judge`, but both kinds accept `evaluation-input/v1` and return `evaluation-result/v1`. Every Descriptor-declared Criterion must return its declared score plus a non-empty `reason` string and a non-empty `recommendation` string. Missing explanations or recommendations invalidate the evaluator result; Reporter must not invent them. Use `harbor_evaluator_inspect` before proposing a change. After the user approves, use `harbor_evaluator_update` only for an exact `editable_files` path and provide the current digest plus new Evaluator and Stack versions. The tool creates a new versioned bundle; it does not overwrite the old implementation, run meta-evaluation, establish a baseline, or invoke Gate.
|
|
169
|
+
|
|
132
170
|
## Handle evaluator meta-evaluation
|
|
133
171
|
|
|
134
172
|
Rotate roles when improving the Evaluator:
|
|
135
173
|
|
|
136
174
|
- Candidate is the Evaluator/Rubric/Judge version.
|
|
137
|
-
- Dataset contains independently maintained
|
|
175
|
+
- Dataset contains fixed artifacts plus independently maintained GT with explicit source kind and provenance.
|
|
138
176
|
- Metrics include RCR, bias, variance, calibration, latency, and cost as appropriate.
|
|
139
177
|
- The Candidate evaluator must not author its own GT or final promotion decision.
|
|
140
178
|
|
|
179
|
+
GT may be human, programmatic, consensus-based, produced by an independently pinned model, or imported from an external standard. Independence and provenance matter more than the author type. The Candidate evaluator must never see labels before producing its observation.
|
|
180
|
+
|
|
181
|
+
When GT is missing, clarify its id/version, source kind, owner, provenance, Criteria, case selection, and adjudication process. Then call `harbor_ground_truth_init`; it creates a non-overwriting draft and never invents cases or labels. After cases are populated, collect repeated `evaluator-observations/v1` and call `harbor_evaluator_meta_evaluate`. Report ESF, SCE, RCR, coverage, disagreement slices, latency, and cost as applicable.
|
|
182
|
+
|
|
141
183
|
Manage evaluator Candidates and meta-evaluation Jobs with the same Manifest, Context v2, Doctor, evidence, and Gate rules.
|
|
142
184
|
|
|
143
185
|
## Report each cycle
|
|
@@ -148,7 +190,9 @@ Return:
|
|
|
148
190
|
- Candidate, Dataset, Stack, Context, Judge, and Policy identities.
|
|
149
191
|
- Comparable baseline or fresh-baseline decision.
|
|
150
192
|
- Metric deltas, exception counts, Population groups, and artifact validation.
|
|
193
|
+
- Dataset coverage, terminal-state counts, valid/invalid score counts, and selected attempt policy.
|
|
151
194
|
- Representative Trial evidence and root-cause classes.
|
|
195
|
+
- Evidence provenance and any capability unavailable on a legacy Job.
|
|
152
196
|
- Controlled change hypothesis and mutation surface.
|
|
153
197
|
- Gate decision with exact reason codes.
|
|
154
198
|
- External CI/CD action still required.
|