@haystackeditor/cli 0.25.1 → 0.26.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.
Files changed (97) hide show
  1. package/README.md +39 -455
  2. package/dist/capture/app-config.js +25 -1
  3. package/dist/commands/capture-brief.js +41 -32
  4. package/dist/commands/feedback.js +66 -0
  5. package/dist/commands/init-telemetry.js +53 -7
  6. package/dist/commands/init.js +7 -2
  7. package/dist/commands/lockfile-pin.js +307 -0
  8. package/dist/commands/verify.js +68 -13
  9. package/dist/index.js +27 -923
  10. package/dist/schema.js +4 -10
  11. package/dist/utils/haystack-api.js +0 -36
  12. package/package.json +1 -5
  13. package/schemas/feedback.v1.json +13 -0
  14. package/schemas/pre-verify.v2.json +240 -0
  15. package/schemas/verify-raw.v1.json +1132 -0
  16. package/schemas/verify.v2.json +655 -0
  17. package/dist/assets/hooks/agent-context/detect.ts +0 -316
  18. package/dist/assets/hooks/agent-context/format.ts +0 -100
  19. package/dist/assets/hooks/agent-context/index.ts +0 -41
  20. package/dist/assets/hooks/agent-context/parsers/claude.ts +0 -262
  21. package/dist/assets/hooks/agent-context/parsers/codex.ts +0 -416
  22. package/dist/assets/hooks/agent-context/parsers/gemini.ts +0 -155
  23. package/dist/assets/hooks/agent-context/parsers/opencode.ts +0 -174
  24. package/dist/assets/hooks/agent-context/tsconfig.json +0 -14
  25. package/dist/assets/hooks/agent-context/types.ts +0 -58
  26. package/dist/assets/hooks/llm-rules-template.md +0 -59
  27. package/dist/assets/hooks/package-lock.json +0 -598
  28. package/dist/assets/hooks/package.json +0 -12
  29. package/dist/assets/hooks/scripts/commit-msg.sh +0 -5
  30. package/dist/assets/hooks/scripts/post-commit.sh +0 -5
  31. package/dist/assets/hooks/scripts/pre-commit.sh +0 -175
  32. package/dist/assets/hooks/scripts/pre-push.sh +0 -25
  33. package/dist/assets/hooks/scripts/prepare-commit-msg.sh +0 -5
  34. package/dist/assets/hooks/truncation-checker/ast-analyzer.ts +0 -528
  35. package/dist/assets/hooks/truncation-checker/index.ts +0 -595
  36. package/dist/assets/hooks/truncation-checker/tsconfig.json +0 -13
  37. package/dist/assets/skills/map-cloud-verifier-universe/SKILL.md +0 -2051
  38. package/dist/assets/skills/map-cloud-verifier-universe/agents/openai.yaml +0 -4
  39. package/dist/assets/skills/map-cloud-verifier-universe/references/output-contract.md +0 -3411
  40. package/dist/assets/skills/map-your-system.md +0 -143
  41. package/dist/assets/skills/submit.md +0 -200
  42. package/dist/commands/ask.js +0 -20
  43. package/dist/commands/cloud-verifier-behaviors.js +0 -218
  44. package/dist/commands/cloud-verifier-data-store-census.js +0 -539
  45. package/dist/commands/cloud-verifier-data-store-drift.js +0 -158
  46. package/dist/commands/cloud-verifier-identity-census.js +0 -4060
  47. package/dist/commands/cloud-verifier-materialization.js +0 -704
  48. package/dist/commands/cloud-verifier-pascal-selector-census.js +0 -1382
  49. package/dist/commands/cloud-verifier-python-manifest-selector-census.js +0 -2015
  50. package/dist/commands/cloud-verifier-specialized-operational-census.js +0 -11432
  51. package/dist/commands/cloud-verifier-universe.js +0 -10178
  52. package/dist/commands/config.js +0 -549
  53. package/dist/commands/design-verify.js +0 -311
  54. package/dist/commands/dismiss.js +0 -159
  55. package/dist/commands/hooks.js +0 -226
  56. package/dist/commands/inbox.js +0 -137
  57. package/dist/commands/mcp.js +0 -201
  58. package/dist/commands/policy.js +0 -371
  59. package/dist/commands/pr-status.js +0 -207
  60. package/dist/commands/pr.js +0 -105
  61. package/dist/commands/prepare-universe-review.js +0 -1092
  62. package/dist/commands/production-source-deny-policy.js +0 -100
  63. package/dist/commands/request-review.js +0 -74
  64. package/dist/commands/review.js +0 -191
  65. package/dist/commands/rules.js +0 -98
  66. package/dist/commands/scaffold-provisional-universe.js +0 -806
  67. package/dist/commands/setup.js +0 -1170
  68. package/dist/commands/skills.js +0 -447
  69. package/dist/commands/submit.js +0 -745
  70. package/dist/commands/system-map.js +0 -228
  71. package/dist/commands/triage.js +0 -598
  72. package/dist/commands/webhooks.js +0 -241
  73. package/dist/states.js +0 -46
  74. package/dist/tools/detect.js +0 -832
  75. package/dist/triage/astra.js +0 -202
  76. package/dist/triage/prompts.js +0 -188
  77. package/dist/triage/runner.js +0 -200
  78. package/dist/triage/types.js +0 -7
  79. package/dist/utils/action-output.js +0 -26
  80. package/dist/utils/analysis-api.js +0 -416
  81. package/dist/utils/design-verifier-api.js +0 -294
  82. package/dist/utils/design-verifier-history.js +0 -79
  83. package/dist/utils/design-verifier-result.js +0 -424
  84. package/dist/utils/github-api.js +0 -324
  85. package/dist/utils/pending-state.js +0 -86
  86. package/dist/utils/pr-ref.js +0 -56
  87. package/dist/utils/prompter.js +0 -328
  88. package/schemas/action.v1.json +0 -22
  89. package/schemas/ask.v1.json +0 -40
  90. package/schemas/inbox.v1.json +0 -27
  91. package/schemas/pr-status.v1.json +0 -61
  92. package/schemas/pr.v1.json +0 -97
  93. package/schemas/pr.v3.json +0 -45
  94. package/schemas/setup.v1.json +0 -75
  95. package/schemas/submit.v1.json +0 -90
  96. package/schemas/triage.v1.json +0 -103
  97. 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
- }