dsh-harbor-evolution 0.5.0 → 0.6.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/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
- const result = await runProcess(config.harborDshBin, args, {
38
- cwd: config.projectRoot,
39
- timeoutMs: config.timeoutMs,
40
- allowedExitCodes,
41
- env: { ...process.env, ...(config.pythonPath ? { PYTHONPATH: config.pythonPath } : {}) },
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 { readDashboardSnapshot, readJobDetail, readTrialDetail, readTrialsPage } from './dashboard.js'
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/setup.js CHANGED
@@ -267,7 +267,11 @@ export async function setupIntegration(raw = {}, dependencies = {}) {
267
267
  try {
268
268
  await run('pnpm', [
269
269
  '--silent', 'dlx', `@deepseek-ai/dsh@${DSH_VERSION}`,
270
- 'plugin', '--profile', config.profile, 'add', '-w', '--save-exact', config.pluginSpec,
270
+ // Registry packages ship their built client and need no lifecycle
271
+ // scripts. Skipping scripts also prevents pnpm 11 from reclassifying
272
+ // unrelated DSH native dependencies as newly unapproved builds while
273
+ // adding this plugin to an existing profile.
274
+ 'plugin', '--profile', config.profile, 'add', '-w', '--save-exact', '--ignore-scripts', config.pluginSpec,
271
275
  ], { env: { ...env, DSH_HOME: config.dshHome } })
272
276
  } catch (error) {
273
277
  throw new Error(processFailure(error))
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.5.0",
3
+ "version": "0.6.1",
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, diagnose, evaluate, compare, and safely improve a DeepSeek Harness business Agent with Harbor Evaluation Stack, Dataset Manifest, Context v2, Architecture Doctor, and Promotion Gate. Use for Harbor setup, Agent self-evolution, vertical-search evaluation loops, evaluation architecture review, failed Job diagnosis, Candidate optimization, evaluator meta-evaluation, or promotion decisions.
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 human GT.
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` only to reopen a stable Job summary. Inspect Trial assessments and classify each failure as:
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 human GT.
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.