@haystackeditor/cli 0.25.1 → 0.27.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 +39 -463
- package/dist/capture/app-config.js +30 -6
- package/dist/commands/capture-brief.js +45 -35
- package/dist/commands/capture-contract.js +4 -4
- package/dist/commands/crawl-report.js +196 -0
- package/dist/commands/db-profile-upload.js +3 -3
- package/dist/commands/feedback.js +66 -0
- package/dist/commands/init-telemetry.js +55 -9
- package/dist/commands/init.js +8 -3
- package/dist/commands/lockfile-pin.js +307 -0
- package/dist/commands/telemetry-token.js +3 -3
- package/dist/commands/tokens.js +4 -4
- package/dist/commands/verify-explore.js +3 -3
- package/dist/commands/verify-history.js +2 -2
- package/dist/commands/verify-onboarding.js +13 -16
- package/dist/commands/verify-precompute.js +3 -4
- package/dist/commands/verify.js +31 -118
- package/dist/index.js +55 -968
- package/dist/schema.js +4 -10
- package/dist/utils/haystack-api.js +8 -36
- package/package.json +1 -5
- package/schemas/feedback.v1.json +13 -0
- package/schemas/pre-verify.v2.json +240 -0
- package/schemas/verify-raw.v1.json +1132 -0
- package/schemas/verify.v2.json +655 -0
- package/dist/assets/hooks/agent-context/detect.ts +0 -316
- package/dist/assets/hooks/agent-context/format.ts +0 -100
- package/dist/assets/hooks/agent-context/index.ts +0 -41
- package/dist/assets/hooks/agent-context/parsers/claude.ts +0 -262
- package/dist/assets/hooks/agent-context/parsers/codex.ts +0 -416
- package/dist/assets/hooks/agent-context/parsers/gemini.ts +0 -155
- package/dist/assets/hooks/agent-context/parsers/opencode.ts +0 -174
- package/dist/assets/hooks/agent-context/tsconfig.json +0 -14
- package/dist/assets/hooks/agent-context/types.ts +0 -58
- package/dist/assets/hooks/llm-rules-template.md +0 -59
- package/dist/assets/hooks/package-lock.json +0 -598
- package/dist/assets/hooks/package.json +0 -12
- package/dist/assets/hooks/scripts/commit-msg.sh +0 -5
- package/dist/assets/hooks/scripts/post-commit.sh +0 -5
- package/dist/assets/hooks/scripts/pre-commit.sh +0 -175
- package/dist/assets/hooks/scripts/pre-push.sh +0 -25
- package/dist/assets/hooks/scripts/prepare-commit-msg.sh +0 -5
- package/dist/assets/hooks/truncation-checker/ast-analyzer.ts +0 -528
- package/dist/assets/hooks/truncation-checker/index.ts +0 -595
- package/dist/assets/hooks/truncation-checker/tsconfig.json +0 -13
- package/dist/assets/skills/map-cloud-verifier-universe/SKILL.md +0 -2051
- package/dist/assets/skills/map-cloud-verifier-universe/agents/openai.yaml +0 -4
- package/dist/assets/skills/map-cloud-verifier-universe/references/output-contract.md +0 -3411
- package/dist/assets/skills/map-your-system.md +0 -143
- package/dist/assets/skills/submit.md +0 -200
- package/dist/commands/ask.js +0 -20
- package/dist/commands/cloud-verifier-behaviors.js +0 -218
- package/dist/commands/cloud-verifier-data-store-census.js +0 -539
- package/dist/commands/cloud-verifier-data-store-drift.js +0 -158
- package/dist/commands/cloud-verifier-identity-census.js +0 -4060
- package/dist/commands/cloud-verifier-materialization.js +0 -704
- package/dist/commands/cloud-verifier-pascal-selector-census.js +0 -1382
- package/dist/commands/cloud-verifier-python-manifest-selector-census.js +0 -2015
- package/dist/commands/cloud-verifier-specialized-operational-census.js +0 -11432
- package/dist/commands/cloud-verifier-universe.js +0 -10178
- package/dist/commands/config.js +0 -549
- package/dist/commands/design-verify.js +0 -311
- package/dist/commands/dismiss.js +0 -159
- package/dist/commands/hooks.js +0 -226
- package/dist/commands/inbox.js +0 -137
- package/dist/commands/mcp.js +0 -201
- package/dist/commands/policy.js +0 -371
- package/dist/commands/pr-status.js +0 -207
- package/dist/commands/pr.js +0 -105
- package/dist/commands/prepare-universe-review.js +0 -1092
- package/dist/commands/production-source-deny-policy.js +0 -100
- package/dist/commands/request-review.js +0 -74
- package/dist/commands/review.js +0 -191
- package/dist/commands/rules.js +0 -98
- package/dist/commands/scaffold-provisional-universe.js +0 -806
- package/dist/commands/setup.js +0 -1170
- package/dist/commands/skills.js +0 -447
- package/dist/commands/status.js +0 -35
- package/dist/commands/submit.js +0 -745
- package/dist/commands/system-map.js +0 -228
- package/dist/commands/triage.js +0 -598
- package/dist/commands/webhooks.js +0 -241
- package/dist/states.js +0 -46
- package/dist/tools/detect.js +0 -832
- package/dist/triage/astra.js +0 -202
- package/dist/triage/prompts.js +0 -188
- package/dist/triage/runner.js +0 -200
- package/dist/triage/types.js +0 -7
- package/dist/types.js +0 -326
- package/dist/utils/action-output.js +0 -26
- package/dist/utils/analysis-api.js +0 -416
- package/dist/utils/config.js +0 -54
- package/dist/utils/design-verifier-api.js +0 -294
- package/dist/utils/design-verifier-history.js +0 -79
- package/dist/utils/design-verifier-result.js +0 -424
- package/dist/utils/github-api.js +0 -324
- package/dist/utils/pending-state.js +0 -86
- package/dist/utils/pr-ref.js +0 -56
- package/dist/utils/prompter.js +0 -328
- package/schemas/action.v1.json +0 -22
- package/schemas/ask.v1.json +0 -40
- package/schemas/inbox.v1.json +0 -27
- package/schemas/pr-status.v1.json +0 -61
- package/schemas/pr.v1.json +0 -97
- package/schemas/pr.v3.json +0 -45
- package/schemas/setup.v1.json +0 -75
- package/schemas/submit.v1.json +0 -90
- package/schemas/triage.v1.json +0 -103
- package/schemas/triage.v2.json +0 -64
|
@@ -1,424 +0,0 @@
|
|
|
1
|
-
import { isDeepStrictEqual } from 'node:util';
|
|
2
|
-
function findingExplanation(kind) {
|
|
3
|
-
if (kind === 'declared-change-not-observed') {
|
|
4
|
-
return 'The registered intended behavior change was not observed; investigate the captured outputs before concluding why.';
|
|
5
|
-
}
|
|
6
|
-
if (kind === 'unexpected-change') {
|
|
7
|
-
return 'The proposed revision produced a difference outside this case’s registered intent; this is an anomaly candidate for investigation.';
|
|
8
|
-
}
|
|
9
|
-
if (kind === 'intended-change-control-confounded') {
|
|
10
|
-
return 'The intended difference was observed, but control-before also varied on an intersecting output path; inspect it before claiming causality.';
|
|
11
|
-
}
|
|
12
|
-
return 'Executed behavior produced a suspicious difference against the registered expectation.';
|
|
13
|
-
}
|
|
14
|
-
function sameRecordedValue(left, right) {
|
|
15
|
-
return isDeepStrictEqual(left, right);
|
|
16
|
-
}
|
|
17
|
-
function executionSummaries(value) {
|
|
18
|
-
if (!Array.isArray(value))
|
|
19
|
-
return [];
|
|
20
|
-
return value.filter((execution) => !!execution && typeof execution === 'object' &&
|
|
21
|
-
['base1', 'base2', 'head'].includes(execution.side) &&
|
|
22
|
-
['completed', 'failed', 'missing'].includes(execution.status) &&
|
|
23
|
-
Array.isArray(execution.diagnostics) && Array.isArray(execution.outputExcerpt));
|
|
24
|
-
}
|
|
25
|
-
function universeSummaries(value, caseId) {
|
|
26
|
-
if (!Array.isArray(value))
|
|
27
|
-
return [];
|
|
28
|
-
return value.filter((universe) => universe?.version === 'dv-case-universe-summary-v1' && universe.caseId === caseId &&
|
|
29
|
-
/^universe-[0-9a-f]{24}$/.test(universe.universeId || '') &&
|
|
30
|
-
['control-before', 'before', 'after'].includes(universe.role) &&
|
|
31
|
-
universe.lease && typeof universe.lease.available === 'boolean');
|
|
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
|
-
}
|
|
81
|
-
export function buildDesignVerifierAgentReport(run) {
|
|
82
|
-
const repository = `${run.owner}/${run.repo}`;
|
|
83
|
-
if (run.status !== 'succeeded' || !run.result) {
|
|
84
|
-
return {
|
|
85
|
-
ok: false,
|
|
86
|
-
status: 'failed',
|
|
87
|
-
verdict: 'inconclusive',
|
|
88
|
-
headline: 'VERIFICATION INCONCLUSIVE',
|
|
89
|
-
explanation: run.error || 'The verifier did not produce a terminal behavioral verdict.',
|
|
90
|
-
runId: run.runId,
|
|
91
|
-
repository,
|
|
92
|
-
pullRequest: run.pr,
|
|
93
|
-
revisions: { base: run.baseSha, head: run.headSha },
|
|
94
|
-
durationMs: run.durationMs,
|
|
95
|
-
timings: null,
|
|
96
|
-
changedFiles: [],
|
|
97
|
-
cases: [],
|
|
98
|
-
runHealth: null,
|
|
99
|
-
inspectCommand: `haystack design-verify ${repository}#${run.pr} --run ${run.runId}`,
|
|
100
|
-
replay: null,
|
|
101
|
-
error: run.error,
|
|
102
|
-
exitCode: 2,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
const verdict = run.result.verdict || {};
|
|
106
|
-
const summary = verdict.summary || {};
|
|
107
|
-
const runHealth = runHealthOf(verdict.runHealth) || missingRunHealth();
|
|
108
|
-
const findings = Array.isArray(verdict.findings) ? verdict.findings : [];
|
|
109
|
-
const confirmations = Array.isArray(verdict.confirmations) ? verdict.confirmations : [];
|
|
110
|
-
const findingsByCase = new Map();
|
|
111
|
-
for (const finding of findings) {
|
|
112
|
-
const caseFindings = findingsByCase.get(String(finding?.caseId || '')) || [];
|
|
113
|
-
caseFindings.push(finding);
|
|
114
|
-
findingsByCase.set(String(finding?.caseId || ''), caseFindings);
|
|
115
|
-
}
|
|
116
|
-
const confirmedIds = new Set(confirmations.map((confirmation) => confirmation.caseId));
|
|
117
|
-
const scenarioByCase = new Map(run.result.scenarios.map((scenario) => [scenario.caseId, scenario]));
|
|
118
|
-
const probeByCase = new Map(run.result.probes.map((probe) => [probe.caseId, probe]));
|
|
119
|
-
const cases = run.result.cases.map((testCase) => {
|
|
120
|
-
const id = String(testCase.id || '');
|
|
121
|
-
const caseFindings = findingsByCase.get(id) || [];
|
|
122
|
-
const finding = caseFindings[0];
|
|
123
|
-
const scenario = scenarioByCase.get(id);
|
|
124
|
-
const probe = probeByCase.get(id);
|
|
125
|
-
const observations = (Array.isArray(scenario?.divergences) ? scenario.divergences : []).map((item) => ({
|
|
126
|
-
path: String(item.display || (Array.isArray(item.path) ? item.path.join('.') : 'observation')),
|
|
127
|
-
base: item.base,
|
|
128
|
-
baseRepeat: item.base2,
|
|
129
|
-
head: item.head,
|
|
130
|
-
controlObservedAtSamePath: item.controlObservedAtSamePath === true,
|
|
131
|
-
controlObservedAtAncestorPath: item.controlObservedAtAncestorPath === true,
|
|
132
|
-
controlObservedAtDescendantPath: item.controlObservedAtDescendantPath === true,
|
|
133
|
-
}));
|
|
134
|
-
const hasControlEvidence = typeof scenario?.noisePathCount === 'number' ||
|
|
135
|
-
scenario?.controlScenarioErrorVariation != null;
|
|
136
|
-
return {
|
|
137
|
-
id,
|
|
138
|
-
title: String(testCase.title || scenario?.scenario || id),
|
|
139
|
-
story: String(testCase.story || ''),
|
|
140
|
-
expectedHead: String(testCase.expected_head_observation || ''),
|
|
141
|
-
verdict: caseFindings.length
|
|
142
|
-
? 'finding'
|
|
143
|
-
: confirmedIds.has(id)
|
|
144
|
-
? 'confirmed'
|
|
145
|
-
: scenario?.status === 'clean'
|
|
146
|
-
? 'clean'
|
|
147
|
-
: 'inconclusive',
|
|
148
|
-
findingKind: finding?.kind || null,
|
|
149
|
-
findingKinds: caseFindings
|
|
150
|
-
.map((item) => String(item?.kind || ''))
|
|
151
|
-
.filter(Boolean),
|
|
152
|
-
baseStable: hasControlEvidence
|
|
153
|
-
? scenario.noisePathCount === 0 && scenario.controlScenarioErrorVariation == null
|
|
154
|
-
: observations.length
|
|
155
|
-
? observations.every((item) => sameRecordedValue(item.base, item.baseRepeat))
|
|
156
|
-
: scenario?.status === 'clean' ? true : null,
|
|
157
|
-
observations,
|
|
158
|
-
generatedFiles: probe?.files || [],
|
|
159
|
-
executedCommand: probe?.run || null,
|
|
160
|
-
executions: executionSummaries(scenario?.executions),
|
|
161
|
-
universes: universeSummaries(scenario?.universes, id),
|
|
162
|
-
infrastructure: scenario?.infrastructureReceipt &&
|
|
163
|
-
typeof scenario.infrastructureReceipt === 'object'
|
|
164
|
-
? {
|
|
165
|
-
verified: scenario.infrastructureReceipt.status === 'passed',
|
|
166
|
-
recipeId: typeof scenario.infrastructureReceipt.recipeId === 'string'
|
|
167
|
-
? scenario.infrastructureReceipt.recipeId
|
|
168
|
-
: null,
|
|
169
|
-
freshInstances: Array.isArray(scenario.infrastructureReceipt.environmentInstanceIds)
|
|
170
|
-
? scenario.infrastructureReceipt.environmentInstanceIds.length
|
|
171
|
-
: 0,
|
|
172
|
-
baseRepeat: typeof scenario.infrastructureReceipt.baseRepeat === 'string'
|
|
173
|
-
? scenario.infrastructureReceipt.baseRepeat
|
|
174
|
-
: null,
|
|
175
|
-
errors: Array.isArray(scenario.infrastructureReceipt.errors)
|
|
176
|
-
? scenario.infrastructureReceipt.errors.filter((item) => typeof item === 'string')
|
|
177
|
-
: [],
|
|
178
|
-
}
|
|
179
|
-
: null,
|
|
180
|
-
};
|
|
181
|
-
});
|
|
182
|
-
let agentVerdict;
|
|
183
|
-
let headline;
|
|
184
|
-
let explanation;
|
|
185
|
-
let exitCode;
|
|
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) {
|
|
199
|
-
agentVerdict = 'anomaly_candidate';
|
|
200
|
-
headline = 'ANOMALY CANDIDATE';
|
|
201
|
-
explanation = findingExplanation(String(findings[0]?.kind || ''));
|
|
202
|
-
exitCode = 1;
|
|
203
|
-
}
|
|
204
|
-
else if (summary.caseDesignOutcome === 'no-supported-cases' || Number(summary.scenarios || 0) === 0) {
|
|
205
|
-
agentVerdict = 'inconclusive';
|
|
206
|
-
headline = 'VERIFICATION INCONCLUSIVE';
|
|
207
|
-
explanation = 'The verifier could not design and execute a supported behavioral case.';
|
|
208
|
-
exitCode = 2;
|
|
209
|
-
}
|
|
210
|
-
else if (confirmations.length > 0) {
|
|
211
|
-
agentVerdict = 'fix_confirmed';
|
|
212
|
-
headline = 'FIX CONFIRMED';
|
|
213
|
-
explanation = 'The intended behavior change was observed on output paths that did not vary between control-before and before.';
|
|
214
|
-
exitCode = 0;
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
agentVerdict = 'no_unexpected_change';
|
|
218
|
-
headline = 'NO UNEXPECTED CHANGE';
|
|
219
|
-
explanation = 'The executed cases found no observed difference outside their registered expectations.';
|
|
220
|
-
exitCode = 0;
|
|
221
|
-
}
|
|
222
|
-
return {
|
|
223
|
-
ok: exitCode === 0,
|
|
224
|
-
status: 'complete',
|
|
225
|
-
verdict: agentVerdict,
|
|
226
|
-
headline,
|
|
227
|
-
explanation,
|
|
228
|
-
runId: run.runId,
|
|
229
|
-
repository,
|
|
230
|
-
pullRequest: run.pr,
|
|
231
|
-
revisions: { base: run.baseSha, head: run.headSha },
|
|
232
|
-
durationMs: run.durationMs,
|
|
233
|
-
timings: run.result.timings || null,
|
|
234
|
-
changedFiles: run.result.changedFiles,
|
|
235
|
-
cases,
|
|
236
|
-
runHealth,
|
|
237
|
-
inspectCommand: `haystack design-verify ${repository}#${run.pr} --run ${run.runId}`,
|
|
238
|
-
replay: run.result.replay,
|
|
239
|
-
error: null,
|
|
240
|
-
exitCode,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
function displayValue(value) {
|
|
244
|
-
const encoded = JSON.stringify(value);
|
|
245
|
-
return encoded === undefined ? String(value) : encoded;
|
|
246
|
-
}
|
|
247
|
-
function durationLabel(durationMs) {
|
|
248
|
-
if (durationMs === null)
|
|
249
|
-
return '';
|
|
250
|
-
const totalSeconds = Math.round(durationMs / 1000);
|
|
251
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
252
|
-
const seconds = totalSeconds % 60;
|
|
253
|
-
return minutes ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
254
|
-
}
|
|
255
|
-
function preciseDurationLabel(durationMs) {
|
|
256
|
-
if (durationMs === null)
|
|
257
|
-
return 'not recorded';
|
|
258
|
-
if (durationMs < 1000)
|
|
259
|
-
return `${Number(durationMs.toFixed(3))}ms`;
|
|
260
|
-
const minutes = Math.floor(durationMs / 60_000);
|
|
261
|
-
const seconds = Number(((durationMs % 60_000) / 1000).toFixed(3));
|
|
262
|
-
return minutes ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
263
|
-
}
|
|
264
|
-
function timingStageLabel(stage) {
|
|
265
|
-
if (stage === 'blast-radius')
|
|
266
|
-
return 'blast radius';
|
|
267
|
-
if (stage === 'case-design')
|
|
268
|
-
return 'case design';
|
|
269
|
-
return stage;
|
|
270
|
-
}
|
|
271
|
-
function commandLabel(command) {
|
|
272
|
-
if (!command)
|
|
273
|
-
return null;
|
|
274
|
-
return command.argv.map((arg) => (/^[A-Za-z0-9_./:=+-]+$/.test(arg) ? arg : JSON.stringify(arg))).join(' ');
|
|
275
|
-
}
|
|
276
|
-
function executionSideLabel(side) {
|
|
277
|
-
if (side === 'base1')
|
|
278
|
-
return 'before';
|
|
279
|
-
if (side === 'base2')
|
|
280
|
-
return 'control-before';
|
|
281
|
-
return 'after';
|
|
282
|
-
}
|
|
283
|
-
function executionNeedsAttention(execution) {
|
|
284
|
-
return execution.status !== 'completed' || execution.diagnostics.length > 0;
|
|
285
|
-
}
|
|
286
|
-
export function formatDesignVerifierAgentReport(report) {
|
|
287
|
-
const timing = durationLabel(report.durationMs);
|
|
288
|
-
const lines = [
|
|
289
|
-
`${report.exitCode === 0 ? '✓' : report.exitCode === 1 ? '✗' : '?'} ${report.headline}${timing ? ` ${timing}` : ''}`,
|
|
290
|
-
'',
|
|
291
|
-
report.explanation,
|
|
292
|
-
];
|
|
293
|
-
if (report.revisions.base || report.revisions.head) {
|
|
294
|
-
lines.push('', `Revisions: before ${report.revisions.base || 'unknown'} (control + comparison)`, ` after ${report.revisions.head || 'unknown'} (comparison)`);
|
|
295
|
-
}
|
|
296
|
-
if (report.timings) {
|
|
297
|
-
lines.push('', 'Timing breakdown:');
|
|
298
|
-
if (report.timings.admissionMs !== null) {
|
|
299
|
-
lines.push(` admission: ${preciseDurationLabel(report.timings.admissionMs)}`);
|
|
300
|
-
}
|
|
301
|
-
for (const stage of report.timings.stages) {
|
|
302
|
-
lines.push(` ${timingStageLabel(stage.stage)}: ${preciseDurationLabel(stage.durationMs)} (${stage.status})`);
|
|
303
|
-
}
|
|
304
|
-
lines.push(` pipeline total: ${preciseDurationLabel(report.timings.pipelineDurationMs)}`);
|
|
305
|
-
if (report.timings.unattributedMs !== null) {
|
|
306
|
-
lines.push(` orchestration/finalization not recorded in a phase: ${preciseDurationLabel(report.timings.unattributedMs)}`);
|
|
307
|
-
}
|
|
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
|
-
}
|
|
325
|
-
for (const testCase of report.cases) {
|
|
326
|
-
lines.push('', `Test: ${testCase.title}`);
|
|
327
|
-
if (testCase.story)
|
|
328
|
-
lines.push(` Behavior: ${testCase.story}`);
|
|
329
|
-
if (testCase.expectedHead)
|
|
330
|
-
lines.push(` Expected on head: ${testCase.expectedHead}`);
|
|
331
|
-
if (testCase.findingKinds.length > 0) {
|
|
332
|
-
lines.push(` Candidate reasons: ${testCase.findingKinds.join(', ')}`);
|
|
333
|
-
}
|
|
334
|
-
if (testCase.observations.length) {
|
|
335
|
-
lines.push(' Observed:');
|
|
336
|
-
for (const observation of testCase.observations) {
|
|
337
|
-
lines.push(` ${observation.path}`);
|
|
338
|
-
lines.push(` control-before: ${displayValue(observation.baseRepeat)}`);
|
|
339
|
-
lines.push(` before: ${displayValue(observation.base)}`);
|
|
340
|
-
lines.push(` after: ${displayValue(observation.head)}`);
|
|
341
|
-
if (observation.controlObservedAtSamePath || observation.controlObservedAtAncestorPath ||
|
|
342
|
-
observation.controlObservedAtDescendantPath) {
|
|
343
|
-
lines.push(` control evidence: ${observation.controlObservedAtSamePath
|
|
344
|
-
? 'variation observed at this same path'
|
|
345
|
-
: observation.controlObservedAtAncestorPath
|
|
346
|
-
? 'variation observed at an ancestor path'
|
|
347
|
-
: 'variation observed at a descendant path'}`);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
if (testCase.baseStable !== null) {
|
|
352
|
-
lines.push(` Control evidence: ${testCase.baseStable
|
|
353
|
-
? 'control-before and before agreed'
|
|
354
|
-
: 'control-before and before varied; inspect alongside the difference'}`);
|
|
355
|
-
}
|
|
356
|
-
if (testCase.infrastructure) {
|
|
357
|
-
lines.push(testCase.infrastructure.verified
|
|
358
|
-
? ` Infrastructure: verified; same recipe, ${testCase.infrastructure.freshInstances} fresh instances`
|
|
359
|
-
: ` Infrastructure: unverified; ${testCase.infrastructure.errors.join('; ') || 'receipt missing'}`);
|
|
360
|
-
}
|
|
361
|
-
const executionsWithDiagnostics = testCase.executions.filter(executionNeedsAttention);
|
|
362
|
-
if (executionsWithDiagnostics.length) {
|
|
363
|
-
lines.push(' Execution diagnostics:');
|
|
364
|
-
for (const execution of executionsWithDiagnostics) {
|
|
365
|
-
const exit = execution.commandExitCode === null ? 'exit unknown' : `exit ${execution.commandExitCode}`;
|
|
366
|
-
const elapsed = execution.durationMs === null ? '' : `, ${durationLabel(execution.durationMs)}`;
|
|
367
|
-
lines.push(` ${executionSideLabel(execution.side)} (${exit}${elapsed}):`);
|
|
368
|
-
if (execution.scenarioError)
|
|
369
|
-
lines.push(` Scenario error: ${execution.scenarioError}`);
|
|
370
|
-
for (const diagnostic of execution.diagnostics) {
|
|
371
|
-
const identity = [diagnostic.type, diagnostic.code].filter(Boolean).join(', ');
|
|
372
|
-
lines.push(` ${diagnostic.kind}${identity ? ` (${identity})` : ''}: ${diagnostic.message}`);
|
|
373
|
-
}
|
|
374
|
-
if (execution.diagnosticsOmitted > 0) {
|
|
375
|
-
lines.push(` ... ${execution.diagnosticsOmitted} additional diagnostics omitted`);
|
|
376
|
-
}
|
|
377
|
-
if (execution.outputExcerpt.length) {
|
|
378
|
-
lines.push(' Relevant command output:');
|
|
379
|
-
for (const output of execution.outputExcerpt) {
|
|
380
|
-
lines.push(` ${output.stream}: ${output.text}`);
|
|
381
|
-
}
|
|
382
|
-
if (execution.outputLinesOmitted > 0) {
|
|
383
|
-
lines.push(` ... ${execution.outputLinesOmitted} output lines omitted`);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
lines.push(` Full output artifact: ${execution.outputArtifact}`);
|
|
387
|
-
lines.push(` Structured trace artifact: ${execution.traceArtifact}`);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if (testCase.generatedFiles.length)
|
|
391
|
-
lines.push(` Generated probe artifact: ${testCase.generatedFiles.join(', ')}`);
|
|
392
|
-
const command = commandLabel(testCase.executedCommand);
|
|
393
|
-
if (command)
|
|
394
|
-
lines.push(` Executed in the sandbox: ${command}`);
|
|
395
|
-
for (const universe of testCase.universes.filter(item => item.lease.available)) {
|
|
396
|
-
lines.push(` Replay ${universe.role}: haystack design-verify ${report.repository}#${report.pullRequest}` +
|
|
397
|
-
` --run ${report.runId} --case ${testCase.id} --universe ${universe.role} --replay`);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
if (report.changedFiles.length) {
|
|
401
|
-
lines.push('', 'Changed files to inspect:', ...report.changedFiles.map((file) => ` ${file}`));
|
|
402
|
-
}
|
|
403
|
-
lines.push('', `Inspect this exact result again: ${report.inspectCommand}`, `Run: ${report.runId}`);
|
|
404
|
-
return lines.join('\n');
|
|
405
|
-
}
|
|
406
|
-
export function designVerifierStageLabel(stage) {
|
|
407
|
-
if (!stage)
|
|
408
|
-
return 'Waiting for the run to start';
|
|
409
|
-
if (stage === 'prepare' || stage === 'bake')
|
|
410
|
-
return 'Preparing the repository';
|
|
411
|
-
if (stage === 'blast-radius')
|
|
412
|
-
return 'Mapping affected behavior';
|
|
413
|
-
if (stage === 'prefetch')
|
|
414
|
-
return 'Retrieving relevant failure patterns';
|
|
415
|
-
if (stage === 'stage1')
|
|
416
|
-
return 'Designing behavioral test cases';
|
|
417
|
-
if (stage.startsWith('build:'))
|
|
418
|
-
return 'Building an executable probe';
|
|
419
|
-
if (stage.startsWith('execute:'))
|
|
420
|
-
return 'Running control-before/before/after comparisons';
|
|
421
|
-
if (stage === 'verdict')
|
|
422
|
-
return 'Comparing observations';
|
|
423
|
-
return stage;
|
|
424
|
-
}
|