The Modern App Manifesto

Twelve principles for building apps that people and agents use side by side.

Introduction

A modern app serves people and the agents working with them. Its web UI, API, CLI, TUI, SDKs, MCP server and in-app assistant expose one set of capabilities. Documentation written for both readers helps people and agents understand those capabilities and use them well.

When something fails, the app tells the person what happened and gives their authorized agents the evidence to help. The experience continues through diagnosis, recovery and verification of the result.

The twelve-factor app made software legible to a platform. Tools that knew nothing about its code could build, deploy and scale an app that followed it. Software now has a second audience. Scripts, coding agents and in-app assistants operate apps next to people. An interface that hides its actions, state and outcomes leaves them guessing, and it slows people down too.

Several of these principles were good practice before agents existed, and agents make them mandatory.

Three terms recur. An automated client is any program that uses the app. An agent is an automated client that chooses and carries out steps. The assistant is the app's built-in interface for working with an agent. When this page says the assistant acts, the agent behind that interface acts.

A modern app is one set of capabilities across many interfaces:

The interfaces of a modern app
InterfaceWhat it makes easy
Web UIVisual exploration, forms and live status
APIDirect integration with other software
CLIScripting, CI and operations
TUIInteractive work in a terminal
SDKsIntegration in the developer's own language
MCP serverTool discovery over a standard protocol
AssistantUnderstanding a task and getting help to finish it
DocsLearning, for people and agents alike

Every interface serves people and agents alike. A person can use the CLI, and an agent can drive the web UI. This extends API-first design: the API is the first interface, not the only one an agent can use (II). Docs are the one interface that runs no operation, so a rule about running an operation binds the other interfaces.

A modern app ships all of these interfaces. If you leave one out, say why, and keep the contract general enough to add it later.

Who should read this

Read this if you build or run an app, whether it is a service, a utility or a platform. The principles do not depend on a language, a framework or a model provider. MCP appears as a concrete tool protocol. The principle behind it is interoperability, and any equivalent protocol serves.

Relationship to twelve-factor

The twelve-factor methodology applies to service components. This manifesto extends it with expectations for how people and automated clients use those services. The table below shows where a principle extends a factor. Not every principle maps to a factor. The extensions are proposals. They are not claims about what the original factors prescribed.

How the principles extend the twelve factors
Original foundationExtension in this manifesto
Dependencies; build, release, runDeclare each model provider as an explicit adapter, and treat a provider swap as a release.
Config; backing servicesConfigure supported model services, credential access and task budgets.
Processes; disposabilityPersist long-running task progress outside transient workers.
Dev/prod parityVersion prompts, tool definitions, schemas and retrieval settings with each release. Exercise representative tools, permissions, model configurations and failure cases.
LogsConnect operations to evidence that authorized people and agents can retrieve.

The twelve principles

I. One contract, many interfaces

Define each capability once, with the same meaning in every interface.

The twelve-factor app has one codebase. The modern app adds a boundary: a machine-readable contract that describes what the app can do. An OpenAPI document, a schema or an equivalent description records operations, inputs, results, permissions and errors. The contract adds to the codebase principle and does not replace it.

A large app may keep several related, versioned contracts. A task-oriented tool may compose several operations, if it uses the same application layer and obeys the same authority and recovery rules. Generated and handwritten interfaces both need conformance checks in CI. Independently maintained behavior drifts, and a check turns that drift into a build failure.

II. Every interface for both

People and agents can use every interface, with the same capabilities and the same policy.

API-first design builds the API before the UI, so the UI has no private access. This principle goes one step further. No interface belongs to people alone, and none belongs to agents alone. The commitment has three parts:

Equal capability does not mean identical gestures or identical disclosure. A chart, a table and a JSON response can carry the same information. Credentials show the difference. Any interface can start key creation or rotation, and the raw secret reaches a person only through an authorized path (X).

The app owes people and agents usable, documented capabilities and observable outcomes. It cannot guarantee that every model succeeds at every task, so measure success with representative agents (XII). Test the interactive interfaces themselves, because an API test does not show that the web UI or TUI works for an agent. A web UI uses semantic, labelled elements and stable identifiers. A TUI works completely from the keyboard and offers a structured view of its screen state.

The limit on an agent is policy, not missing capability. An agent acts under an accountable identity and never exceeds that identity's authority (VII). Approval, consent and grants of authority are capabilities like any other, and the delegation rules in VII govern them.

III. Self-description

Every interface describes what an operation does, what it needs and how it ends.

CLIs offer --json and machine-readable help. APIs publish schemas for requests, results and errors. Tools declare typed inputs and outputs. Pagination, rate limits and versions appear in responses, not only in the prose docs.

Data shapes are not enough. Describe prerequisites, side effects and the authority an operation requires. Say whether completion is immediate or asynchronous, and how a person or an automated client checks the result. An unavailable action explains what it needs. A valid call can still be the wrong operation for the user's goal.

People and programs read different channels. Progress, color and diagnostics go to stderr, and data goes to stdout. A CLI turns off animation when stdout is not a terminal, and turns off color when NO_COLOR is set to a non-empty value. Document exit codes and where structured errors appear.

IV. Docs for two readers

Keep one accurate body of documentation that helps people and agents discover, finish and troubleshoot real tasks.

Good docs lead with the task, show a working example and explain the reasoning. They cover permissions, error references, limits, compatibility and migrations. People and agents need the same facts. Reasoning helps agents, and searchable text, stable links and runnable examples help people.

Docs live in the repository and ship with the version they describe. Prose, schemas, examples and the app stay aligned. Test every executable example in CI, and label illustrative fragments as such.

Publish a markdown copy of each page. Publish an llms.txt index for discovery and an llms-full.txt file containing the full documentation in markdown. Generate the full-text editions from the same source as the pages people read. Check in CI that the editions stay aligned and the index links resolve. These files are required interfaces for agents; their presence alone does not prove documentation quality.

V. Errors carry a path forward

The app tells the person what happened and gives authorized agents the evidence to help.

Apps often treat an error as a string for a person to read. The user sees "Something went wrong" and the details go to a log nobody opens. A modern app records one structured failure for each failed operation. It delivers that record to the user, to the authorized agents involved in the task and to the logs.

The same error schema travels in each transport's own envelope. That means the API's error body, the CLI's --json error and the MCP tool's error result. An agent outside the app gets the evidence without the assistant in the middle (I, III).

{
  "code": "deploy.image_pull_failed",
  "message": "We could not pull the image for this deploy.",
  "operation_id": "op_4821",
  "failure_stage": "pull",
  "outcome": "no_change",
  "retry": { "safe": true, "needs": "a valid registry credential" },
  "cause": "registry returned 401 for ghcr.io/acme/web:1.4.2",
  "remediation": "Check the registry credential for this project.",
  "trace_id": "01JB3Z8K2M",
  "docs": "https://docs.example.com/errors/deploy.image_pull_failed",
  "evidence": "/operations/op_4821/evidence"
}

The record says what was attempted, where it failed and whether the outcome is known. A failure before any change differs from a lost response with an unknown outcome. Larger diagnostic bundles stay behind scoped references, such as evidence above. Add a trace ID when one exists.

The app keeps the failure with its task. Under the user's sharing policy, it notifies the assistant or attaches the record when help begins. This works even if the assistant panel was closed at the time of failure. Repeated reports of one failure update its recovery status. They do not start competing repairs. Recovery progress stays visible to the user.

Everything in the record is evidence, not instruction. The cause above is a registry response, and an attacker can shape text like that. Saying so does not enforce anything. The app checks every proposed action against the recorded grant (VII), whatever the assistant concludes.

Sharing debug data has a cost, so treat it as a product decision. The app builds the record from data the user may access and may share. Those are separate permissions. For its own assistant, the app sends the record only to a model endpoint the operator chose (XI). For an external agent, the app controls which agent is authorized and what evidence it releases. It cannot control what that agent does next, so the sharing policy states that limit. Secrets never enter the record (X).

VI. Assistance in context

Help appears where people work, with explicit context, and they stay in control.

Users get help without leaving the page, finding the docs and translating them back into the UI. The in-app assistant reads the docs and calls the app's tools. It explains, fills in forms and performs multi-step tasks on request.

The app gives the assistant explicit, current task context: the workspace, the selected resource, relevant unsaved input and prior attempts. Page visibility alone does not establish intent or permission. Before changing anything, the assistant names the target and checks its state. The user can inspect and correct that understanding. External agents get task context through the app's interfaces and operate on the same identified task.

People can act directly, work with an assistant or delegate a task. The app keeps progress visible and lets them change how they work without losing the task. Tasks that do not need a model stay usable through direct controls during an outage or at a budget limit.

The assistant is an interface like the others. It uses the contract (I), serves people and agents like every interface (II) and runs under the rules in VII. It is never a privileged back door.

A recovery, start to finish

  1. A deployment fails. The UI keeps the failure visible with a clear explanation and a next step.
  2. The app makes the record available to the authorized assistant, now or when the panel opens.
  3. The assistant reads the operation, the target, configuration references and redacted evidence. It treats the 401 as an observed rejection and an expired credential as a hypothesis.
  4. The assistant explains the evidence and proposes a repair. The user reconnects the credential through the normal secure path, or the assistant acts within its authority.
  5. Recovery coordinates with other people and agents and honors the retry rules. Two agents that receive the failure do not repair it twice.
  6. The app checks the deployment and reports success, partial completion or the remaining blocker. A successful tool call alone does not show that the task finished.

Recovery preserves the user's intended outcome. If a private image cannot be pulled, deploying a different public image is a different task. The assistant explains any change to the outcome and obtains authorization. The app judges success against the agreed goal.

The assistant cannot repair every failure automatically. When recovery cannot finish, the assistant preserves the task and says what remains unresolved. It hands off the evidence and the steps already tried.

The same promise covers frontend failures. When a form rejects input, the user sees the field error. The assistant receives permitted field context and validation codes, and the user's unsaved work stays intact. A rejected form field is not a failed operation, so it needs no permanent record.

VII. Delegated authority

Every agent acts under an accountable identity, with explicit authority, revocable access and a record of its actions.

The identity is a person or an explicitly authorized service identity. The app records the initiating authority and the executing agent. That supports scheduled organizational work without impersonating a person or tying a job to a departed employee's account. Verifying an identity differs from deciding what that identity may authorize.

Agents make mistakes, and the content they read can mislead them. Treat everything an agent reads as untrusted input: tool results, logs, documents and error causes. The app limits the damage. Tokens carry the narrowest scope that does the job and expire. Every action records who asked, which agent acted, what inputs it read and what changed.

Users may delegate execution and approval within explicit limits: which actions, which resources, for how long, and whether the agent may delegate further. The app checks each action against the recorded grant. An action outside the grant pauses and returns to the user for authorization, and no explanation from the agent changes that. Users see what their agents did and revoke access at any time.

The app enforces these checks even when the assistant chooses a poor action. Tool choice and explanations belong in evaluations (XII).

VIII. Reliable operations

Define retries, conflicts, interruption and recovery for every change.

Agents retry. Networks fail and models try the same call twice. Mutating endpoints accept idempotency keys. Commands offer --dry-run and show what they would change. Long jobs report progress and resume where they stopped. This extends twelve-factor IX, disposability.

Irreversible and idempotent are different properties. A payment cannot be cancelled once it settles, yet a key still makes a retried request safe. Confirmation does not prevent duplicates. Irreversible effects need deduplication and a way to reconcile an unknown outcome. Where a change cannot be previewed, say so and require explicit confirmation (VII).

People and agents can change the same resource: a person edits a form while an agent updates its record. Every change checks that its assumptions still hold. When another person or automated client has changed the state, the app reports the conflict and supports a fresh plan. A preview names the state it examined. An old approval does not silently authorize a materially different action.

People see active work, request cancellation and take over. The app reports which effects already happened and what remains in flight. Cancellation does not imply rollback. Where reversal exists, expose it as a separate operation with its own result.

Persist long-running progress outside transient workers. When exact replay is impossible because a model, a document or an outside system changed, the app says so.

IX. Evidence on demand

Authorized people and agents can retrieve the evidence behind an operation.

The twelve-factor app writes logs to stdout and lets the environment collect them. The modern app also lets authorized people and agents ask why an operation failed, such as "why did my run fail at 14:02?" The platform collects and indexes the streams. The app supplies trace IDs, operation IDs and structured fields. Both the in-app assistant and external agents can use them. This extends twelve-factor XI, logs.

Access follows the same rules as everything else. People and automated clients see what they may see, and the app redacts sensitive fields before telemetry leaves the source. Evidence carries timestamps and states its limits, so old, partial or missing telemetry never passes for a complete account.

X. Secrets out of reach

Let agents use credentials without reading them.

This departs from twelve-factor III, which kept credentials in environment variables with the rest of config. An agent that can run env can read them. Here, config means non-secret settings and stays in the environment. Secrets come from a secret manager, and the runtime injects them at the point of use.

Every interface can start credential operations such as creating or rotating a key. The raw value reaches a person only through an authorized path outside model-visible context. Agents receive references and scoped ways to use them.

A secret manager alone does not isolate secrets from an agent. An agent with the same process environment or file access can still read them. Name the trusted execution boundary that holds credentials and exposes scoped operations. Prompts, tool results, error records and transcripts never contain a secret. Redact on the way out and scan on the way in. Assume an agent can repeat anything it reads.

XI. Models are backing services

Declare which models the app supports and run them within explicit budgets.

This extends twelve-factor IV. A model resembles a database or a message queue: the app reaches it through a URL and a credential. Supported providers connect through explicit adapters, and each declares the capabilities the app needs. Switching providers means choosing another supported, validated integration. A swap is a release, so the evaluations in XII gate it, and the app does not promise identical behavior. A fallback model needs defined compatibility like any other provider.

Budgets are task limits on elapsed time, tool calls and model usage. The execution system enforces them outside the model, and retries and delegated work count toward the originating task. At the limit, the app preserves progress and explains what remains. Stopping model calls does not reverse completed operations.

XII. Verify outcomes

Test contracts exactly, evaluate judgment statistically and check that tasks succeed.

Use exact assertions where the answer is exact: arguments, authorization decisions and resulting application state. Use evaluation for judgment and task success, such as tool choice, explanations and recovery. Evaluations pass on thresholds. App controls enforce permissions whatever the model does.

Keep a set of real scenarios, including the failures from V, with user data scrubbed. Run them on every change to prompts, tool definitions, retrieval settings or models. Test the interfaces themselves as well as the assistant's judgment.

This extends twelve-factor X, dev/prod parity. Version prompts, tool definitions, schemas and retrieval settings with the release, and record the effective versions with each run. Pin the model version where the provider allows it.