feat: plugin contract e2e, qualify --changed, production observe, regressions

This commit is contained in:
John Dvorak
2026-05-22 11:05:52 -07:00
parent d0523fcc2d
commit 1de735ee08
34 changed files with 1392 additions and 122 deletions
+1 -1
View File
@@ -140,7 +140,7 @@ const CONFIG_SCHEMA: Record<string, SchemaField> = {
optional: true,
items: { type: 'object' },
},
chaos: {
pluginContracts: {
type: 'object',
optional: true,
properties: {},
+1 -1
View File
@@ -93,7 +93,7 @@ export function createContext(options: Record<string, unknown> = {}): CliContext
const packageManager = detectPackageManager(cwd);
// Normalize options
const format = options.format === 'json' || options.format === 'ndjson'
const format = options.format === 'json' || options.format === 'ndjson' || options.format === 'json-summary' || options.format === 'ndjson-summary'
? options.format
: 'human';
+7 -4
View File
@@ -102,9 +102,11 @@ function getCommandHelp(command: string): string {
${pc.dim('Options:')}
--profile <name> Profile name from config
--seed <number> Deterministic seed
--changed Filter to git-modified routes
${pc.dim('Examples:')}
apophis qualify --profile oauth-nightly --seed 42
apophis qualify --changed
`,
replay: `
${pc.bold('apophis replay')} — Replay a failure using seed and stored trace
@@ -167,18 +169,18 @@ function printInternalError(error: unknown): void {
console.error();
}
function resolveRequestedFormat(argv: string[]): 'human' | 'json' | 'ndjson' {
function resolveRequestedFormat(argv: string[]): 'human' | 'json' | 'ndjson' | 'json-summary' | 'ndjson-summary' {
for (let i = 0; i < argv.length; i++) {
const arg = argv[i];
if (!arg) continue;
if (arg === '--format' && argv[i + 1]) {
const value = argv[i + 1];
if (value === 'json' || value === 'ndjson') return value;
if (value === 'json' || value === 'ndjson' || value === 'json-summary' || value === 'ndjson-summary') return value as 'json' | 'ndjson' | 'json-summary' | 'ndjson-summary';
return 'human';
}
if (arg.startsWith('--format=')) {
const value = arg.slice('--format='.length);
if (value === 'json' || value === 'ndjson') return value;
if (value === 'json' || value === 'ndjson' || value === 'json-summary' || value === 'ndjson-summary') return value as 'json' | 'ndjson' | 'json-summary' | 'ndjson-summary';
return 'human';
}
}
@@ -277,6 +279,7 @@ export async function main(argv: string[] = process.argv.slice(2)): Promise<numb
case 'qualify':
cmd.option('--profile <name>', 'Profile name from config');
cmd.option('--seed <number>', 'Deterministic seed');
cmd.option('--changed', 'Filter to git-modified routes');
break;
case 'replay':
cmd.option('--artifact <path>', 'Path to failure artifact');
@@ -375,7 +378,7 @@ export async function main(argv: string[] = process.argv.slice(2)): Promise<numb
init: new Set(['--preset', '-p', '--force', '-f', '--noninteractive']),
verify: new Set(['--profile', '--routes', '--seed', '--changed', '--workspace']),
observe: new Set(['--profile', '--check-config', '--workspace']),
qualify: new Set(['--profile', '--seed', '--workspace']),
qualify: new Set(['--profile', '--seed', '--workspace', '--changed']),
replay: new Set(['--artifact', '--route']),
doctor: new Set(['--mode', '--strict', '--workspace']),
migrate: new Set(['--check', '--dry-run', '--write']),
+1
View File
@@ -297,6 +297,7 @@ export interface Artifact {
total: number;
passed: number;
failed: number;
skipped?: number;
};
executionSummary?: ExecutionSummary;
coverageBreakdown?: {