@haystackeditor/cli 0.15.29 → 0.15.31
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/dist/assets/telemetry/browser-runtime.js +148 -2
- package/dist/commands/design-verify.d.ts +2 -0
- package/dist/commands/design-verify.js +27 -1
- package/dist/commands/precompute-delivery-contract.d.ts +68 -0
- package/dist/commands/precompute-delivery-contract.js +1 -0
- package/dist/commands/precompute-delivery-worker.d.ts +1 -0
- package/dist/commands/precompute-delivery-worker.js +68 -0
- package/dist/commands/precompute-delivery.d.ts +20 -0
- package/dist/commands/precompute-delivery.js +319 -0
- package/dist/commands/verify-explore.d.ts +14 -0
- package/dist/commands/verify-explore.js +180 -0
- package/dist/commands/verify-history.d.ts +14 -0
- package/dist/commands/verify-history.js +152 -0
- package/dist/commands/verify-hosted.d.ts +12 -2
- package/dist/commands/verify-hosted.js +305 -50
- package/dist/commands/verify-precompute.d.ts +12 -1
- package/dist/commands/verify-precompute.js +135 -27
- package/dist/commands/verify.d.ts +6 -0
- package/dist/commands/verify.js +22 -0
- package/dist/index.js +71 -5
- package/dist/types/verify-history.d.ts +38 -0
- package/dist/types/verify-history.js +1 -0
- package/dist/utils/design-verifier-api.d.ts +8 -0
- package/dist/utils/design-verifier-api.js +23 -0
- package/dist/utils/design-verifier-history.d.ts +46 -0
- package/dist/utils/design-verifier-history.js +79 -0
- package/dist/utils/design-verifier-result.d.ts +17 -0
- package/dist/utils/design-verifier-result.js +80 -1
- package/dist/utils/git.js +5 -1
- package/dist/utils/telemetry.js +8 -2
- package/package.json +1 -1
- package/schemas/cloud-verifier.v1.json +108 -45
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const outcomes = ['healthy', 'degraded', 'invalid', 'failed', 'unknown'];
|
|
2
|
+
export function isDesignVerifierHistoryReport(value, expected) {
|
|
3
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
4
|
+
return false;
|
|
5
|
+
const report = value;
|
|
6
|
+
if (report.version !== 'dv-cli-history-v1' || report.owner !== expected.owner ||
|
|
7
|
+
report.repo !== expected.repo || report.limit !== expected.limit ||
|
|
8
|
+
!Number.isSafeInteger(report.retentionDays) || report.retentionDays <= 0 ||
|
|
9
|
+
!Array.isArray(report.runs) || report.runs.length > report.limit || !report.summary)
|
|
10
|
+
return false;
|
|
11
|
+
const seen = new Set();
|
|
12
|
+
const counts = { healthy: 0, degraded: 0, invalid: 0, failed: 0, unknown: 0 };
|
|
13
|
+
const blockers = new Map();
|
|
14
|
+
for (const run of report.runs) {
|
|
15
|
+
if (!run || !Number.isSafeInteger(run.pr) || run.pr < 1 || typeof run.runId !== 'string')
|
|
16
|
+
return false;
|
|
17
|
+
const prefix = `${expected.owner}--${expected.repo}--${run.pr}--`;
|
|
18
|
+
if (!run.runId.startsWith(prefix) ||
|
|
19
|
+
!/^[0-9a-f]{12}(?:--attempt-[1-9]\d*)?$/.test(run.runId.slice(prefix.length)) ||
|
|
20
|
+
seen.has(run.runId) || !outcomes.includes(run.outcome) ||
|
|
21
|
+
!['succeeded', 'failed', 'failed-stale'].includes(run.status) ||
|
|
22
|
+
(run.outcome === 'failed') !== (run.status !== 'succeeded') ||
|
|
23
|
+
typeof run.createdAt !== 'string' || !Number.isFinite(Date.parse(run.createdAt)) ||
|
|
24
|
+
(run.finishedAt !== null && (typeof run.finishedAt !== 'string' || !Number.isFinite(Date.parse(run.finishedAt)))) ||
|
|
25
|
+
(run.baseSha !== null && typeof run.baseSha !== 'string') ||
|
|
26
|
+
(run.headSha !== null && typeof run.headSha !== 'string') ||
|
|
27
|
+
(run.admitted !== null && (!Number.isSafeInteger(run.admitted) || run.admitted < 0)) ||
|
|
28
|
+
!Array.isArray(run.losses) || run.losses.some(loss => !loss || typeof loss.code !== 'string' || !loss.code || typeof loss.owner !== 'string' ||
|
|
29
|
+
!loss.owner || !['degraded', 'invalid'].includes(loss.impact) ||
|
|
30
|
+
!Number.isSafeInteger(loss.count) || loss.count < 1))
|
|
31
|
+
return false;
|
|
32
|
+
if (['healthy', 'degraded'].includes(run.outcome) && (run.admitted === null || run.admitted === 0))
|
|
33
|
+
return false;
|
|
34
|
+
seen.add(run.runId);
|
|
35
|
+
counts[run.outcome]++;
|
|
36
|
+
for (const code of new Set(run.losses.map(loss => loss.code))) {
|
|
37
|
+
blockers.set(code, (blockers.get(code) || 0) + 1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const summary = report.summary;
|
|
41
|
+
const usableRate = report.runs.length ? (counts.healthy + counts.degraded) / report.runs.length : null;
|
|
42
|
+
if (summary.total !== report.runs.length || summary.usableRate !== usableRate ||
|
|
43
|
+
outcomes.some(outcome => summary[outcome] !== counts[outcome]) ||
|
|
44
|
+
!Array.isArray(summary.topBlockers) || summary.topBlockers.length !== blockers.size)
|
|
45
|
+
return false;
|
|
46
|
+
const seenBlockers = new Set();
|
|
47
|
+
return summary.topBlockers.every(blocker => {
|
|
48
|
+
if (!blocker || seenBlockers.has(blocker.code) || blockers.get(blocker.code) !== blocker.affectedRuns)
|
|
49
|
+
return false;
|
|
50
|
+
seenBlockers.add(blocker.code);
|
|
51
|
+
return true;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function terminalSafe(value) {
|
|
55
|
+
return Array.from(value).filter(character => {
|
|
56
|
+
const point = character.codePointAt(0);
|
|
57
|
+
return point >= 32 && !(point >= 127 && point <= 159);
|
|
58
|
+
}).join('');
|
|
59
|
+
}
|
|
60
|
+
export function formatDesignVerifierHistory(report) {
|
|
61
|
+
const { summary } = report;
|
|
62
|
+
const lines = [`Execution history for ${terminalSafe(report.owner)}/${terminalSafe(report.repo)}`,
|
|
63
|
+
`Latest ${summary.total} recorded terminal attempts within ${report.retentionDays} days (limit ${report.limit}).`];
|
|
64
|
+
if (summary.total === 0) {
|
|
65
|
+
lines.push('No recorded attempts in this window. History starts when recording is deployed.');
|
|
66
|
+
return lines.join('\n');
|
|
67
|
+
}
|
|
68
|
+
lines.push(`Usable evidence: ${Math.round(summary.usableRate * 100)}% of attempts (${summary.healthy + summary.degraded}/${summary.total}).`, `Fully healthy: ${summary.healthy}; partially degraded: ${summary.degraded}; invalid: ${summary.invalid}; failed: ${summary.failed}; unknown: ${summary.unknown}.`, 'Usable includes partial evidence from degraded attempts. This measures execution reliability, not bug-detection accuracy.', '');
|
|
69
|
+
for (const run of report.runs) {
|
|
70
|
+
lines.push(`${run.finishedAt || run.createdAt} PR #${run.pr} ${run.outcome} ${run.admitted ?? '?'} admitted cases`, ` ${run.runId}`);
|
|
71
|
+
}
|
|
72
|
+
if (summary.topBlockers.length) {
|
|
73
|
+
lines.push('', 'Most frequent blockers (attempts affected):');
|
|
74
|
+
for (const blocker of summary.topBlockers.slice(0, 5)) {
|
|
75
|
+
lines.push(` ${terminalSafe(blocker.code)}: ${blocker.affectedRuns}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return lines.join('\n');
|
|
79
|
+
}
|
|
@@ -30,6 +30,22 @@ export interface DesignVerifierAgentCase {
|
|
|
30
30
|
errors: string[];
|
|
31
31
|
} | null;
|
|
32
32
|
}
|
|
33
|
+
export interface DesignVerifierRunHealthLoss {
|
|
34
|
+
code: string;
|
|
35
|
+
owner: 'harness' | 'case';
|
|
36
|
+
impact: 'degraded' | 'invalid';
|
|
37
|
+
count: number;
|
|
38
|
+
caseIds: string[];
|
|
39
|
+
obligationIds: string[];
|
|
40
|
+
slotIds: string[];
|
|
41
|
+
sides: string[];
|
|
42
|
+
}
|
|
43
|
+
export interface DesignVerifierRunHealth {
|
|
44
|
+
version: 'dv-run-health-v1';
|
|
45
|
+
status: 'healthy' | 'degraded' | 'invalid';
|
|
46
|
+
admitted: number;
|
|
47
|
+
losses: DesignVerifierRunHealthLoss[];
|
|
48
|
+
}
|
|
33
49
|
export interface DesignVerifierAgentReport {
|
|
34
50
|
ok: boolean;
|
|
35
51
|
status: 'complete' | 'failed';
|
|
@@ -47,6 +63,7 @@ export interface DesignVerifierAgentReport {
|
|
|
47
63
|
timings: DesignVerifierTimingSummary | null;
|
|
48
64
|
changedFiles: string[];
|
|
49
65
|
cases: DesignVerifierAgentCase[];
|
|
66
|
+
runHealth: DesignVerifierRunHealth | null;
|
|
50
67
|
inspectCommand: string;
|
|
51
68
|
replay: Record<string, unknown> | null;
|
|
52
69
|
error: string | null;
|
|
@@ -30,6 +30,54 @@ function universeSummaries(value, caseId) {
|
|
|
30
30
|
['control-before', 'before', 'after'].includes(universe.role) &&
|
|
31
31
|
universe.lease && typeof universe.lease.available === 'boolean');
|
|
32
32
|
}
|
|
33
|
+
function runHealthOf(value) {
|
|
34
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
35
|
+
return null;
|
|
36
|
+
const health = value;
|
|
37
|
+
if (health.version !== 'dv-run-health-v1' ||
|
|
38
|
+
!['healthy', 'degraded', 'invalid'].includes(health.status) ||
|
|
39
|
+
!Number.isInteger(health.admitted) || health.admitted < 0 ||
|
|
40
|
+
!Array.isArray(health.losses))
|
|
41
|
+
return null;
|
|
42
|
+
const losses = health.losses.filter((loss) => loss && typeof loss === 'object' &&
|
|
43
|
+
typeof loss.code === 'string' && loss.code.length > 0 &&
|
|
44
|
+
['harness', 'case'].includes(loss.owner) &&
|
|
45
|
+
['degraded', 'invalid'].includes(loss.impact) &&
|
|
46
|
+
Number.isInteger(loss.count) && loss.count > 0 &&
|
|
47
|
+
[loss.caseIds, loss.obligationIds, loss.slotIds, loss.sides]
|
|
48
|
+
.every(items => Array.isArray(items) && items.every(item => typeof item === 'string')));
|
|
49
|
+
if (losses.length !== health.losses.length)
|
|
50
|
+
return null;
|
|
51
|
+
const invalidHarnessLoss = losses.some(loss => loss.owner === 'harness' && loss.impact === 'invalid');
|
|
52
|
+
const expectedStatus = health.admitted === 0 || invalidHarnessLoss
|
|
53
|
+
? 'invalid'
|
|
54
|
+
: losses.length > 0 ? 'degraded' : 'healthy';
|
|
55
|
+
if (health.status !== expectedStatus)
|
|
56
|
+
return null;
|
|
57
|
+
return {
|
|
58
|
+
version: health.version,
|
|
59
|
+
status: health.status,
|
|
60
|
+
admitted: health.admitted,
|
|
61
|
+
losses,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function missingRunHealth() {
|
|
65
|
+
return {
|
|
66
|
+
version: 'dv-run-health-v1',
|
|
67
|
+
status: 'invalid',
|
|
68
|
+
admitted: 0,
|
|
69
|
+
losses: [{
|
|
70
|
+
code: 'run-health-missing',
|
|
71
|
+
owner: 'harness',
|
|
72
|
+
impact: 'invalid',
|
|
73
|
+
count: 1,
|
|
74
|
+
caseIds: [],
|
|
75
|
+
obligationIds: [],
|
|
76
|
+
slotIds: [],
|
|
77
|
+
sides: [],
|
|
78
|
+
}],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
33
81
|
export function buildDesignVerifierAgentReport(run) {
|
|
34
82
|
const repository = `${run.owner}/${run.repo}`;
|
|
35
83
|
if (run.status !== 'succeeded' || !run.result) {
|
|
@@ -47,6 +95,7 @@ export function buildDesignVerifierAgentReport(run) {
|
|
|
47
95
|
timings: null,
|
|
48
96
|
changedFiles: [],
|
|
49
97
|
cases: [],
|
|
98
|
+
runHealth: null,
|
|
50
99
|
inspectCommand: `haystack design-verify ${repository}#${run.pr} --run ${run.runId}`,
|
|
51
100
|
replay: null,
|
|
52
101
|
error: run.error,
|
|
@@ -55,6 +104,7 @@ export function buildDesignVerifierAgentReport(run) {
|
|
|
55
104
|
}
|
|
56
105
|
const verdict = run.result.verdict || {};
|
|
57
106
|
const summary = verdict.summary || {};
|
|
107
|
+
const runHealth = runHealthOf(verdict.runHealth) || missingRunHealth();
|
|
58
108
|
const findings = Array.isArray(verdict.findings) ? verdict.findings : [];
|
|
59
109
|
const confirmations = Array.isArray(verdict.confirmations) ? verdict.confirmations : [];
|
|
60
110
|
const findingsByCase = new Map();
|
|
@@ -133,7 +183,19 @@ export function buildDesignVerifierAgentReport(run) {
|
|
|
133
183
|
let headline;
|
|
134
184
|
let explanation;
|
|
135
185
|
let exitCode;
|
|
136
|
-
if (
|
|
186
|
+
if (runHealth.status === 'invalid') {
|
|
187
|
+
agentVerdict = 'inconclusive';
|
|
188
|
+
headline = 'VERIFICATION INCONCLUSIVE';
|
|
189
|
+
explanation = 'No admissible evidence — this is not a pass.';
|
|
190
|
+
exitCode = 2;
|
|
191
|
+
}
|
|
192
|
+
else if (runHealth.status === 'degraded') {
|
|
193
|
+
agentVerdict = 'inconclusive';
|
|
194
|
+
headline = 'VERIFICATION INCONCLUSIVE';
|
|
195
|
+
explanation = 'Evidence losses degraded this run; inspect the loss table before drawing a conclusion.';
|
|
196
|
+
exitCode = 2;
|
|
197
|
+
}
|
|
198
|
+
else if (findings.length > 0) {
|
|
137
199
|
agentVerdict = 'anomaly_candidate';
|
|
138
200
|
headline = 'ANOMALY CANDIDATE';
|
|
139
201
|
explanation = findingExplanation(String(findings[0]?.kind || ''));
|
|
@@ -171,6 +233,7 @@ export function buildDesignVerifierAgentReport(run) {
|
|
|
171
233
|
timings: run.result.timings || null,
|
|
172
234
|
changedFiles: run.result.changedFiles,
|
|
173
235
|
cases,
|
|
236
|
+
runHealth,
|
|
174
237
|
inspectCommand: `haystack design-verify ${repository}#${run.pr} --run ${run.runId}`,
|
|
175
238
|
replay: run.result.replay,
|
|
176
239
|
error: null,
|
|
@@ -243,6 +306,22 @@ export function formatDesignVerifierAgentReport(report) {
|
|
|
243
306
|
lines.push(` orchestration/finalization not recorded in a phase: ${preciseDurationLabel(report.timings.unattributedMs)}`);
|
|
244
307
|
}
|
|
245
308
|
}
|
|
309
|
+
if (report.runHealth && report.runHealth.status !== 'healthy') {
|
|
310
|
+
lines.push('', `Run health: ${report.runHealth.status}; admitted evidence: ${report.runHealth.admitted}`);
|
|
311
|
+
if (report.runHealth.losses.length > 0) {
|
|
312
|
+
lines.push('Evidence loss table:', ' owner impact count loss / stable identifiers');
|
|
313
|
+
for (const loss of report.runHealth.losses) {
|
|
314
|
+
const identifiers = [
|
|
315
|
+
...loss.caseIds,
|
|
316
|
+
...loss.obligationIds,
|
|
317
|
+
...loss.slotIds,
|
|
318
|
+
...loss.sides,
|
|
319
|
+
];
|
|
320
|
+
lines.push(` ${loss.owner.padEnd(7)} ${loss.impact.padEnd(9)} ${String(loss.count).padStart(5)} ` +
|
|
321
|
+
`${loss.code}${identifiers.length ? `: ${identifiers.join(', ')}` : ''}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
246
325
|
for (const testCase of report.cases) {
|
|
247
326
|
lines.push('', `Test: ${testCase.title}`);
|
|
248
327
|
if (testCase.story)
|
package/dist/utils/git.js
CHANGED
|
@@ -386,7 +386,11 @@ function gitCommandOptions(options) {
|
|
|
386
386
|
/** True if `ref` resolves to a commit in the current repo. */
|
|
387
387
|
function gitRefResolves(ref, options = {}) {
|
|
388
388
|
try {
|
|
389
|
-
|
|
389
|
+
// `--end-of-options`, not `--`: rev-parse treats everything after `--` as
|
|
390
|
+
// a pathspec, so `-- refs/remotes/origin/main^{commit}` exits 1 for every
|
|
391
|
+
// ref and the caller fell back to a bare local `main` (which a fresh clone
|
|
392
|
+
// does not have). Verified on git 2.39: `--` rc=1, `--end-of-options` rc=0.
|
|
393
|
+
execFileSync('git', ['rev-parse', '--verify', '--quiet', '--end-of-options', `${ref}^{commit}`], {
|
|
390
394
|
...gitCommandOptions(options),
|
|
391
395
|
});
|
|
392
396
|
return true;
|
package/dist/utils/telemetry.js
CHANGED
|
@@ -103,7 +103,10 @@ export function trackError(errorType, metadata) {
|
|
|
103
103
|
method: 'POST',
|
|
104
104
|
headers: { 'Content-Type': 'application/json' },
|
|
105
105
|
body: JSON.stringify(event),
|
|
106
|
-
}).catch(() => {
|
|
106
|
+
}).catch(() => {
|
|
107
|
+
// Documented fire-and-forget product telemetry: delivery cannot affect
|
|
108
|
+
// the CLI command being measured.
|
|
109
|
+
});
|
|
107
110
|
}
|
|
108
111
|
catch {
|
|
109
112
|
// Telemetry must never crash the CLI
|
|
@@ -135,7 +138,10 @@ export function trackSetupEvent(step, metadata) {
|
|
|
135
138
|
method: 'POST',
|
|
136
139
|
headers: { 'Content-Type': 'application/json' },
|
|
137
140
|
body: JSON.stringify(event),
|
|
138
|
-
}).catch(() => {
|
|
141
|
+
}).catch(() => {
|
|
142
|
+
// Documented fire-and-forget product telemetry: delivery cannot affect
|
|
143
|
+
// the CLI command being measured.
|
|
144
|
+
});
|
|
139
145
|
}
|
|
140
146
|
catch {
|
|
141
147
|
// Telemetry must never crash the CLI
|
package/package.json
CHANGED
|
@@ -69,57 +69,99 @@
|
|
|
69
69
|
"type": "string"
|
|
70
70
|
},
|
|
71
71
|
"result": {
|
|
72
|
+
"oneOf": [
|
|
73
|
+
{
|
|
74
|
+
"type": "object",
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"required": [
|
|
77
|
+
"verdict",
|
|
78
|
+
"summary",
|
|
79
|
+
"casesTotal",
|
|
80
|
+
"casesFailed",
|
|
81
|
+
"regressions",
|
|
82
|
+
"outputLocation",
|
|
83
|
+
"wallSeconds"
|
|
84
|
+
],
|
|
85
|
+
"properties": {
|
|
86
|
+
"verdict": {
|
|
87
|
+
"type": "string"
|
|
88
|
+
},
|
|
89
|
+
"summary": {
|
|
90
|
+
"type": "string"
|
|
91
|
+
},
|
|
92
|
+
"casesTotal": {
|
|
93
|
+
"type": "integer",
|
|
94
|
+
"minimum": 0
|
|
95
|
+
},
|
|
96
|
+
"casesFailed": {
|
|
97
|
+
"type": "integer",
|
|
98
|
+
"minimum": 0
|
|
99
|
+
},
|
|
100
|
+
"regressions": {
|
|
101
|
+
"type": "array",
|
|
102
|
+
"items": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"additionalProperties": false,
|
|
105
|
+
"required": [
|
|
106
|
+
"caseId",
|
|
107
|
+
"title"
|
|
108
|
+
],
|
|
109
|
+
"properties": {
|
|
110
|
+
"caseId": {
|
|
111
|
+
"type": "string"
|
|
112
|
+
},
|
|
113
|
+
"title": {
|
|
114
|
+
"type": "string"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"outputLocation": {
|
|
120
|
+
"type": "string"
|
|
121
|
+
},
|
|
122
|
+
"wallSeconds": {
|
|
123
|
+
"type": "number",
|
|
124
|
+
"minimum": 0
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"type": "object",
|
|
130
|
+
"description": "Opaque VerifyResultV1 document, validated by the hosted service against the canonical P0 contract.",
|
|
131
|
+
"required": [
|
|
132
|
+
"version",
|
|
133
|
+
"provider"
|
|
134
|
+
],
|
|
135
|
+
"properties": {
|
|
136
|
+
"version": {
|
|
137
|
+
"const": "dv-verify-result-v1"
|
|
138
|
+
},
|
|
139
|
+
"provider": {
|
|
140
|
+
"const": "haystack"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
"provider": {
|
|
147
|
+
"const": "haystack"
|
|
148
|
+
},
|
|
149
|
+
"version": {
|
|
150
|
+
"const": "dv-cloud-verifier-fleet-runner-projection-v1"
|
|
151
|
+
},
|
|
152
|
+
"progress": {
|
|
72
153
|
"type": "object",
|
|
73
|
-
"
|
|
154
|
+
"description": "Opaque VerifyProgressV1 document, validated by the hosted service against the canonical P0 contract.",
|
|
74
155
|
"required": [
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"casesTotal",
|
|
78
|
-
"casesFailed",
|
|
79
|
-
"regressions",
|
|
80
|
-
"outputLocation",
|
|
81
|
-
"wallSeconds"
|
|
156
|
+
"version",
|
|
157
|
+
"publicRunId"
|
|
82
158
|
],
|
|
83
159
|
"properties": {
|
|
84
|
-
"
|
|
85
|
-
"
|
|
160
|
+
"version": {
|
|
161
|
+
"const": "dv-verify-progress-v1"
|
|
86
162
|
},
|
|
87
|
-
"
|
|
163
|
+
"publicRunId": {
|
|
88
164
|
"type": "string"
|
|
89
|
-
},
|
|
90
|
-
"casesTotal": {
|
|
91
|
-
"type": "integer",
|
|
92
|
-
"minimum": 0
|
|
93
|
-
},
|
|
94
|
-
"casesFailed": {
|
|
95
|
-
"type": "integer",
|
|
96
|
-
"minimum": 0
|
|
97
|
-
},
|
|
98
|
-
"regressions": {
|
|
99
|
-
"type": "array",
|
|
100
|
-
"items": {
|
|
101
|
-
"type": "object",
|
|
102
|
-
"additionalProperties": false,
|
|
103
|
-
"required": [
|
|
104
|
-
"caseId",
|
|
105
|
-
"title"
|
|
106
|
-
],
|
|
107
|
-
"properties": {
|
|
108
|
-
"caseId": {
|
|
109
|
-
"type": "string"
|
|
110
|
-
},
|
|
111
|
-
"title": {
|
|
112
|
-
"type": "string"
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
"outputLocation": {
|
|
118
|
-
"type": "string"
|
|
119
|
-
},
|
|
120
|
-
"wallSeconds": {
|
|
121
|
-
"type": "number",
|
|
122
|
-
"minimum": 0
|
|
123
165
|
}
|
|
124
166
|
}
|
|
125
167
|
}
|
|
@@ -157,6 +199,27 @@
|
|
|
157
199
|
}
|
|
158
200
|
]
|
|
159
201
|
}
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"if": {
|
|
205
|
+
"properties": {
|
|
206
|
+
"provider": {
|
|
207
|
+
"const": "haystack"
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"required": [
|
|
211
|
+
"provider"
|
|
212
|
+
]
|
|
213
|
+
},
|
|
214
|
+
"then": {
|
|
215
|
+
"required": [
|
|
216
|
+
"version",
|
|
217
|
+
"repository",
|
|
218
|
+
"base_commit",
|
|
219
|
+
"head_commit",
|
|
220
|
+
"created_at"
|
|
221
|
+
]
|
|
222
|
+
}
|
|
160
223
|
}
|
|
161
224
|
]
|
|
162
225
|
}
|