Skip to content

Commit a0a80c5

Browse files
MajorTalclaude
andcommitted
fix(delete): rename archive_project → delete_project; correct semantics
DELETE /projects/v1/:id triggers an immediate, irreversible destructive cascade (drop schema, delete Lambdas, release subdomains, tombstone mailbox, etc.) — not a reactivatable grace window. v1.32.0 conflated it with the automatic lease-expiry state machine, and the misleading "grace window would have reactivated" success text + "Soft-delete" description created a real data-loss-risk bug for agents reading the tool metadata. This commit: - Renames the MCP tool archive_project → delete_project to match the HTTP verb and the CLI's `projects delete` subcommand (all three interfaces are now consistently named). - Rewrites the tool description to state immediate, irreversible destructive cascade, list the cascade effects, and explicitly contrast with the automatic lease-expiry grace path (which is still handled by errors.ts 402 extraction from v1.32.0). - Rewrites the success message to match (no reactivation claim). - Corrects CLI `projects delete` help text. - Updates README, sync.test.ts SURFACE, mcp-integration.test.ts import, and openspec/specs/project-lifecycle-surface/spec.md. Breaking for MCP clients that have `archive_project` cached — they'll need to pick up `delete_project`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3c6a2ac commit a0a80c5

7 files changed

Lines changed: 20 additions & 21 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ npx run402-mcp
6161
| `list_versions` | List published versions of a project. |
6262
| `get_quote` | Get tier pricing. Free, no auth required. |
6363
| `set_tier` | Subscribe, renew, or upgrade tier. Auto-detects action. Handles x402 payment. |
64-
| `archive_project` | Archive a project and remove from local key store. |
64+
| `delete_project` | Immediately and irreversibly delete a project (cascade purge) and remove from local key store. |
6565
| `check_balance` | Check billing account balance for an agent allowance address. |
6666
| `list_projects` | List all active projects for an agent allowance address. |
6767
| `allowance_status` | Check local agent allowance status — address, network, funding. |

cli/lib/projects.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Subcommands:
1818
usage <id> Show compute/storage usage for a project
1919
schema <id> Inspect the database schema
2020
rls <id> <template> <tables_json> Apply Row-Level Security policies
21-
delete <id> Soft-delete a project (enters ~104-day grace; renew to reactivate) and remove it from local state
21+
delete <id> Immediately and irreversibly delete a project (cascade purge) and remove from local state
2222
pin <id> Pin a project (prevents expiry/GC)
2323
promote-user <id> <email> Promote a user to project_admin role
2424
demote-user <id> <email> Demote a user from project_admin role

mcp-integration.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,8 @@ describe("MCP integration (live API, no mocks)", { timeout: 180_000 }, () => {
254254
});
255255

256256
it("cleanup — delete project", async () => {
257-
const { handleArchiveProject } = await import("./src/tools/archive-project.js");
258-
const result = await handleArchiveProject({ project_id: projectId });
257+
const { handleDeleteProject } = await import("./src/tools/delete-project.js");
258+
const result = await handleDeleteProject({ project_id: projectId });
259259
const out = text(result);
260260
assert.equal(result.isError, undefined, `Expected no error, got: ${out}`);
261261
});

openspec/specs/project-lifecycle-surface/spec.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,19 @@ When `POST /subdomains/v1` (or equivalent subdomain-claim route) returns `HTTP 4
2323
- **WHEN** a subdomain-claim tool receives `HTTP 409` with body `{ "message": "Subdomain reserved", "hint": "Name held for original owner during grace period" }`
2424
- **THEN** the tool result includes the 409 status, the message and hint, and next-step text mentioning the reservation rather than reusing the 403 lease-expired guidance
2525

26-
### Requirement: archive_project tool text reflects grace-period vocabulary
26+
### Requirement: delete_project tool name and text match the API
2727

28-
The `archive_project` MCP tool description and success message, and the corresponding CLI subcommand output, SHALL use the new lifecycle vocabulary (purge / grace window) rather than the obsolete "archived" wording. The tool name, CLI subcommand name, HTTP method, and endpoint path SHALL remain unchanged.
28+
The MCP tool that calls `DELETE /projects/v1/:id` SHALL be named `delete_project` (matching the HTTP method) and its description SHALL state that the call triggers an immediate, irreversible cascade purge (drop schema, delete Lambdas, release subdomains, tombstone mailbox, etc.) and SHALL distinguish that explicit purge from the automatic lease-expiry grace state machine. The CLI subcommand SHALL remain `run402 projects delete <id>` and use equivalent wording.
2929

30-
#### Scenario: Successful archive
30+
#### Scenario: Successful delete
3131

32-
- **WHEN** the agent invokes `archive_project` on a live project and the API returns 200
33-
- **THEN** the tool returns success text that uses "purged" (or equivalent purge/grace vocabulary) and does not describe the project as "archived"
32+
- **WHEN** the agent invokes `delete_project` on a live project and the API returns 200
33+
- **THEN** the tool returns success text that names the action as a delete/purge, lists the cascade effects, and states that the action is irreversible
3434

3535
#### Scenario: Tool description advertised via MCP list
3636

3737
- **WHEN** an MCP client lists available tools
38-
- **THEN** the description for `archive_project` reflects the soft-delete lifecycle (grace window → purge) and does not claim the project becomes "archived" after 7 days
38+
- **THEN** the description for `delete_project` describes immediate destructive cascade, explicitly contrasts it with the automatic lease-expiry grace path, and does not claim the project enters a reactivatable grace window
3939

4040
### Requirement: SKILL.md runtime sections document the grace state machine
4141

src/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import { deleteSecretSchema, handleDeleteSecret } from "./tools/delete-secret.js
4343

4444
// New tools — subdomains & projects
4545
import { listSubdomainsSchema, handleListSubdomains } from "./tools/list-subdomains.js";
46-
import { archiveProjectSchema, handleArchiveProject } from "./tools/archive-project.js";
46+
import { deleteProjectSchema, handleDeleteProject } from "./tools/delete-project.js";
4747
import { pinProjectSchema, handlePinProject } from "./tools/pin-project.js";
4848

4949
// New tools — user role management
@@ -395,10 +395,10 @@ server.tool(
395395
);
396396

397397
server.tool(
398-
"archive_project",
399-
"Soft-delete a project: the gateway moves it into the grace-period state machine (active → past_due → frozen → dormant → purged, ~104 days) and this tool removes it from the local key store. End-user data plane keeps serving during grace; renewing the tier before purge reactivates it. After purge the action cannot be undone.",
400-
archiveProjectSchema,
401-
async (args) => handleArchiveProject(args),
398+
"delete_project",
399+
"Immediately and irreversibly delete a project: the gateway runs the full destructive cascade (drop tenant schema, delete Lambda functions, release subdomains, tombstone mailbox, remove sender domain, wipe secrets and app versions) and sets status=purged. This tool also removes the project from the local key store. Distinct from the automatic lease-expiry grace window — this action is the explicit purge and cannot be undone. To recover from a missed renewal use `set_tier` instead.",
400+
deleteProjectSchema,
401+
async (args) => handleDeleteProject(args),
402402
);
403403

404404
// ─── Admin tools ─────────────────────────────────────────────────────────────
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { apiRequest } from "../client.js";
33
import { getProject, loadKeyStore, saveKeyStore } from "../keystore.js";
44
import { formatApiError, projectNotFound } from "../errors.js";
55

6-
export const archiveProjectSchema = {
6+
export const deleteProjectSchema = {
77
project_id: z
88
.string()
9-
.describe("The project ID to soft-delete (enter the grace window)"),
9+
.describe("The project ID to delete (irreversible cascade purge)"),
1010
};
1111

12-
export async function handleArchiveProject(args: {
12+
export async function handleDeleteProject(args: {
1313
project_id: string;
1414
}): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
1515
const project = getProject(args.project_id);
@@ -24,7 +24,6 @@ export async function handleArchiveProject(args: {
2424

2525
if (!res.ok) return formatApiError(res, "deleting project");
2626

27-
// Remove from local key store
2827
const store = loadKeyStore();
2928
delete store.projects[args.project_id];
3029
saveKeyStore(store);
@@ -33,7 +32,7 @@ export async function handleArchiveProject(args: {
3332
content: [
3433
{
3534
type: "text",
36-
text: `Project \`${args.project_id}\` entered the soft-delete state (status: purged) and was removed from the local key store. Renewing the tier during the grace window would have reactivated it.`,
35+
text: `Project \`${args.project_id}\` deleted (status: purged). Schema dropped, functions deleted, subdomains released, mailbox tombstoned. Removed from local key store. This action is irreversible.`,
3736
},
3837
],
3938
};

sync.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ const SURFACE: Capability[] = [
143143
{ id: "get_quote", endpoint: "POST /projects/v1/quote", mcp: "get_quote", cli: "projects:quote", openclaw: "projects:quote" },
144144
{ id: "provision", endpoint: "POST /projects/v1", mcp: "provision_postgres_project", cli: "projects:provision", openclaw: "projects:provision" },
145145
{ id: "set_tier", endpoint: "POST /tiers/v1/:tier", mcp: "set_tier", cli: "tier:set", openclaw: "tier:set" },
146-
{ id: "archive", endpoint: "DELETE /projects/v1/:id", mcp: "archive_project", cli: "projects:delete", openclaw: "projects:delete" },
146+
{ id: "delete", endpoint: "DELETE /projects/v1/:id", mcp: "delete_project", cli: "projects:delete", openclaw: "projects:delete" },
147147

148148
// ── Faucet ───────────────────────────────────────────────────────────────
149149
{ id: "faucet", endpoint: "POST /faucet/v1", mcp: "request_faucet", cli: "allowance:fund", openclaw: "allowance:fund" },

0 commit comments

Comments
 (0)