Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cmd/cloudctl/README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,13 @@ cloudctl -config <path> residual get --tenant-id <tenant> --intent-id <intent>

Each `list` item, actual residual ledger rows from `get`, and the resource output when present expose persisted `stop_evidence` as `stopEvidence`; the resource is returned only when no intent is selected. Without evidence the field is an empty object `{}`; legacy D8 list items without a ledger also return `{}`, while a synthetic legacy D8 residual in `get` may omit the field. An existing unconfirmed-stop record retains `kind=stop_unconfirmed`, `source=cloud`, `observedAt`, and the original-identity `executionId`. This field is display-only: it grants no termination, deletion, or ledger-clearance authority and does not change reclamation responsibility.

Termination and data deletion are separate commands, each requiring a reason, idempotency key, and affirmative confirmation. `terminate` requires at least a Run or Workspace; all additional IDs must identify the same original resource. D8 also requires the original failed delete `--operation-id`. Project/tenant selectors cannot authorize batch writes.
Termination and data deletion are separate commands, each requiring a reason, idempotency key, and affirmative confirmation. `terminate` requires at least a Run or Workspace; all additional IDs must identify the same original resource. D8 with a historical deletion requires the original failed delete `--operation-id`. Project/tenant selectors cannot authorize batch writes.

A D8 residual that reached `done/workspace_unavailable` before any deletion was created additionally requires explicit `--allow-missing-delete-operation`, both exact `--run-id` and `--workspace-id`, and no `--operation-id`. It cannot select a live Run, an absent delivery conclusion, any deletion history, open admission, or an existing residual ledger. The intent records `legacyDeleteOperationMissing=true` without inventing an operation; the default command still rejects this variant. Termination, fencing, and separately confirmed data deletion retain their evidence requirements.

```sh
cloudctl -config <path> residual terminate --tenant-id <tenant> --run-id <run> --workspace-id <workspace> --reason 'operator abandoned execution' --key <terminate-key> --impact-confirmed
cloudctl -config <path> residual terminate --tenant-id <tenant> --run-id <run> --workspace-id <workspace> --allow-missing-delete-operation --reason 'verified recovery copies retained' --key <missing-delete-key> --impact-confirmed
cloudctl -config <path> residual delete-data --tenant-id <tenant> --intent-id <root-intent> --reason 'delete retained data' --key <new-delete-key> --data-delete-confirmed
```

Expand Down
5 changes: 4 additions & 1 deletion cmd/cloudctl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,13 @@ cloudctl -config <path> residual get --tenant-id <tenant> --intent-id <intent>

`list` 的每个条目、`get` 的实际 residual 账本行,以及存在时的资源输出都通过 `stopEvidence` 展示持久化的 `stop_evidence`;资源仅在未选择意图时返回。没有证据时输出空对象 `{}`;没有账本的历史 D8 列表条目也输出 `{}`,但 `get` 中合成的历史 D8 residual 可能省略该字段。已有停止未确认记录会保留 `kind=stop_unconfirmed`、`source=cloud`、`observedAt` 和原身份的 `executionId`。此字段仅供查看,不授予终止、删除或清账权限,也不改变回收责任。

终止和删数据是两次独立命令,各自需要 reason、幂等 key 和真实确认。`terminate` 至少指定 Run 或 Workspace;提供的其他 ID 必须属于同一个原资源。D8 还必须提供原 failed delete 的 `--operation-id`。Project/tenant 不能作为批量写入范围。
终止和删数据是两次独立命令,各自需要 reason、幂等 key 和真实确认。`terminate` 至少指定 Run 或 Workspace;提供的其他 ID 必须属于同一个原资源。D8 有历史删除操作时必须提供原 failed delete 的 `--operation-id`。Project/tenant 不能作为批量写入范围。

对已按 D8 达到 `done/workspace_unavailable`、却从未创建任何删除操作的残留,必须额外显式选择 `--allow-missing-delete-operation`,同时提供精确的 `--run-id` 和 `--workspace-id`,且不提供 `--operation-id`。此选择不能用于仍在运行、没有交付结论、有删除历史、admission 开放或已有残留账本的资源。意图记录 `legacyDeleteOperationMissing=true`,不补造历史 operation;默认命令仍拒绝这一变体。后续终止、围栏和单独删数据的证据要求相同。

```sh
cloudctl -config <path> residual terminate --tenant-id <tenant> --run-id <run> --workspace-id <workspace> --reason 'operator abandoned execution' --key <terminate-key> --impact-confirmed
cloudctl -config <path> residual terminate --tenant-id <tenant> --run-id <run> --workspace-id <workspace> --allow-missing-delete-operation --reason 'verified recovery copies retained' --key <missing-delete-key> --impact-confirmed
cloudctl -config <path> residual delete-data --tenant-id <tenant> --intent-id <root-intent> --reason 'delete retained data' --key <new-delete-key> --data-delete-confirmed
```

Expand Down
6 changes: 5 additions & 1 deletion cmd/cloudctl/residual.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func parseResidual(args []string) (core.ResidualManagementRequest, error) {
flags.IntVar(&request.Limit, "limit", 100, "page size")
flags.StringVar(&request.After, "after", "", "page cursor")
flags.BoolVar(&request.ImpactConfirmed, "impact-confirmed", false, "confirm termination impact")
flags.BoolVar(&request.AllowMissingDeleteOperation, "allow-missing-delete-operation", false, "explicitly select a D8 Run with no historical delete operation")
flags.BoolVar(&request.DataDeleteConfirmed, "data-delete-confirmed", false, "confirm permanent data deletion")
flags.BoolVar(&request.ResponsibilityTransferred, "responsibility-transferred", false, "accept remaining external responsibility")
flags.StringVar(&request.EvidenceNote, "evidence-note", "", "external disposition explanation")
Expand Down Expand Up @@ -74,6 +75,9 @@ func parseResidual(args []string) (core.ResidualManagementRequest, error) {
if (request.RunID == "" && request.WorkspaceID == "") || !request.ImpactConfirmed {
return request, invalidArguments()
}
if request.AllowMissingDeleteOperation && (request.RunID == "" || request.WorkspaceID == "" || request.OperationID != "") {
return request, invalidArguments()
}
case "delete-data":
request.Action = core.ResidualDeleteData
if request.IntentID == "" || !request.DataDeleteConfirmed {
Expand Down Expand Up @@ -101,7 +105,7 @@ func residualFlags(action string) map[string]bool {
case "get":
names = []string{"project-id", "run-id", "workspace-id", "operation-id", "reclaim-id", "intent-id"}
case "terminate":
names = []string{"project-id", "run-id", "workspace-id", "operation-id", "reason", "key", "operator-label", "impact-confirmed"}
names = []string{"project-id", "run-id", "workspace-id", "operation-id", "reason", "key", "operator-label", "impact-confirmed", "allow-missing-delete-operation"}
case "delete-data":
names = []string{"intent-id", "reason", "key", "operator-label", "data-delete-confirmed"}
case "confirm-external":
Expand Down
7 changes: 7 additions & 0 deletions cmd/cloudctl/residual_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ func TestCloudctlResidualManagement(t *testing.T) {
args: []string{"delete-data", "--tenant-id", "tenant", "--intent-id", "root", "--reason", "delete retained data", "--key", "delete-key", "--data-delete-confirmed=true"},
want: core.ResidualManagementRequest{Action: core.ResidualDeleteData, TenantID: "tenant", IntentID: "root", Reason: "delete retained data", Key: "delete-key", DataDeleteConfirmed: true, Limit: 100, ExternalFacts: map[string]string{}},
},
{
name: "explicit D8 without a historical delete",
args: []string{"terminate", "--tenant-id", "tenant", "--run-id", "run", "--workspace-id", "workspace", "--allow-missing-delete-operation", "--reason", "verified recovery copies", "--key", "missing-delete", "--impact-confirmed"},
want: core.ResidualManagementRequest{Action: core.ResidualTerminate, TenantID: "tenant", RunID: "run", WorkspaceID: "workspace", AllowMissingDeleteOperation: true, Reason: "verified recovery copies", Key: "missing-delete", ImpactConfirmed: true, Limit: 100, ExternalFacts: map[string]string{}},
},
{
name: "external facts preserve exact references",
args: []string{"confirm-external", "--tenant-id", "tenant", "--intent-id", "unfinished", "--reason", "external disposal", "--key", "external-key", "--evidence-note", "host decommissioned", "--external-fact", "sandbox:original=ticket=123", "--external-fact", "data:workspace=ticket-456", "--responsibility-transferred"},
Expand Down Expand Up @@ -98,6 +103,8 @@ func TestCloudctlResidualRejectsUnsafeOrAmbiguousArguments(t *testing.T) {
terminate,
appendCopy(terminate, "--impact-confirmed=false"),
appendCopy(terminate, "--impact-confirmed", "--data-delete-confirmed"),
appendCopy(terminate, "--impact-confirmed", "--allow-missing-delete-operation"),
appendCopy(terminate, "--impact-confirmed", "--run-id", "run", "--allow-missing-delete-operation", "--operation-id", "old-delete"),
{"terminate", "--tenant-id", "tenant", "--project-id", "project", "--reason", "reason", "--key", "key", "--impact-confirmed"},
{"terminate", "--tenant-id", "tenant", "--run-id", "run", "--reason", " ", "--key", "key", "--impact-confirmed"},
{"terminate", "--tenant-id", "tenant", "--run-id", "run", "--reason", "reason", "--key", " ", "--impact-confirmed"},
Expand Down
9 changes: 7 additions & 2 deletions docs/agent-runs/residual-reclamation.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Workspaces continue to return 404.
| Command | Scope and required confirmation |
|---|---|
| `list` / `get` | Tenant is required; intersect Project, Run, Workspace, operation or reclaim filters; details expose only operational state, targets, audit and evidence |
| `terminate` | One exact Run/Workspace, reason, idempotency key and explicit impact confirmation; D8 also requires the original failed delete operation; retains data by default |
| `terminate` | One exact Run/Workspace, reason, idempotency key and explicit impact confirmation; D8 requires its original failed deletion or explicit missing-delete selection; retains data by default |
| `delete-data` | One root intent with successful physical termination and retained disposition, separate reason/key and data-deletion confirmation; no bare-Workspace deletion |
| `confirm-external` | One existing unfinished root or dependent intent, reason/key, explanation, external fact references for every unfinished target and explicit acceptance of remaining responsibility |

Expand Down Expand Up @@ -122,11 +122,16 @@ It transfers responsibility for an existing intent and stops new permits. It doe
permit same-volume reuse or authorize data deletion. Missing intents, succeeded intents, empty explanations and missing targets are rejected.
Late physical facts remain evidence; they cannot silently convert external disposition into system-proven success.

D8 takeover requires the original done Run, retained Workspace binding and failed/node_unavailable delete operation; it never backfills the D4/D5
D8 with a historical deletion requires the original done Run, retained Workspace binding and failed/node_unavailable delete operation; it never backfills the D4/D5
ledger. Without a ledger, the root owns targets, evidence, conflicts and the new delete-operation binding. The old failed operation, Run terminal
and settlement remain unchanged. Ordinary scans do not create controlled D8 responsibility. Missing ledger rows or retained disposition cannot
bypass admission, Project-operation or data-reuse guards.

When D8 finished before a deletion was declared, the deployment operator must select the variant with exact Run/Workspace IDs and explicit
`--allow-missing-delete-operation`. Only an original `done/workspace_unavailable` Run with a delivery conclusion, no deletion history,
the retained binding and closed admission qualifies. Root and dependent deletion intents retain `legacyDeleteOperationMissing` provenance.
The default command does not expand admission, invent an original operation, or rewrite the business outcome.

## Existing fact intake and conflicts

Cloud uses existing `runtime_ack`, creation-fact, execution-result and Revision intake. This step adds no RPC, proto or Controller discovery
Expand Down
7 changes: 5 additions & 2 deletions docs/agent-runs/residual-reclamation.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Workspace 软删除并写 `cleared/released`,不改 Run 的 phase/status/resul
| 命令 | 范围与必要确认 |
|---|---|
| `list` / `get` | tenant 必填;查询可按 Project、Run、Workspace、operation 或 reclaim 相交过滤,详情只返回运维所需状态、目标、审计与证据 |
| `terminate` | 一个精确 Run/Workspace,reason、幂等键和显式影响确认;D8 还需原 failed delete operation 身份;默认保留数据 |
| `terminate` | 一个精确 Run/Workspace,reason、幂等键和显式影响确认;D8 需原 failed delete operation 身份或明确的无删除记录选择;默认保留数据 |
| `delete-data` | 一个已真实终止成功且 retained 的根意图,独立 reason、幂等键和数据删除确认;不能按裸 Workspace 发起 |
| `confirm-external` | 一个已存在且未完成的根或从属意图,reason、幂等键、说明、每个未完成目标的外部事实引用及承担剩余责任的确认 |

Expand Down Expand Up @@ -102,7 +102,10 @@ Workspace 软删除并写 `cleared/released`,不改 Run 的 phase/status/resul
停止该责任的新许可,不伪造 `terminated`、`lateEnsureFenced` 或 `removed`,也不据此开放同卷复用或授权删卷。
没有意图、意图已 succeeded、说明为空或漏目标均拒绝;迟到物理事实继续留存,不能静默把外部处置改成系统证明。

D8 只按原 done Run、保留的 Workspace 绑定及 failed/node_unavailable delete operation 接管,不回填 D4/D5 账本。
D8 有原删除操作时,按原 done Run、保留的 Workspace 绑定及 failed/node_unavailable delete operation 接管,不回填 D4/D5 账本。
若 D8 在删除声明建立前结束,部署运维须以精确 Run/Workspace 和显式 `--allow-missing-delete-operation` 选择无删除操作变体。
仅原 `done/workspace_unavailable`、已有交付结论、没有任何删除历史、保留原绑定且 admission 关闭的资源可受理;意图与从属删除保留
`legacyDeleteOperationMissing` 来源标记。默认命令不扩展准入,不补造原 operation,原业务结论仍不变。
无账本时根意图持有目标、证据、冲突和删除 operation 绑定;原 failed operation、Run 终态与结算保持不变。
普通扫描不会自动为 D8 创建受控责任;无账本或已 retained 也不能绕过 admission、Project 操作和数据复用保护。

Expand Down
Loading
Loading