/** * S3: Init command scaffold templates * Each preset returns a config object and file contents for the init command. */ import type { ApophisConfig, PresetDefinition, ProfileDefinition, EnvironmentPolicy } from '../../../core/types.js'; export interface ScaffoldResult { config: ApophisConfig; readmeContent: string; } // ───────────────────────────────────────────────────────────────────────────── // safe-ci: Minimal CI-safe preset (default) // ───────────────────────────────────────────────────────────────────────────── export function safeCiScaffold(): ScaffoldResult { const preset: PresetDefinition = { name: 'safe-ci', timeout: 5000, parallel: false, chaos: false, observe: false, }; const profile: ProfileDefinition = { name: 'quick', mode: 'verify', preset: 'safe-ci', routes: ['POST /users'], }; const envLocal: EnvironmentPolicy = { name: 'local', allowVerify: true, allowObserve: true, allowQualify: false, allowChaos: false, allowBlocking: true, requireSink: false, }; const config: ApophisConfig = { mode: 'verify', profiles: { quick: profile }, presets: { 'safe-ci': preset }, environments: { local: envLocal }, // Uncomment to enforce cross-cutting behavioral rules across all matching routes: // pluginContracts: { // 'auth-presence': { // appliesTo: '/api/**', // hooks: { onRequest: { requires: ['request_headers(this).authorization != null'] } }, // }, // }, }; const readmeContent = ` # APOPHIS Setup — safe-ci preset This project was scaffolded with \`apophis init --preset safe-ci\`. ## Quick Start 1. Ensure you have a Fastify app with @fastify/swagger registered. 2. Add behavioral contracts to your route schemas using \`x-ensures\`. 3. Run: apophis verify --profile quick ## What This Preset Does - Runs only behavioral contracts (not schema-only routes). - No chaos, no observe, no stateful testing. - Safe for CI pipelines. - Timeout: 5s per route. ## Example Behavioral Contract Add this inside your route schema to check that a created resource is retrievable: \`\`\`javascript "x-ensures": [ "response_code(GET /users/{response_body(this).id}) == 200" ] \`\`\` If \`apophis verify\` says "No behavioral contracts found", it means your routes have schemas but no \`x-ensures\` or \`x-requires\` clauses. Add at least one clause per route you want to verify. ## Next Steps - Add more routes to the \`routes\` array in your profile. - Try \`apophis init --preset platform-observe\` for production readiness. - Try \`apophis init --preset protocol-lab\` for multi-step flows. `; return { config, readmeContent }; } // ───────────────────────────────────────────────────────────────────────────── // platform-observe: Production-ready with observe mode // ───────────────────────────────────────────────────────────────────────────── export function platformObserveScaffold(): ScaffoldResult { const preset: PresetDefinition = { name: 'platform-observe', timeout: 10000, parallel: true, chaos: false, observe: true, sampling: 0.1, blocking: false, sinks: { logs: true, metrics: true }, }; const profile: ProfileDefinition = { name: 'staging-observe', mode: 'observe', preset: 'platform-observe', routes: [], }; const envStaging: EnvironmentPolicy = { name: 'staging', allowVerify: true, allowObserve: true, allowQualify: true, allowChaos: false, allowBlocking: false, requireSink: true, sinks: { logs: true, metrics: true }, }; const envProduction: EnvironmentPolicy = { name: 'production', allowVerify: true, allowObserve: true, allowQualify: false, allowChaos: false, allowBlocking: false, requireSink: true, }; const config: ApophisConfig = { mode: 'observe', profile: 'staging-observe', profiles: { 'staging-observe': profile }, presets: { 'platform-observe': preset }, environments: { staging: envStaging, production: envProduction, }, // pluginContracts: { // 'request-id': { // appliesTo: '/api/**', // hooks: { onSend: { ensures: ['response_headers(this).x-request-id != null'] } }, // }, // }, }; const readmeContent = ` # APOPHIS Setup — platform-observe preset This project was scaffolded with \`apophis init --preset platform-observe\`. ## Quick Start 1. Ensure you have a Fastify app with @fastify/swagger registered. 2. Configure your reporting sink (see environments.staging.requireSink). 3. Run: apophis observe --profile staging-observe ## What This Preset Does - Enables observe mode for production readiness checks. - Validates non-blocking semantics and sink configuration. - Parallel execution for faster feedback. - Requires sink config in staging/production. ## Safety - Observe mode is non-blocking by default. - Production requires explicit policy to enable blocking. - Chaos is disabled in this preset. ## Next Steps - Add a sink configuration to your environment policy. - Run \`apophis doctor\` to validate the full setup. `; return { config, readmeContent }; } // ───────────────────────────────────────────────────────────────────────────── // llm-safe: Minimal preset for LLM-generated codebases // ───────────────────────────────────────────────────────────────────────────── export function llmSafeScaffold(): ScaffoldResult { const preset: PresetDefinition = { name: 'llm-safe', timeout: 3000, parallel: false, chaos: false, observe: false, }; const profile: ProfileDefinition = { name: 'llm-check', mode: 'verify', preset: 'llm-safe', routes: [], }; const envLocal: EnvironmentPolicy = { name: 'local', allowVerify: true, allowObserve: false, allowQualify: false, allowChaos: false, allowBlocking: false, requireSink: false, }; const config: ApophisConfig = { mode: 'verify', profile: 'llm-check', profiles: { 'llm-check': profile }, presets: { 'llm-safe': preset }, environments: { local: envLocal }, // pluginContracts: { // 'auth-presence': { // appliesTo: '/api/**', // hooks: { onRequest: { requires: ['request_headers(this).authorization != null'] } }, // }, // }, }; const readmeContent = ` # APOPHIS Setup — llm-safe preset This project was scaffolded with \`apophis init --preset llm-safe\`. ## Quick Start 1. Ensure you have a Fastify app with @fastify/swagger registered. 2. Add behavioral contracts to your route schemas using \`x-ensures\`. 3. Run: apophis verify --profile llm-check ## What This Preset Does - Ultra-minimal preset for LLM-generated codebases. - 3s timeout per route (fast feedback). - No observe, no qualify, no chaos — verify only. - Conservative defaults to avoid surprising failures. ## Example Behavioral Contract Add this inside your route schema to check that a created resource is retrievable: \`\`\`javascript "x-ensures": [ "response_code(GET /users/{response_body(this).id}) == 200" ] \`\`\` If \`apophis verify\` says "No behavioral contracts found", it means your routes have schemas but no \`x-ensures\` or \`x-requires\` clauses. Add at least one clause per route you want to verify. ## Next Steps - Add routes to the \`routes\` array once you have behavioral contracts. - Run \`apophis doctor\` to check for missing dependencies. `; return { config, readmeContent }; } // ───────────────────────────────────────────────────────────────────────────── // protocol-lab: Multi-step flow and stateful testing // ───────────────────────────────────────────────────────────────────────────── export function protocolLabScaffold(): ScaffoldResult { const preset: PresetDefinition = { name: 'protocol-lab', timeout: 15000, parallel: false, chaos: true, observe: false, // chaosStrategy: 'sample', // 'one' | 'all' | 'sample' | 'routes' // chaosSampleSize: 3, // routes to target when strategy is 'sample' // chaosSampleRoutes: [], // explicit route list when strategy is 'routes' }; const profile: ProfileDefinition = { name: 'oauth-nightly', mode: 'qualify', preset: 'protocol-lab', routes: [], seed: 42, }; const envLocal: EnvironmentPolicy = { name: 'local', allowVerify: true, allowObserve: true, allowQualify: true, allowChaos: true, allowBlocking: true, requireSink: false, }; const envTest: EnvironmentPolicy = { name: 'test', allowVerify: true, allowObserve: true, allowQualify: true, allowChaos: true, allowBlocking: true, requireSink: false, }; const config: ApophisConfig = { mode: 'qualify', profile: 'oauth-nightly', profiles: { 'oauth-nightly': profile }, presets: { 'protocol-lab': preset }, environments: { local: envLocal, test: envTest, }, // pluginContracts: { // 'rate-limit': { // appliesTo: 'POST /api/**', // hooks: { onResponse: { ensures: ['status != 429'] } }, // }, // }, // scenarios: [{ // name: 'create-and-read', // steps: [ // { request: { method: 'POST', url: '/users', body: { name: 'test' } }, expect: ['status:201'], capture: { userId: 'response_body(this).id' } }, // { request: { method: 'GET', url: '/users/{userId}' }, expect: ['status:200', 'response_body(this).name == "test"'] }, // ], // }], }; const readmeContent = ` # APOPHIS Setup — protocol-lab preset This project was scaffolded with \`apophis init --preset protocol-lab\`. ## Quick Start 1. Ensure you have a Fastify app with @fastify/swagger registered. 2. Define multi-step flows in your route schemas. 3. Run: apophis qualify --profile oauth-nightly --seed 42 ## What This Preset Does - Enables qualify mode for stateful and scenario testing. - Chaos engineering enabled (local/test only). - Deep depth for thorough exploration. - 15s timeout per route. ## Safety - Chaos is blocked in production by default. - Use \`apophis doctor\` to validate environment safety before qualifying. ## Machine Output in CI Qualify can produce large output. In CI, use machine-readable formats and filter events: - \`--format json\` emits a single stable JSON artifact (good for small-to-medium runs). - \`--format ndjson\` emits one event per line (good for streaming parsers). - Use \`--quiet\` to suppress human progress text. - Pipe ndjson to \`jq\` or a custom filter to extract only failures: \`\`\`bash apophis qualify --profile oauth-nightly --format ndjson | jq 'select(.type == "route.failed")' \`\`\` - For very large runs, consider writing artifacts to a directory and parsing the JSON file instead of stdout: \`\`\`bash apophis qualify --profile oauth-nightly --format json --artifact-dir reports/apophis \`\`\` ## Next Steps - Define scenario sequences in your config. - Example scenario is commented out in apophis.config.js — uncomment and adapt. - Add route allowlists for chaos if needed. - Run \`apophis replay --artifact \` to debug failures. `; return { config, readmeContent }; } // ───────────────────────────────────────────────────────────────────────────── // Preset registry // ───────────────────────────────────────────────────────────────────────────── export const PRESETS: Record ScaffoldResult> = { 'safe-ci': safeCiScaffold, 'platform-observe': platformObserveScaffold, 'llm-safe': llmSafeScaffold, 'protocol-lab': protocolLabScaffold, }; export function getPresetNames(): string[] { return Object.keys(PRESETS); } export function getScaffoldForPreset(preset: string): ScaffoldResult | null { const fn = PRESETS[preset]; return fn ? fn() : null; }