The Promise None of Them Kept Mark Burgess shipped CFEngine in 1993, formalized promise theory with Siri Fagernes around 2005, and published *In Search of Certainty* in 2015, but four generations of infrastructure tools—Chef, Puppet, Terraform, and Ansible—failed to keep the promise of autonomous agents that observe, reason, and commit. Burgess argues that large language models now supply the missing reasoning layer, enabling a new tool called 'swamp' built for the agent that can. Mark Burgess shipped CFEngine in 1993, formalized promise theory with Siri Fagernes around 2005, and published In Search of Certainty in 2015. The model is precise: autonomous agents observe their environment, reason locally about what they find, and make promises about their own behavior. The agent does not receive instructions from a controller. The agent looks around, decides what to do, and commits to doing it. Every infrastructure tool since has claimed some lineage from this idea. Chef called its resources “convergent.” Puppet called its catalogs “desired state.” Terraform called its plans “declarative.” Ansible called its playbooks “idempotent.” None of those four kept the promise. Burgess’s own tool did, and ran into a different limit. The reason to revisit any of this now is that the missing piece finally shipped, and it arrived from outside infrastructure entirely. LLMs supply the reasoning layer promise theory always assumed: an agent can observe a live system, weigh what it finds against context nobody enumerated in advance, and decide whether to act. Four generations of tooling could not do that at any price. CFEngine came closest and stopped short for reasons worth being precise about, and swamp is built for the agent that can. The promise theory control loop the-promise-theory-control-loop Promise theory starts from three axioms. An agent is autonomous and cannot be coerced. An agent can only promise its own behavior. An agent’s knowledge of the world is local. Compose them and you get a control loop: Observation. The agent perceives its environment directly. Not from a file. Not from a state snapshot someone else produced. The agent looks. Knowledge is local, so no global observer exists to hand the agent a summary, and a promise is assessed by whoever observes it, so an assessment the agent did not make itself buys it nothing. Local reasoning. The agent decides what to do based on what it observed. No central controller dictates the action. The agent’s judgment is sovereign within its domain. Voluntary commitment. The agent promises its own behavior. It does not impose behavior on others. A promise is a commitment you make about yourself, never an obligation you push onto a remote system. An imposition the receiving agent never promised to accept accomplishes nothing. Observe, reason, commit. The loop leaves out most of promise theory, including trust accumulation, cooperative equilibria, and the algebra of conditional promises, and it is still enough to sort the tools. Terraform: diffing files against files terraform-diffing-files-against-files Terraform’s agent the plan / apply cycle operates on two inputs: your .tf files and a state file. The state file is a snapshot of what Terraform last saw, or more precisely, what it last wrote. terraform plan diffs one file against another file. It does not observe your infrastructure. Between applies, Terraform has no awareness. If someone adds a security group rule through the console, Terraform does not detect it until a human runs terraform plan . If a Lambda configuration drifts, Terraform does not converge toward the desired state. It sits inert until invoked. This fails all three properties: Observation: Terraform does not observe. It reads a cached state file that another apply produced. The refresh operation partially mitigates this, but it runs only when a human invokes the tool. Between invocations, Terraform is blind. Solve that with sub-minute reconciliation loops and you have built something like Kubernetes. The state file records Terraform’s own last write, the one piece of evidence an agent assessing its own promise-keeping cannot use. Local reasoning: Terraform does not reason. It diffs two data structures desired vs. last-known and produces a list of API calls. The reasoning was pre-computed by the human who wrote the HCL. Terraform executes; it does not decide. Voluntary commitment: Terraform does not promise its own behavior; it imposes changes on remote resources through API calls. The resources promise nothing back, and Terraform promises nothing about ongoing maintenance. It applies once and stops. Terraform’s actual model is closer to a batch script with a diffing preamble. Undeniably useful, and not what Burgess described. Ansible: imposition from a center ansible-imposition-from-a-center Ansible’s default mode inverts promise theory. A control node connects to targets via SSH and pushes tasks. The target node has no agent, no local reasoning, no capacity to self-assess. It receives instructions and executes them. When the control node is down, nothing converges. Connectivity that fails mid-run leaves operations partial with no mechanism to resume, and state that drifts between playbook runs goes undetected. Against the three properties: Observation: The setup module runs on the target, so the target does observe itself. It has no authority over the result. Facts travel back to the control node, when: conditions and Jinja templates evaluate there, and the decision returns as a task to execute. Observation without decision rights is telemetry rather than perception. Local reasoning: Absent. The target executes tasks in the order received. It does not decide whether the task is relevant, whether the action is necessary given current state, or whether a better path exists. It runs what it was told. Voluntary commitment: Inverted. The control node imposes obligations and the target makes no promise. In Burgess’s terms this is an imposition model rather than a promise model, a distinction the theory is built on. Ansible’s actual model is remote procedure execution with YAML serialization. The “agentless” marketing is technically true no daemon on the target and theoretically catastrophic no autonomous agent means no local reasoning, no convergence, no promises . The honest exceptions the-honest-exceptions Ansible solved a real problem: managing devices where you cannot install an agent. Network switches, locked-down appliances, environments where a persistent daemon is politically or technically impossible. For those use cases, imposition is the only available mode. ansible-pull is the second exception. Run it from cron on the target and you get a local agent that clones a playbook repository, evaluates conditions locally, and converges on a schedule with no controller involved. That is the promise-theoretic mode, shipped in the box, and almost nobody deploys it. So the critique lands on how Ansible is operated rather than on what it can do. Its dominant mode is structurally incompatible with the theory it gets associated with, and that incompatibility costs convergence, verifiability, and autonomy. CFEngine: the one that kept it cfengine-the-one-that-kept-it Burgess built the reference implementation, and it satisfies the loop. A cf-agent runs on every host on a short interval, five minutes by default. It observes local state, decides locally which of its promises are unkept, and repairs those and nothing else. No controller issues orders. A policy server publishes policy and the agent pulls what it has agreed to apply, which is voluntary cooperation in the literal sense Burgess meant. Take the control loop as the test and CFEngine passes on all three counts. Where CFEngine stops is scope and memory. A cf-agent observes what its promises describe. Write a promise about /etc/ssh/sshd config and the agent watches that file. Write nothing about the security group in front of the host and the agent holds no opinion, because the observation is bounded by the policy that requested it. The agent’s assessment is also a verdict rather than a record. It can tell you whether the promise is kept right now. Ask what the config looked like last Tuesday, or how many times this promise has been repaired in the last month, or which hosts drifted together, and there is no queryable answer, because promise-keeping was never stored as data. Trust in promise theory is accumulated assessment. Accumulation needs somewhere to accumulate. Chef and Puppet: the loop inside a declaration chef-and-puppet-the-loop-inside-a-declaration Chef and Puppet inherited the pull-based agent and gave part of it back. A Chef client runs on a schedule every 30 minutes by default , pulls its run list from a server, converges toward the declared state, and reports back. A local agent runs autonomously and decides when to converge. But the agent cannot observe beyond what its recipes describe. A Chef resource checks whether a file exists, whether a package is installed, whether a service is running. It does not survey the broader system and form a judgment about what matters. The observation is scoped to the declaration. If you did not write a resource for it, the agent does not see it. The reasoning is also constrained: the agent converges toward whatever the recipe says. It does not ask “is this recipe still relevant?” or “has the context changed such that this action would cause harm?” It applies the declared state mechanically. These tools approximate promise theory within the boundaries of their declarations, inheriting CFEngine’s scope bound and adding a server dependency to it. Outside those boundaries, they are as blind as Terraform between applies. What the agent actually needs what-the-agent-actually-needs Promise theory describes an agent that can: - Look at a live system and capture what it finds - Compare what it found against what it expected or against what existed last time - Decide whether action is needed based on that comparison - Act only when the decision warrants it - Record what it did and what it observed, versioned over time Steps 1 through 4 are the control loop. Step 5 is what CFEngine left out and what the declarative generation never attempted. The gap is observational capacity and retained assessment rather than syntax or speed. Those tools could not look, so we looked for them, wrote our observations into files, and handed the files to tools that could parse but not perceive. Swamp: observation first, action second swamp-observation-first-action-second A model method runs against a live system: swamp model method run my-account discover all --json This produces typed, versioned, schema-validated data. The agent observed. The observation is stored as an immutable snapshot carrying its provenance: the model name, id, and type, the output spec that produced it, a monotonic version, an isLatest flag, a creation timestamp, and the owning run. When a workflow drove the observation, the workflow run, job, and step names ride along too. The next execution produces a new version. Now you can diff: swamp data query 'modelName == "my-account" && specName == "discovery" && isLatest == true' --json Reality at T compared against reality at T-1. No declaration file in the middle. The agent queries what exists and reasons about whether the delta warrants action. A workflow encodes the decision logic: jobs: - name: observe steps: - name: scan task: type: model method modelIdOrName: my-account methodName: discover all - name: evaluate dependsOn: - job: observe condition: type: succeeded steps: - name: check-drift task: type: model method modelIdOrName: drift-state methodName: compute drift The observation step runs and the evaluation step consumes its output. A downstream step acts when drift exists, and when nothing drifted the workflow terminates without side effects. The decision happens at workflow level rather than inside each resource implementation. How the properties map how-the-properties-map Against the three parts of the loop: Observation. Model methods observe live systems directly. discover all calls AWS APIs. cert check connects to a TLS endpoint. get os info shells out to read the running kernel. The observation is the agent’s own act rather than a file a human pre-computed, and that is what makes the assessment worth anything. Local reasoning. The agent whether human or AI queries versioned data with CEL expressions and makes decisions. “Has this changed since baseline?” is a query. “Should I act?” is a judgment the workflow encodes. The reasoning stays local to the agent’s context: it decides from what it observed rather than from instructions pushed by a controller. Voluntary commitment. Swamp’s remote execution runs on enrollment rather than intrusion. A worker dials out to the orchestrator, presents a scoped token, and advertises what it can do: swamp worker connect ws://orchestrator.internal:4000 \ --token