Agents that can change — through proof

mdflow v4 · proposal-first evolution

Evolve turns a reported problem into a reviewable prompt proposal. It does not let a timing signal rewrite a live agent, and it does not call an equal green score a fix.

md feedback → reviewed eval → md evolve plan → private proposal → exact verification → review → explicit apply or reject.

The moat is not self-editing. It is a provider-neutral change protocol where evidence, capabilities, cost, proof, consent, lineage, and rollback are all inspectable.

The contract

Evolve makes ten promises:

  1. The resolved maintainer, maximum flow invocations, and write surface appear before paid work starts.
  2. Quick reruns and metadata-only failures cannot authorize source mutation.
  3. A proposal cannot silently add command, network, provider, or file access.
  4. Current and proposal verification never use the canonical flow as a test fixture.
  5. Proof is bound to exact content and execution configuration.
  6. Apply and rollback are atomic, locked, hash-guarded transactions.
  7. Rejection, timeout, and infrastructure failure leave evidence open.
  8. “Verified improvement” requires feedback-specific red/green proof.
  9. Decisions have durable states, reason codes, artifacts, and event journals.
  10. Automatic policy creates proposals only. Source changes require review and an explicit apply command.

One problem, one stable ID

$ md feedback flows/review.md "missed the renamed-file regression"
Feedback fb_01J… saved for flows/review.md

Status: saved, not yet proved
Next: md feedback distill fb_01J…
Plan: md evolve plan flows/review.md

Feedback is private, bounded, and durable. Each item moves through open → targeted → resolved | dismissed; it is never consumed by a wall-clock watermark. md feedback list, show, dismiss, and reopen make that state visible; md feedback forget <id> --yes explicitly removes one item and its status history from private storage.

md evolve prune --days 30 removes older unapplied attempts and completed job logs after confirmation, while retaining applied rollback lineage.

A quick rerun is ambiguous: maybe the user changed input, compared providers, or pressed Enter twice. When enabled, mdflow records it only as a low-confidence observation and asks for explicit feedback. It does not spend or mutate.

Distill drafts a test, not truth

$ md feedback distill fb_01J…
Draft eval case: ~/.mdflow/evolution/drafts/fb_01J….eval-case.ts

This is an untrusted, deliberately failing draft.
Review its assertion before copying it into the suite.

Eval files are executable TypeScript. Generated code cannot quietly become the policy that judges future agents, so the draft lives outside the repository and fails on purpose until a human defines an observable assertion. A reviewed case links back with evidence: ["fb_01J…"].

Plan before spending

$ md evolve plan flows/review.md
proposal ready to plan: suite and actionable feedback are present
cost: at most 7 flow invocations: 1 proposal + 3 current + 3 proposal
writes: private evolution artifact only; source remains unchanged
maintainer: claude/opus (isolated)

plan is free. It statically inspects the suite without importing executable top-level code, and the arithmetic includes repeated eval trials. Non-interactive paid work refuses without --yes; interactive confirmation defaults to No. Machine consumers can use one --json result or streaming --events NDJSON.

The policy can cap invocations, proposals per day, and cooldown. propose mode prints this plan and queues private background work after explicit actionable feedback. Legacy evolve: auto maps to proposal-only behavior. There is no unattended apply.

The live flow never becomes the fixture

Each proposal gets a private evr_... receipt directory containing its plan, evidence membership, immutable current and proposal files, prompt/capability diffs, current/proposal results, decision, and append-only events.

Current and proposal run from separate repository snapshots. Tracked and untracked non-ignored files are copied off-path; symlinks that escape the repository are refused. No .pending.md, scratch gate ledger, or half-tested candidate appears beside a runnable flow.

These workspaces are isolation fixtures, not host sandboxes. Eval modules and engines still have whatever filesystem, network, credential, and process access the current user grants them. Timed-out process groups receive TERM and then KILL, but users must still review executable suites.

Capability changes are a separate axis

Freezing YAML is not enough: a prompt body can contain inline commands, executable fences, remote URLs, context providers, globs, and file imports. Evolve computes a capability manifest before candidate execution. New capabilities park the proposal as capability_rejected unless a policy explicitly permits the private experiment; they can never produce an automatic source edit.

Proof is a receipt, not a timestamp

A content receipt hashes:

Change any relevant input and the old receipt becomes stale. Moving a checkout does not invalidate equivalent content merely because an absolute path changed. Automatic comparative proof accepts either a clean current receipt or a stable receipt whose failures are all linked to the targeted feedback; unrelated current failures block the run.

Unknown nonzero exits fail unless a case explicitly sets allowNonZero: true. Recognized provider, authentication, environment, cancellation, and timeout failures are inconclusive. Repeated stochastic cases report every trial; mixed pass/fail results are flaky and cannot mint clean proof, even when their numeric quorum passes.

Four honest outcomes

Outcome What it means
Verified improvement A feedback-linked case failed on current, passed on proposal, and every proposal guardrail passed without flake.
Regression-safe proposal Guardrails passed, but no case proved the reported problem red/green. Human review only.
Rejected The proposal regressed declared behavior. Evidence remains open.
Inconclusive Timeout, infrastructure uncertainty, interruption, or flake prevented a trustworthy result. Evidence remains open.

Equal scores can justify a reviewable proposal. They cannot justify the word “fixed.”

Review, apply, recover

$ md evolve show evr_01J…
Status: verified_improvement
Capabilities: no additions
Invocations: 7/7

diff --git current.md proposal.md
…

$ md evolve apply evr_01J…
Status: applied

$ md evolve rollback evr_01J…
Status: rolled_back

show exposes the decision, capability delta, planned/actual invocations, and prompt diff. Apply takes a per-flow lock and compare-and-swaps against the exact base hash; a human edit made after proposal creation wins. Persistence uses a same-directory temporary file, fsync, and rename. Rollback is guarded against overwriting newer work, and interrupted transaction states recover from content hashes rather than backup filenames.

Policy without magic

evolve:
  mode: propose             # off | observe | suggest | propose | apply
  triggers: [explicit-feedback, classified-failure]
  maintainer:
    engine: claude
    model: opus
    isolated: true
    timeout-ms: 180000
  budget:
    max-invocations: 9
    max-per-day: 2
    cooldown-ms: 86400000
  gate:
    require-feedback-eval: true
    allow-capability-delta: false
    repetitions: 1
  apply: review

Use --no-evolve or MDFLOW_EVOLVE=off as an immediate escape hatch. Workflow runs keep the same feedback affordances, but proposal generation explicitly refuses until step-level attribution can identify the responsible agent node. md evolve prune --days 30 --yes removes eligible old private proposals and completed job logs while retaining applied lineage.

The creed

If a guardrail is not covered by an eval, it is a wish.

If feedback is not covered by a red/green case, it is not a proved fix.

Agents may change—but only through evidence, capability accounting, exact receipts, explicit consent, atomic application, and rollback.


→ mdflow.dev · → GitHub · → normative Evolve docs

Read as Markdown · Docs · About · Contact · Privacy · For agents