Experiments & Contracts

Base URL http://localhost:3333/api/ · Authentication · Conventions

Experiment Routes

Base path: /api/experiments

Manages A/B testing experiments, evaluation datasets, and benchmarks for the evolution system.

Create Eval Dataset

POST /datasets

  • Authentication: Required
  • Description: Create an evaluation dataset (manual or synthetic)
  • Body:
{
  "name": "Dataset Name",
  "skillId": "skill-id",
  "category": "general",
  "source": "manual|synthetic|history|golden",
  "items": [
    {
      "input": "Test input",
      "expectedOutput": "Expected output",
      "metadata": {}
    }
  ],
  "splitConfig": {
    "train": 0.7,
    "test": 0.2,
    "validation": 0.1
  }
}
  • Response:
{
  "success": true,
  "datasetId": "dataset-uuid"
}

List Datasets

GET /datasets

  • Authentication: Required
  • Parameters:
    • skillId (query, optional): Filter by skill
    • category (query, optional): Filter by category
  • Response:
{
  "success": true,
  "datasets": [
    {
      "id": "dataset-id",
      "name": "Dataset Name",
      "skillId": "skill-id",
      "category": "general",
      "source": "manual",
      "itemCount": 100,
      "createdAt": "2024-01-01T00:00:00Z"
    }
  ]
}

Generate Dataset

POST /datasets/generate

  • Authentication: Required
  • Description: Auto-generate a dataset from goal history, golden standards, or synthetically
  • Body:
{
  "skillId": "skill-id",
  "source": "history|golden|synthetic",
  "category": "general",
  "provider": "openai",
  "model": "gpt-4"
}
  • Response:
{
  "success": true,
  "datasetId": "dataset-uuid"
}

Get Dataset with Splits

GET /datasets/:id

  • Authentication: Required
  • Parameters:
    • id (path): Dataset ID
  • Response:
{
  "success": true,
  "dataset": {
    "id": "dataset-id",
    "name": "Dataset Name",
    "items": []
  },
  "splits": {
    "train": [],
    "test": [],
    "validation": []
  }
}

Delete Dataset

DELETE /datasets/:id

  • Authentication: Required
  • Parameters:
    • id (path): Dataset ID
  • Response:
{
  "success": true
}

Get Benchmarks

GET /benchmarks

  • Authentication: Required
  • Description: List golden standard benchmarks available for experiments
  • Response:
{
  "success": true,
  "benchmarks": [
    {
      "id": "benchmark-id",
      "name": "Benchmark Name",
      "sourceGoalId": "goal-id",
      "createdAt": "2024-01-01T00:00:00Z"
    }
  ]
}

Create Experiment

POST /

  • Authentication: Required
  • Body:
{
  "name": "Experiment Name",
  "hypothesis": "Skill v2 will outperform v1 on accuracy",
  "type": "ab_test|benchmark|regression",
  "sourceGoalId": "goal-id",
  "benchmarkId": "benchmark-id",
  "skillId": "skill-id",
  "evalDatasetId": "dataset-id",
  "config": {}
}
  • Response:
{
  "success": true,
  "experiment": {
    "id": "experiment-id",
    "name": "Experiment Name",
    "status": "created",
    "createdAt": "2024-01-01T00:00:00Z"
  }
}

List Experiments

GET /

  • Authentication: Required
  • Parameters:
    • status (query, optional): Filter by status
    • limit (query, optional): Max results (default: 50)
  • Response:
{
  "success": true,
  "experiments": [
    {
      "id": "experiment-id",
      "name": "Experiment Name",
      "status": "created|running|completed|failed",
      "type": "ab_test",
      "createdAt": "2024-01-01T00:00:00Z"
    }
  ]
}

Get Experiment with Results

GET /:id

  • Authentication: Required
  • Parameters:
    • id (path): Experiment ID
  • Response:
{
  "success": true,
  "experiment": {
    "id": "experiment-id",
    "name": "Experiment Name",
    "status": "completed",
    "results": {},
    "createdAt": "2024-01-01T00:00:00Z"
  }
}

Run Experiment

POST /:id/run

  • Authentication: Required
  • Parameters:
    • id (path): Experiment ID
  • Body (optional):
{
  "provider": "openai",
  "model": "gpt-4"
}
  • Description: Fire-and-forget experiment execution. Returns immediately while the experiment runs in the background.
  • Response:
{
  "success": true,
  "message": "Experiment run started"
}

Delete Experiment

DELETE /:id

  • Authentication: Required
  • Parameters:
    • id (path): Experiment ID
  • Response:
{
  "success": true
}

Get Experiment Runs

GET /:id/runs

  • Authentication: Required
  • Parameters:
    • id (path): Experiment ID
  • Description: Get all run results for an experiment
  • Response:
{
  "success": true,
  "runs": [
    {
      "id": "run-id",
      "experimentId": "experiment-id",
      "status": "completed",
      "results": {},
      "startedAt": "2024-01-01T00:00:00Z",
      "completedAt": "2024-01-01T00:05:00Z"
    }
  ]
}

Contract Routes

Base path: /api/contracts

Contracts are refinement-type runtime invariants (PRD-091 Layer 5). They are either authored by the user/agent or mined by InsightEngine from successful executions ("output must be JSON", "step count ≤ 5", "response includes citation block"). At runtime, ContractsService.check evaluates whether evidence satisfies the contract; violations are counted on the contract row and feed into FitnessScoreService (contract cleanliness component).

Contract shape

{
  "id": "uuid",
  "user_id": "user-uuid",
  "target_type": "tool|workflow|skill|agent",
  "target_id": "target-uuid",
  "name": "Output must be valid JSON",
  "predicate": { "type": "json_valid", "field": "output" },
  "source": "authored|mined",
  "status": "active|disabled|deprecated",
  "confidence": 0.92,
  "evidence_count": 47,
  "violation_count": 2,
  "created_at": "...",
  "updated_at": "..."
}

List Contracts

GET /

  • Authentication: Required
  • Parameters:
    • status (query, optional): active, disabled, deprecated
    • targetType (query, optional): tool, workflow, skill, agent
  • Response:
{ "success": true, "contracts": [ { ... } ] }

Create Contract

POST /

  • Authentication: Required
  • Body:
{
  "targetType": "tool",
  "targetId": "web-search",
  "name": "Output must include source URLs",
  "predicate": { "type": "regex", "field": "output", "pattern": "https?://" },
  "confidence": 0.9
}
  • Description: Authors a new contract. targetType and targetId scope the contract to a specific asset; predicate is a JSON shape interpreted by ContractsService.check. Errors return 400 if required fields are missing.
  • Response (201):
{ "success": true, "contract": { ... } }

Get Single Contract

GET /:id

  • Authentication: Required
  • Response:
{ "success": true, "contract": { ... } }
  • Errors: 404 not found, 403 forbidden

Get Contract Violations

GET /:id/violations

  • Authentication: Required
  • Description: Returns the violation history for a contract — useful when surfacing why a canary-check flagged regression.
  • Response:
{
  "success": true,
  "violations": [
    {
      "id": "v-uuid",
      "contract_id": "c-uuid",
      "source_execution_id": "exec-uuid",
      "details": { "expected": "...", "actual": "..." },
      "created_at": "..."
    }
  ]
}

Update Contract Status

PATCH /:id

  • Authentication: Required
  • Body:
{ "status": "disabled" }
  • Description: Currently only status is mutable. Use this to disable a noisy contract without deleting it.
  • Response:
{ "success": true, "contract": { ... } }

Check Evidence Against Active Contracts

POST /check

  • Authentication: Required
  • Body:
{
  "targetType": "tool",
  "targetId": "web-search",
  "runtimeState": { "output": "..." },
  "sourceExecutionId": "exec-uuid"
}
  • Description: Evaluates runtimeState against every active contract for (targetType, targetId). Records evidence on each contract; persists a violation row if a predicate fails. Returns per-contract verdicts.
  • Response:
{
  "success": true,
  "checked": 3,
  "passed": 2,
  "failed": 1,
  "verdicts": [
    { "contractId": "...", "name": "...", "passed": true },
    { "contractId": "...", "name": "...", "passed": false, "violationId": "..." }
  ]
}

Delete Contract

DELETE /:id

  • Authentication: Required
  • Response:
{ "success": true, "deleted": true }

Mutation History Routes

Base path: /api/mutations

Every router-applied change is recorded here (PRD-091 Layer 7). Each row captures the before-state snapshot and the fitness_before baseline so a regression can trigger non-lossy revert. This is the audit trail the agent should consult when the user asks "what did AGNT change?" or "undo that change."

Mutation shape

{
  "id": "uuid",
  "user_id": "user-uuid",
  "insight_id": "insight-uuid|null",
  "target_type": "agent|skill|workflow|tool|memory",
  "target_id": "target-uuid",
  "operation": "apply|revert",
  "status": "applied|reverted|failed",
  "before_snapshot": { "...": "...": "the asset as it was before the change" },
  "after_snapshot": { "...": "...": "the asset as written" },
  "fitness_before": 0.82,
  "fitness_after": 0.78,
  "fitness_delta": -0.04,
  "reverted_reason": "manual|canary_regression|null",
  "created_at": "...",
  "reverted_at": "..."
}

List Mutations

GET /

  • Authentication: Required
  • Parameters:
    • status (query, optional): applied, reverted, failed
    • targetType (query, optional): agent, skill, workflow, tool, memory
    • limit (query, optional): default 200
  • Description: Returns the mutation history for the authenticated user, newest first.
  • Response:
{ "success": true, "history": [ { ... } ] }

Get Single Mutation

GET /:id

  • Authentication: Required
  • Description: Full row including before_snapshot and after_snapshot. Use this to render a diff or to surface what changed.
  • Response:
{ "success": true, "mutation": { ... } }
  • Errors: 404 not found, 403 forbidden

Canary Check (Detect Regression)

POST /:id/canary-check

  • Authentication: Required
  • Description: Re-scores fitness for the mutated asset right now (using FitnessScoreService.forTool / forWorkflow) and compares against the stored fitness_before baseline. Persists fitness_after and fitness_delta on the row. The agent should call this before suggesting revert.
  • Response:
{
  "success": true,
  "verdict": {
    "regression": true,
    "delta": -0.12,
    "fitnessAfter": 0.70
  }
}
  • Verdict shape:
    • regression: true when delta < -0.05 (default minDelta)
    • regression: false, reason: 'not_applicable' when mutation isn't in applied status
    • regression: false, reason: 'no_baseline' when fitness_before was never captured
    • regression: false, reason: 'no_after_score' when there's no recent execution data to score against

Revert Mutation

POST /:id/revert

  • Authentication: Required
  • Body (optional):
{ "reason": "manual" }
  • Description: Marks the mutation row as reverted with reverted_reason. The actual rollback uses the before_snapshot to restore the asset (handled by the model). Safe to call only when canary-check confirms regression OR the user explicitly requests it.
  • Response:
{ "success": true }
  • Default reason: "manual" when the user triggers revert; the periodic canary sweep uses "canary_regression".

The AGNT briefing

Stay in the loop.

New agents, workflows and field notes. Sign up for AGNT updates.

By subscribing, you agree to receive AGNT updates. Unsubscribe any time. Privacy policy.