Repository navigation
Conversation
jo-hnny
left a comment
There was a problem hiding this comment.
Review 基于当前 head 21bac7062b00bdf11e20d0259ca9d04be56cb4e7(base e517de7)。
契约同步本身没有问题:api/ags/v20250920/api.json 与 tccli 902c4bef 结构化比对,三个 Snapshot Action、相关 Object、StartSandboxInstanceRequest 和 metadata 均无差异。但以下问题需要处理后再合入:1 个阻断项、1 个应修复项、1 个小问题。
Findings
1. 阻断:允许只传 --source-instance-id、不传 tool,与公开契约冲突,且没有实测证据
公开契约中,StartSandboxInstanceRequest.ToolId 和 ToolName 的说明都是"与 ToolName/ToolId 至少有一个要填";SourceInstanceId 只写了"待克隆的源沙箱 ID",没有说明它可以替代 tool 选择。PR 在以下位置放宽了这一要求:
internal/commands/instance/create/command.go的validateToolSelection:只有--source-instance-id时也会通过internal/cli/schema.go:instance.create的AnyOf新增了{Required: [SourceInstanceId]}instance create的 help、示例,以及中英文 README:写明克隆时可以省略 tool
此外,"若提供 tool,则必须与源实例一致"和"从源实例磁盘克隆"这两点,公开契约里也没有依据。PR 正文已说明没有运行真实 E2E,所以这些行为目前都未经验证。如果服务端仍要求 tool,用户按 README 中的 agr instance create --source-instance-id ins-source ... 执行会直接失败。
建议二选一:
- 保留原有的"tool 必填"校验和 schema,只把
--source-instance-id作为可选 flag 加入; - 或者补充真实 E2E 或接口负责人确认,作为可以公开的依据,并推动上游修正契约文档。
2. 应修复:help 和本地校验写入了公开契约中没有的约束
- 本地拒绝了契约允许的请求。
internal/commands/instance/snapshot/module.go的validatePagination会拒绝"正数MaxResults同时带正数Offset或Limit",返回INVALID_PAGINATION。公开契约只规定了Offset与NextToken互斥。实测agr instance snapshot list --max-results 20 --limit 10 -o json以退出码 2 失败,请求不会发出。 - help 和 README 中有契约里找不到依据的说法:
- "memory snapshots are currently unsupported"(当前不支持内存快照)
- "Omitting Memory follows service policy"(省略 Memory 时遵循服务端策略)
- "The source instance must be running and support snapshots"(源实例必须运行中且支持快照)
- "Limit with NextToken is supported"(
Limit可以与NextToken同时使用) - "keep SnapshotIds, Filters, and Limit unchanged"(翻页时保持这三项不变)
- Filter 名称示例
status、snapshot-name(契约中Filter.Name没有列出可用值)
- 前后矛盾。
--instance-id的 usage 写着 "Source instance ID required by the service",但契约中InstanceId是可选字段,CLI 也没有强制校验,不传时会直接发出不含InstanceId的请求。
建议本地只对契约明确写出的互斥规则做拒绝校验;其余说法要么删除,要么附上可以公开的依据后再保留。
3. 小问题:snapshot list 的页大小没有本地校验
help 写了 "Page sizes are at most 100",但实测 --limit -5 和 --max-results 500 都没有被本地校验拦下,一直走到凭据检查才失败。instance list 会校验 MaxResults 在 0–100 之间,并拒绝负数 Limit/Offset,建议保持一致。另外,positiveInteger 与 instance list 中的 integerRequestValue 逻辑重复,可以考虑复用。
非阻断,供参考
instance snapshot delete不提供交互确认,help 中已写明;instance delete在 TTY 下会提示确认,并提供--yes。快照是持久化数据,可以考虑与instance delete保持一致。- 目前用户可以创建、查询、删除快照,但还没有使用快照恢复实例的入口。这受限于当前公开契约,PR 正文已如实说明。
- Release impact 选择 "Stable and preview",与 #143 将正式契约放入双通道的做法一致,Beta 警告也已保留,分类合理。
已验证
go run ./cmd/internal/cobragen check、go run ./cmd/internal/apipatch check-all:通过go test ./internal/commands/instance/... ./internal/controlplane/... ./internal/cli/... ./cmd/agr/... -count=1:通过;PR 的 CI 全绿- 编译 stable 和 preview 二进制:两者
agr schema中 snapshot 相关内容一致;无 TTY、stdin 关闭时,instance snapshot delete缺少 ID 会返回结构化 JSON 错误,退出码 2 - 未运行真实云端 E2E,因此第 1、2 条中服务端的实际行为未能证明或证伪
jo-hnny
left a comment
There was a problem hiding this comment.
Review 基于当前 head 212cbedf6a368e92269084f7f9bec20490c48655(base 5bd4a49)。
上一轮 review 的问题大多已处理:页大小本地校验、snapshot delete 的 TTY 确认、--snapshot-id 恢复入口均已补齐。api/ags/v20250920/api.json 与 tccli 58f4f795 结构化比对完全一致;stable 与 preview 中 instance.create 和三个 snapshot 命令的 agr schema 输出一致。
剩余 2 个应修复项、3 个小问题。
Findings
1. 应修复:--snapshot-id 与 tool 选择器的组合规则不一致
agr instance create --snapshot-id s -t x能通过本地校验并发出请求(实测走到凭据检查才失败)。- 但 PR 对
SourceInstanceId+SnapshotId做了本地互斥(CONFLICTING_FLAGS,含--request),这条规则公开契约同样没有写;SnapshotId+ToolId/ToolName却没有任何处理。 - schema 中
snapshot-id的IncompatibleWith只有source-instance-id;help 只说恢复"不需要" tool 选择器、使用快照保存的工具配置,没有说明传了 tool 会怎样。用户或 agent 按习惯多传-t时,CLI 没有给出明确语义。
建议二选一,并请接口负责人确认服务端对该组合的行为:
- 如果服务端不接受该组合:本地拒绝
--snapshot-id与--tool-id/--tool-name(含--request),同步IncompatibleWith、help 和中英文 README; - 如果希望组合规则统一交给服务端:去掉本地的
SourceInstanceId/SnapshotId互斥,避免只对其中一部分组合做本地校验。
2. 应修复:PR 正文把不适用的 API patch 验证列为合并前提
正文的 "API patch verification — required before merging" 仍有 7 项未勾选,并写明合并前必须完成。但本 PR:
- 没有修改
api.patch.json、help.patch.json、mapping.patch.json、e2e-coverage.yaml; - 现有 patch 的 36 个 op 均不涉及
StartSandboxInstance或 Snapshot 相关 Action/Object。
PATCH-VERIFICATION.md 明确写了 "an unchanged, non-empty patch does not by itself require a new API review or live E2E run",PR 模板也说明该小节只在 patch 行为受影响时使用。按正文现在的写法,这个 PR 无法满足自己声明的合并条件,也会让 reviewer 等待一份不需要的报告。建议删除该小节,或改为"不适用:patch 未变化,且不涉及本次 Action"。
3. 小问题:克隆路径没有当前 head 的真实验证
正文的 25/25 断言覆盖了 snapshot 创建、恢复、ClientToken 重试和清理,但没有 instance create --source-instance-id。help 和 README 对克隆做了行为承诺:可以不传 tool、传了必须与源实例一致、系统盘复制后写入相互独立。建议在 E2E 中补一个克隆场景,或在 Verification 中明确说明克隆路径未实测。
4. 小问题:help/README 中仍有公开契约没有依据的服务端细节
CLI 本地已按这些规则校验,行为上自洽,但公开契约只写了 Offset 与 NextToken 互斥。以下内容仍缺少可公开的依据:
- 两种页大小都省略时服务端默认 20;
- 正数
MaxResults不能与正数Offset或Limit组合(本地以INVALID_PAGINATION拒绝); - 翻页时需保持
Limit不变、MaxResults可以变化; - 省略
Memory时服务端默认包含内存; - Filter 名称示例
status、snapshot-name(契约中Filter.Name未列出可用值)。
其中"Limit 不变、MaxResults 可变"属于翻页 token 的实现细节,后续容易漂移。建议保留的条目附上接口负责人确认或推动上游文档补充;否则只保留契约写明的部分。
5. 小问题
- PR 顺带修正了
instance delete的 help(原文写"不提示确认",代码实际会在 TTY 下提示)。修正正确,建议在 Summary 中注明这项范围外改动。 internal/commands/instance/snapshot/module.go的paginationInteger与instance list的integerRequestValue逻辑重复,可以考虑复用。
已验证
go test ./internal/commands/instance/... ./internal/cli/... ./internal/controlplane/... ./cmd/agr/... -count=1,以及-tags=preview的对应包:通过go run ./cmd/internal/cobragen check、go run ./cmd/internal/apipatch check-all:通过;PR CI 全绿- 编译 stable/preview 二进制,在无凭据的隔离 HOME 下实测:
--source-instance-id+--snapshot-id(flag 和--request)→CONFLICTING_FLAGS,退出码 2--request与--snapshot-id混用 →REQUEST_FLAG_CONFLICT- snapshot delete 位置参数与
--request中SnapshotId不一致 →REQUEST_ARG_CONFLICT snapshot list --limit -5/--max-results 500/--offset 5 --next-token abc→INVALID_PAGINATION,退出码 2- pty 模拟 TTY 执行
snapshot delete,回答n→ 返回Cancelled,未发出请求
- 未运行真实云端 E2E,未复核正文中 25/25 报告。
Summary
The canonical CLI contract was missing the published SandboxSnapshot API family and instance cloning/restoration inputs. Sync the public contract and expose snapshot creation, inspection, deletion, and restoration in both stable and preview.
agr instance create --source-instance-idclones an existing instance;agr instance create --snapshot-idrestores an ACTIVE saved disk-only snapshot into a new instance. The source modes are mutually exclusive. Snapshot restoration also rejectsToolIdandToolName; cloning permits an optional selector matching the source tool. These rules apply to flags and--request, including stdin.api.jsonfrom tccli commit58f4f795e322eafedc319dbd2cb798060296f481and use AGS/common Go SDKv1.3.193. The publishedStartSandboxInstance.SnapshotIdfield is ahead of that SDK, so the existing contract-aware signed JSON transport preserves it without typed-SDK field loss.Memory=false, validate conflicting pagination modes, and return snapshot IDs and continuation tokens. Snapshot deletion follows the existing TTY confirmation behavior with--yes; successful deletion output acknowledges request acceptance.instance deletehelp to describe its existing TTY confirmation and--yesbehavior.Closes #144.
Verification
Local validation passed against the final source tree:
go test -race ./... -count=1go test -race -tags=preview ./... -count=1golangci-lint run --timeout=5min stable and previewgo run ./cmd/internal/cobragen checkgo run ./cmd/internal/apipatch check-allgo run ./cmd/internal/apipatch coveragegit diff --checkLocal HTTPS integration tests verify signed payloads for flags and JSON/stdin requests, response output, usage errors, source conflicts, and schema contracts. The restore-selector matrix covers
SourceInstanceId,ToolId, andToolNamethrough flags, JSON, and stdin, requiring exit 2 and zero API requests. Full local test runs used an isolated home without cloud credentials.Real cloud E2E passed on commit
1950d24dae429471dc99e00a32cf497329703dc7inap-chongqing, using stable and preview binaries built from the clean committed tree against the public Cloud API and instance data plane: 107/107 assertions passed, 0 failed, 0 skipped.--requestwith identical parameters andClientToken; both returned their original instance IDs. Verified independent writes between source and restored instances.--source-instance-id, inherited the source tool, copied current disk contents, and kept later writes independent. Matching tool IDs succeeded; mismatching tool IDs were rejected by the service.statusandsnapshot-namefilters, changingMaxResultsbetween cursor pages, unchangedLimitwithNextToken, and token rejection after changingLimit. Raw API requests confirmed the service rejects positiveMaxResultscombined with positiveOffsetorLimit.Memoryfollows source policy: it produced a disk-only snapshot on the disk-only source; without that override, the service rejected the default memory snapshot as unsupported.STOPPED; deleted all 16 test snapshots and confirmed absence. A final list preserved all 5 pre-existing snapshots and contained none of the test snapshots.The first run stopped because the test supplied 16 values to one Filter, exceeding the service maximum of 10. Its 7 instances and 16 snapshots were also confirmed cleaned up. The successful rerun corrected only that test input. These results establish behavior on the tested route; the Snapshot API remains Beta.
Live E2E candidate identity
1950d24dae429471dc99e00a32cf497329703dc7; base:5bd4a49f83afc7ab51e916218f67f1039a290958.8446a4092334e1f7c713c6a1e792fc2140de5911; both binaries report the head revision andvcs.modified=false.cb5ba104c355f662d679c168e7be429d692c1bd571d74a35afe8afc91f084614.2483476bfc3a2350f7c49ab9fd2226424a1c0ffb41979b9939b8fe7852cf5e7d.2026-10-14T11:35:56.540226+00:00to2026-10-14T11:37:10.476047+00:00(UTC).API patch verification is not applicable to this change: the API overlay patches and coverage plan are unchanged, and their operations do not affect the instance cloning or snapshot Actions changed here. Under Patch verification, the retained non-empty overlay alone does not require a new full patch E2E run or independent reproduction. Normal CI and maintainer review still apply.
Release impact
Reason: Both builds inherit the updated canonical contract and expose snapshot lifecycle commands, source-instance cloning, and saved-snapshot restoration, with matching help/schema and examples. The Snapshot API remains Beta in both channels.