Files
apophis-fastify/docs/llm-safe-adoption.md
T

4.7 KiB

LLM-Safe Adoption

APOPHIS is designed to be safe and predictable for LLM-generated Fastify services.

It applies the invariant-driven approach from Invariant-Driven Automated Testing (Malhado Ribeiro, 2021) to LLM-assisted development: constrained vocabulary, deterministic replay, and executable contracts give coding agents a verifiable loop between generated changes and behavioral correctness.

Why APOPHIS Is Good for LLM-Generated Services

Coding agents benefit from:

  • Constrained vocabulary: Small set of CLI commands and config options
  • Official scaffolds: Tested templates that produce valid config
  • Policy guards: CI catches unsafe modes and malformed setup
  • Deterministic output: Fixed seed, config, schemas, and deterministic handlers produce repeatable output
  • Behavioral contracts: Agents write x-ensures clauses, APOPHIS verifies them

Official Scaffolds

Use apophis init with a preset:

Preset Use Case
safe-ci Minimal CI-safe preset (default)
llm-safe Minimal preset for LLM-generated codebases
platform-observe Production-ready with observe mode
protocol-lab Multi-step flow and stateful testing
apophis init --preset llm-safe

apophis doctor Checks

Run apophis doctor to validate your setup:

  • Dependencies: Checks for fastify, @fastify/swagger
  • Config validation: Rejects unknown keys, unsafe modes
  • Route discovery: Confirms routes are discoverable
  • Safety checks: Blocks qualify in production, missing sinks
  • Docs drift: Validates examples in CI mode
apophis doctor

CI Policy Guards

Add these checks to your CI pipeline:

name: APOPHIS Checks

on: [push, pull_request]

jobs:
  apophis:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npx apophis doctor
      - run: npx apophis verify --profile ci --changed

Template Examples

Minimal LLM-Safe Config

// apophis.config.js
export default {
  mode: 'verify',
  profile: 'llm-check',
  profiles: {
    'llm-check': {
      name: 'llm-check',
      mode: 'verify',
      preset: 'llm-safe',
      routes: []
    }
  },
  presets: {
    'llm-safe': {
      name: 'llm-safe',
      timeout: 3000,
      parallel: false,
      chaos: false,
      observe: false
    }
  },
  environments: {
    local: {
      name: 'local',
      allowVerify: true,
      allowObserve: false,
      allowQualify: false,
      allowChaos: false,
      allowBlocking: false,
      requireSink: false
    }
  }
};

Route Template with Behavioral Contract

import crypto from 'crypto';

app.post('/users', {
  schema: {
    'x-category': 'constructor',
    'x-ensures': [
      // BEHAVIORAL: Creating a user must make it retrievable
      'response_code(GET /users/{response_body(this).id}) == 200'
    ],
    body: {
      type: 'object',
      properties: {
        name: { type: 'string', minLength: 1 }
      },
      required: ['name']
    },
    response: {
      201: {
        type: 'object',
        properties: {
          id: { type: 'string' },
          name: { type: 'string' }
        }
      }
    }
  }
}, async (request, reply) => {
  const { name } = request.body;
  const id = `usr-${crypto.createHash('sha256').update(name).digest('hex').slice(0, 8)}`;
  reply.status(201);
  return { id, name };
});

CI Policy Guard Script

// scripts/apophis-ci-guard.js
import { execSync } from 'node:child_process';

// Run doctor
const doctorResult = execSync('npx apophis doctor', { encoding: 'utf-8' });
console.log(doctorResult);

// Run verify
const verifyResult = execSync('npx apophis verify --profile ci --changed', { encoding: 'utf-8' });
console.log(verifyResult);

Best Practices

  1. Start with presets: Avoid raw manual config until the project needs explicit overrides.
  2. Run doctor first: Catch setup issues before running verify.
  3. Use --changed in CI: Only verify routes that changed in the PR.
  4. Commit config: Store apophis.config.js in version control.
  5. Pin versions: Pin apophis-fastify version in package.json.

Troubleshooting

"Unknown config key"

APOPHIS rejects unknown keys to prevent hallucinated config. Check the key name against the config schema.

"Qualify blocked in production"

Qualify mode is blocked in production by default. Use a non-production environment or explicitly allow it in your environment policy.

"Missing sink config"

Observe mode requires a sink config in staging/production. Add requireSink: true to your environment policy and configure a sink.