Destructive operations
TL;DR
- A destructive operation on Pipelogic is any verb that removes platform state — deleting a component, a backend, a file, or a node. Each one runs on a fetch-and-acknowledge contract: the caller first reads the affected row, then explicitly acknowledges the removal, and only then does the resource go away.
- The contract is the same across surfaces: App, REST API,
pplCLI. No surface offers a path that skips the acknowledgement; the check lives in the platform contract, not in a particular client. - The acknowledgement shape depends on the resource. Two-step for component delete in agent mode (the first call returns the full row in a confirmation envelope; the second call adds
--yesto commit). One-step for backend delete in agent mode (the calling identity is the consent).--yesup front for file and node delete (the row was already inspected during the fetch). - Updates are not destructive.
updateverbs apply partial fields, do not take--yes, and do not need acknowledgement — the worst case is the wrong field changed, which another update corrects, not the resource is gone. - Promoting a prerelease into the
releasedcatalog is the recommended hand-off to a human reviewer: agent flows surface the candidate prerelease, and a person runs the promote. The hand-off matters because promotion moves the public contract — what other backends pin, whatlatestanddefaultresolve to.
Why a fetch-and-acknowledge contract exists
The fetch-and-acknowledge contract puts the read before the removal: the caller sees what will be removed while it still exists, rather than discovering it after the fact. The check is part of the contract itself, not an after-the-fact message.
Every destructive verb has a step where the caller looks at the affected resource — either by running a separate get first, or by reading the row carried in a confirmation envelope returned from the first call. That step is the gate, not a presentation detail. Proceeding through it without reading takes more effort than reading does, so the cautious path is the default one.
The same gate applies to every caller. A human pressing the App's delete button sees the row in a confirmation dialog. A caller running ppl component delete in agent mode reads the confirmation envelope, decides, and re-issues the call with --yes. For the deletes that take --yes up front, the read happened during the preceding fetch. No surface lets a caller skip the read.
Mental model
┌──────────────────────────────────────────────────────────┐
│ 1. FETCH ROW │
│ list / get / versions │
│ (or first-call confirmation envelope) │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 2. READ ROW │
│ caller inspects: id, workspace, scope, │
│ version, tags, dependents │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 3. ACKNOWLEDGE │
│ destructive command with the contract │
│ the resource requires │
└──────────────────────────────────────────────────────────┘
│
▼
resource gone
Every destructive call goes through these three steps. The fetch can happen explicitly (a separate list / get call) or implicitly (the first agent-mode component delete call returns the row in a confirmation envelope); either way, the caller has seen the row before committing.
The acknowledgement shapes
Use this section to know which acknowledgement applies before reaching for a verb.
Two-step (component delete in agent mode). The first call returns a success envelope whose data carries action: "component.delete.confirmation_required" and the full component row (or version row for a single-version delete). The row is in the envelope. Inspect, decide, then re-run the same call with --yes to commit. This shape applies because component deletion cascades — versions, releases, dependents — so the contract surfaces what the cascade affects before the caller agrees to it. Agent component deletion also requires the component to be empty: delete the ephemeral releases first, then the now-empty component.
One-step (backend delete in agent mode). The calling identity is treated as explicit consent: agents that have already inspected the backend via get do not need a separate --yes. Backend deletes are frequent during ephemeral test work, so a two-step contract would add round-trips without adding safety; the identity gate is what makes the one-step shape sound.
--yes up front (file delete, node delete). The row was inspected during the preceding fetch (file list --query=… or node list --query=…). The contract treats that fetch as the read, and the --yes is the explicit acknowledgement that it confirmed the right target. This shape applies to resources whose delete behavior is local and self-contained; the flag prevents a delete from a stale id without requiring a separate envelope round-trip.
The shapes differ because the contract matches what each operation does. The platform applies the lightest contract that still prevents the failure mode the operation can produce.
What is not destructive
Updates are not gated. ppl component update, ppl file update, and ppl backend update apply the fields you pass and leave everything else alone. The worst case is the wrong field changed, which another update corrects; the worst case for delete is the resource is gone, which it does not. The platform treats them as different operations and gives them different contracts.
Listing, inspecting, fetching, deploying, redeploying, undeploying, and every read-only verb are also not gated. Tearing down a deployment is reversible — redeploy from the same backend. The principle is consistent: gate the irreversible removals, leave the reversible operations without acknowledgement.
Hand off promotion to a human
Promoting a prerelease into the released catalog (ppl component promote) moves the public contract — what other backends will pin, what latest and default resolve to. The recommended flow treats this as a human decision: an automated flow builds and validates the prerelease, and a person makes the commit to the released channel.
Agent flows that lead up to promotion stop at the safe step and surface the candidate — the prerelease id and the release branch. A person picks up that candidate, reviews the proof loop that produced it, and runs the promote. That hand-off is what keeps a change to the public contract a deliberate human decision.
See Release semantics for the full discussion.
How the contract composes
The fetch-and-acknowledge pattern composes with the rest of the platform's safeguards. A team cleaning up ephemeral test backends uses leases — the lease's commit or rollback is itself the acknowledgement, scoped to exactly the resources the lease owns. A team experimenting with backend graph mutations relies on the operation log — destructive graph operations are undoable as long as the backend itself is not deleted. A team writing automation relies on the destructive-operation contracts exposed in agent mode — the envelope shapes make whether an operation is destructive and needs acknowledgement machine-decidable.
The contract guarantees that a delete cannot happen on a single call without a read, on any surface and for any caller. That property lets automation drive the platform without each caller reinventing its own delete-confirmation pattern.
Where this fits
Destructive operations are where the platform's removal contracts apply most directly. The fetch-and-acknowledge contract, the per-resource acknowledgement shapes, and the human hand-off on promotion together make a removal a two-part action: read the row, then acknowledge. Each destructive operation costs one extra read or flag, and in return deleting the wrong resource by accident is hard to do.
Related
- Agent-mode output — the envelope shapes the contracts use.
- Release semantics — the human hand-off on promotion.
- The lease lifecycle — scoped cleanup that respects the same safety opinions.
- Backend operations — undoable graph mutations under the operation log.
- Test with a live backend — clean teardown via
backend undeployand thenbackend delete.