21 KiB
Batch Auto-Import V2 Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: 实现 V2 的 URL + key 批量导入能力,覆盖模型发现、同模型别名归并、重复导入复用、异步确认、最终 gateway 验证、结果 API 与结果页所需状态投影。
Architecture: 采用 BatchImportService + ConfirmationWorker + ValidationService + RunStateStore + ResultProjection 分层架构。V2 只以 import_runs / import_run_items / import_run_item_events 作为运行态真相,旧 import_batches/* 仅保留 legacy linkage。重复导入决策基于 provider_id + api_key_fingerprint + canonical_model_family,最终可用性只认宿主真实 /v1/chat/completions。
Tech Stack: Go 1.22.2、database/sql + SQLite、Chi、OpenAPI 3.1、Go testing、httptest、现有 internal/host/sub2api 适配层与 tests/integration 集成测试套件。
0. 实施约束
- 只通过宿主 HTTP API 工作,不直写宿主数据库。
- 所有状态枚举、字段名、API 路由必须遵循当前 canonical contract。
- 每个任务都先写失败测试,再做最小实现,再跑验证。
- 每个任务独立提交,避免大而混杂的 commit。
- 任何 UI/API 展示都只能读 V2 canonical state,不得回退到日志拼接。
1. 任务总览
T1 Canonical types and enums
T2 Probe models + alias normalization + canonical family
T3 Capability profile + smoke completion routing
T4 Provider ID + reuse policy
T5 Run/item/event state store repositories
T6 BatchImportService: Stage 0~2
T7 ConfirmationWorker + retry + lease
T8 ValidationService + access status
T9 ResultProjection
T10 HTTP API: runs/items
T11 CLI: batch-import
T12 Integration + contract verification
T13 Design restoration audit
2. 设计还原验证矩阵
2.1 目标覆盖矩阵
| 设计目标 | 对应任务 | 验证方式 |
|---|---|---|
| URL + key 自动发现模型 | T2, T6, T12 | /v1/models 拉取、集成测试 |
| 模型纠错与别名归一化 | T2, T4, T9, T12 | unit + item detail projection |
| 同模型跨中转快速识别 | T2, T4, T12 | canonical_model_family 测试 |
| 重复导入自动复用 | T4, T6, T9, T12 | reuse decision + projection |
| 已启用重复账号直接复用 | T4, T6, T9, T12 | matched_account_state=active |
| 已停用/已弃用账号快速启用 | T4, T6, T7, T9, T12 | account_resolution=reactivated |
| transport + model capability profile | T3, T9, T10, T12 | profile persistence + API schema |
| channel/account 演化 | T6, T12 | patch contract + host stub |
| 异步确认与重试 | T7, T12 | lease/retry/event trail |
| gateway completion 最终判定 | T8, T12 | access_status 唯一写入 |
| 结果 API 与结果页数据源 | T5, T9, T10, T12 | run/item/event projection |
| 单一状态源 | T5, T7, T8, T9 | 只读 import_runs/* |
2.2 契约覆盖矩阵
| 契约 | 对应任务 |
|---|---|
run_id / item_id / provider_id |
T1, T4, T5 |
run.state |
T1, T5, T9 |
current_stage / confirmation_status / access_status |
T1, T5, T7, T8 |
matched_account_state / account_resolution |
T4, T5, T6, T9, T10 |
api_key_fingerprint |
T4, T5, T6 |
canonical_model_families |
T2, T4, T5, T9, T10 |
provision_reused / reused_from_* |
T4, T5, T6, T9, T10 |
/api/batch-import/runs* |
T10, T12 |
如果 T1~T12 全部完成并通过验证,T13 必须能证明上述矩阵全部为“已覆盖”,否则不得宣称 V2 可按设计实现。
3. 实施任务
Task 1: Canonical Types And Enums
Files:
- Create:
internal/batch/types.go - Test:
internal/batch/types_test.go - Reference:
docs/2026-05-21-BATCH_AUTO_IMPORT_SPEC.md
Step 1: Write the failing test
为以下枚举写失败测试:
RunStateItemStageConfirmationStatusAccessStatusMatchedAccountStateAccountResolution
至少覆盖:
- 常量值是否与文档一致
- 非法字符串是否会在后续解析层被拒绝
Step 2: Run test to verify it fails
Run:
go test ./internal/batch -run 'TestRunStateConstants|TestItemStateConstants' -count=1
Expected: FAIL,提示类型或常量不存在。
Step 3: Write minimal implementation
在 internal/batch/types.go 中定义上述类型与常量,不提前引入不需要的 helper。
Step 4: Run test to verify it passes
Run:
go test ./internal/batch -run 'TestRunStateConstants|TestItemStateConstants' -count=1
Expected: PASS
Step 5: Commit
git add internal/batch/types.go internal/batch/types_test.go
git commit -m "feat(batch): add canonical v2 state enums"
Task 2: Probe Models, Alias Normalization, Canonical Family
Files:
- Create:
internal/probe/models.go - Create:
internal/probe/aliases.go - Test:
internal/probe/models_test.go - Test:
internal/probe/aliases_test.go - Reference:
docs/2026-05-21-BATCH_AUTO_IMPORT_TDD_PLAN.md
Step 1: Write the failing test
覆盖:
/v1/modelsOpenAI 格式解析- 空模型列表
- 鉴权失败
kimi 2.6 / kimi-2.6 / kimi-k2.6归并到同一canonical_model_familydeepseek-ai/DeepSeek-V4-Provendor 前缀归一化
Step 2: Run test to verify it fails
Run:
go test ./internal/probe -run 'TestProviderModels|TestCanonicalModelFamily' -count=1
Expected: FAIL
Step 3: Write minimal implementation
实现:
ProviderModelsNormalizeModelIDCanonicalModelIDCanonicalModelFamilyBuildAliasTableResolveRequestedModelRecommendModels
Step 4: Run test to verify it passes
Run:
go test ./internal/probe -run 'TestProviderModels|TestCanonicalModelFamily' -count=1
Expected: PASS
Step 5: Commit
git add internal/probe/models.go internal/probe/aliases.go internal/probe/models_test.go internal/probe/aliases_test.go
git commit -m "feat(probe): add model discovery and canonical family normalization"
Task 3: Capability Profile And Smoke Completion Routing
Files:
- Create:
internal/probe/capability.go - Create:
internal/probe/completion.go - Test:
internal/probe/capability_test.go - Test:
internal/probe/completion_test.go - Reference:
docs/2026-05-22-BATCH_AUTO_IMPORT_V2_ARCHITECTURE.md
Step 1: Write the failing test
覆盖:
responses不支持但chat/completions可用- transport profile 的 advisory 记录
- per-model profile 记录
ResolveSmokeModel基于别名与能力选择 smoke model
Step 2: Run test to verify it fails
Run:
go test ./internal/probe -run 'TestProbeCapabilities|TestResolveSmokeModel|TestSmokeCompletion' -count=1
Expected: FAIL
Step 3: Write minimal implementation
实现:
TransportProfileModelCapabilityProfileCapabilityProfileProbeCapabilitiesCompletionResultResolveSmokeModelSmokeCompletion
Step 4: Run test to verify it passes
Run:
go test ./internal/probe -run 'TestProbeCapabilities|TestResolveSmokeModel|TestSmokeCompletion' -count=1
Expected: PASS
Step 5: Commit
git add internal/probe/capability.go internal/probe/completion.go internal/probe/capability_test.go internal/probe/completion_test.go
git commit -m "feat(probe): add capability profile and smoke completion routing"
Task 4: Provider ID And Reuse Policy
Files:
- Create:
internal/batch/provider_id.go - Create:
internal/batch/reuse_policy.go - Test:
internal/batch/provider_id_test.go - Test:
internal/batch/reuse_policy_test.go - Reference:
docs/2026-05-21-BATCH_AUTO_IMPORT_SPEC.md:336
Step 1: Write the failing test
覆盖:
- 同 host 不同 path 生成不同
provider_id - 已存在 active provider 且 family 已覆盖 ->
reused - 已存在 active account ->
matched_account_state=active,account_resolution=reused disabled/deprecated账号 ->reactivatedbrokenprovider/account ->replace- 同 family 不同 alias -> 视为已覆盖
Step 2: Run test to verify it fails
Run:
go test ./internal/batch -run 'TestNormalizeProviderID|TestDecideReuse' -count=1
Expected: FAIL
Step 3: Write minimal implementation
实现:
NormalizeProviderIDReuseDecisionDecideReuse
不要在这一步直接改 service。
Step 4: Run test to verify it passes
Run:
go test ./internal/batch -run 'TestNormalizeProviderID|TestDecideReuse' -count=1
Expected: PASS
Step 5: Commit
git add internal/batch/provider_id.go internal/batch/reuse_policy.go internal/batch/provider_id_test.go internal/batch/reuse_policy_test.go
git commit -m "feat(batch): add provider id and reuse policy"
Task 5: Run/Item/Event State Store Repositories
Files:
- Modify:
internal/store/migrations/0007_batch_import_runs.sql - Modify:
internal/store/migrations/0008_batch_import_run_events.sql - Modify:
internal/store/sqlite/import_runs_repo.go - Create:
internal/store/sqlite/import_run_items_repo.go - Create:
internal/store/sqlite/import_run_item_events_repo.go - Modify:
internal/store/sqlite/db.go - Test:
internal/store/sqlite/import_runs_repo_test.go - Test:
tests/integration/store_init_test.go
Step 1: Write the failing test
覆盖:
- run 创建/更新
- item upsert 持久化
api_key_fingerprint / canonical_model_families / matched_account_state / account_resolution / provision_reused - event append/list
- lease 字段持久化
Step 2: Run test to verify it fails
Run:
go test ./internal/store/sqlite/... ./tests/integration/... -run 'TestRunStateStore|TestStoreAppliesLatestMigration' -count=1
Expected: FAIL
Step 3: Write minimal implementation
补足 repo 与 migration,确保 schema 与文档完全一致。
Step 4: Run test to verify it passes
Run:
go test ./internal/store/sqlite/... ./tests/integration/... -run 'TestRunStateStore|TestStoreAppliesLatestMigration' -count=1
Expected: PASS
Step 5: Commit
git add internal/store/migrations/0007_batch_import_runs.sql internal/store/migrations/0008_batch_import_run_events.sql internal/store/sqlite/import_runs_repo.go internal/store/sqlite/import_run_items_repo.go internal/store/sqlite/import_run_item_events_repo.go internal/store/sqlite/db.go internal/store/sqlite/import_runs_repo_test.go tests/integration/store_init_test.go
git commit -m "feat(store): complete v2 runtime state repositories"
Task 6: BatchImportService Stage 0~2
Files:
- Create:
internal/batch/service.go - Create:
internal/batch/capability_profile.go - Create:
internal/batch/channel_evolution.go - Test:
internal/batch/service_test.go - Test:
internal/batch/channel_evolution_test.go - Reference:
internal/provision/import_service.go
Step 1: Write the failing test
覆盖:
- 创建 run + items
- reuse preflight 跳过重复 provision
- active 账号重复导入 -> reused
- deprecated 账号重复导入 -> reactivated
- patch-only 新 alias
- legacy batch/provider link 回写
Step 2: Run test to verify it fails
Run:
go test ./internal/batch -run 'TestBatchImport_StartRun|TestModelMappingDelta' -count=1
Expected: FAIL
Step 3: Write minimal implementation
实现:
BatchImportService.StartRunImportRoutingStrategyBuildImportRoutingStrategyChannelPatchContractModelMappingDelta
先接现有 provision.ImportService,不要提前扩展 UI/API。
Step 4: Run test to verify it passes
Run:
go test ./internal/batch -run 'TestBatchImport_StartRun|TestModelMappingDelta' -count=1
Expected: PASS
Step 5: Commit
git add internal/batch/service.go internal/batch/capability_profile.go internal/batch/channel_evolution.go internal/batch/service_test.go internal/batch/channel_evolution_test.go
git commit -m "feat(batch): implement v2 run setup and provision stages"
Task 7: ConfirmationWorker, Lease And Retry
Files:
- Create:
internal/batch/confirmation.go - Test:
internal/batch/confirmation_test.go - Reference:
docs/2026-05-22-BATCH_AUTO_IMPORT_V2_ARCHITECTURE.md:398
Step 1: Write the failing test
覆盖:
- 只捞
confirm + pending + retry_due + lease_expired 403probe race -> advisory- 初次
503 no available accounts-> retry -> success - 多 worker lease 互斥
disabled/deprecated命中后 reactivated 投影正确
Step 2: Run test to verify it fails
Run:
go test ./internal/batch -run 'TestConfirmationWorker' -count=1
Expected: FAIL
Step 3: Write minimal implementation
实现:
ConfirmationWorker.TickConfirmationWorker.ConfirmItem- retry 计划
- lease 生命周期
- advisory event 写入
Step 4: Run test to verify it passes
Run:
go test ./internal/batch -run 'TestConfirmationWorker' -count=1
Expected: PASS
Step 5: Commit
git add internal/batch/confirmation.go internal/batch/confirmation_test.go
git commit -m "feat(batch): add confirmation worker and retry handling"
Task 8: ValidationService And Final Access Status
Files:
- Create:
internal/batch/validation.go - Test:
internal/batch/validation_test.go - Reference:
internal/access/closure.go
Step 1: Write the failing test
覆盖:
confirmed/advisory + chat 200 -> active- exhausted transient ->
degraded - definitive invalid path ->
broken - 只有 ValidationService 可以写
access_status
Step 2: Run test to verify it fails
Run:
go test ./internal/batch -run 'TestValidationService' -count=1
Expected: FAIL
Step 3: Write minimal implementation
实现:
ValidationService.ValidateItemaccess_status映射- 对 run summary 的最小更新
Step 4: Run test to verify it passes
Run:
go test ./internal/batch -run 'TestValidationService' -count=1
Expected: PASS
Step 5: Commit
git add internal/batch/validation.go internal/batch/validation_test.go
git commit -m "feat(batch): add validation service for final access status"
Task 9: ResultProjection
Files:
- Create:
internal/batch/status_projection.go - Test:
internal/batch/status_projection_test.go - Reference:
docs/2026-05-22-BATCH_AUTO_IMPORT_V2_API_SCHEMAS.md
Step 1: Write the failing test
覆盖:
- run summary 聚合
- item summary/detail projection
- warning 文案模板
provision_reusedbadgematched_account_state / account_resolution文案与 badge
Step 2: Run test to verify it fails
Run:
go test ./internal/batch -run 'TestStatusProjection' -count=1
Expected: FAIL
Step 3: Write minimal implementation
实现:
- run list projection
- item list projection
- item detail projection
- warning/badge mapping
Step 4: Run test to verify it passes
Run:
go test ./internal/batch -run 'TestStatusProjection' -count=1
Expected: PASS
Step 5: Commit
git add internal/batch/status_projection.go internal/batch/status_projection_test.go
git commit -m "feat(batch): add result projection for v2 runs and items"
Task 10: HTTP API For Runs And Items
Files:
- Create:
internal/app/http_batch_import.go - Create:
internal/app/http_batch_runs.go - Modify:
internal/app/http_api.go - Test:
internal/app/http_batch_import_test.go - Test:
internal/app/http_batch_runs_test.go - Reference:
docs/openapi.yaml
Step 1: Write the failing test
覆盖:
POST /api/batch-import/runsGET /api/batch-import/runsGET /api/batch-import/runs/{run_id}GET /api/batch-import/runs/{run_id}/itemsGET /api/batch-import/runs/{run_id}/items/{item_id}subscription/self_service条件必填- 列表过滤
matched_account_state / account_resolution
Step 2: Run test to verify it fails
Run:
go test ./internal/app -run 'TestBatchImportHTTP|TestBatchRunsHTTP' -count=1
Expected: FAIL
Step 3: Write minimal implementation
按 OpenAPI 只输出 projection,不泄漏 legacy 表结构。
Step 4: Run test to verify it passes
Run:
go test ./internal/app -run 'TestBatchImportHTTP|TestBatchRunsHTTP' -count=1
Expected: PASS
Step 5: Commit
git add internal/app/http_batch_import.go internal/app/http_batch_runs.go internal/app/http_api.go internal/app/http_batch_import_test.go internal/app/http_batch_runs_test.go
git commit -m "feat(api): add batch import v2 endpoints"
Task 11: CLI Entry For Batch Import
Files:
- Modify:
cmd/cli/main.go - Create:
cmd/cli/batch_import.go - Test:
cmd/cli/batch_import_test.go
Step 1: Write the failing test
覆盖:
- 参数解析
subscription必填订阅参数self_service必填probe_api_key--confirm-timeout- 结果输出
run_id/result_page
Step 2: Run test to verify it fails
Run:
go test ./cmd/cli -run 'TestBatchImportCLI' -count=1
Expected: FAIL
Step 3: Write minimal implementation
实现 CLI 到 V2 API/service 的入口,不在 CLI 层重复业务逻辑。
Step 4: Run test to verify it passes
Run:
go test ./cmd/cli -run 'TestBatchImportCLI' -count=1
Expected: PASS
Step 5: Commit
git add cmd/cli/main.go cmd/cli/batch_import.go cmd/cli/batch_import_test.go
git commit -m "feat(cli): add v2 batch import command"
Task 12: Integration And End-To-End Verification
Files:
- Create:
tests/integration/batch_import_v2_test.go - Modify:
tests/integration/host_stub_test.go(如需 stub 扩展)
Step 1: Write the failing test
至少覆盖 6 条真实业务链:
- 发现模型并归一化
- 重复导入 active 账号 -> reused
- deprecated 账号 -> reactivated
- 同 family 不同 alias -> patch_only
- probe race + warmup retry -> advisory + active
- run/item/event 详情可从 V2 新表完全读出
Step 2: Run test to verify it fails
Run:
go test ./tests/integration/... -run 'TestBatchImportV2' -count=1
Expected: FAIL
Step 3: Write minimal implementation
补齐 host stub、fake adapter、seed data,确保每条链路都可复现。
Step 4: Run test to verify it passes
Run:
go test ./tests/integration/... -run 'TestBatchImportV2' -count=1
Expected: PASS
Step 5: Commit
git add tests/integration/batch_import_v2_test.go tests/integration/host_stub_test.go
git commit -m "test(integration): cover batch import v2 flows"
Task 13: Design Restoration Audit
Files:
- Create:
docs/2026-05-22-BATCH_AUTO_IMPORT_V2_RESTORATION_CHECKLIST.md - Modify:
docs/EXECUTION_BOARD.md
Step 1: Write the failing audit checklist
列出必须逐项勾选的设计恢复项:
- 8 项 Objective
- canonical contract
- 结果 API
- migration
- worker/retry/lease
- reuse/reactivation
Step 2: Run verification to identify gaps
Run:
go test ./... -count=1
go test ./tests/integration/... -count=1
go test -cover ./internal/... -count=1
go vet ./...
gofmt -l .
Expected: 在实现完成前,这一步用来发现剩余设计缺口;在最终完成时必须全绿。
Step 3: Write the audit artifact
将每一项设计要求映射到:
- 代码文件
- 测试文件
- API 路由
- 状态字段
Step 4: Update board with true gate
在执行板中明确:
- 哪些任务完成
- 哪些设计要求已还原
- 是否可宣称“V2 设计已被完整实现”
Step 5: Commit
git add docs/2026-05-22-BATCH_AUTO_IMPORT_V2_RESTORATION_CHECKLIST.md docs/EXECUTION_BOARD.md
git commit -m "docs(v2): add restoration checklist and completion gate"
4. 全局验证门禁
完成 T1~T13 后,必须一次性通过:
gofmt -l .
go vet ./...
go test ./... -count=1
go test ./tests/integration/... -count=1
go test -cover ./internal/... -count=1
额外检查:
docs/openapi.yaml与 handler 响应字段一致import_runs/*足以支撑结果页,不依赖 legacy 表拼接matched_account_state / account_resolution / provision_reused能在 item detail 里直接读到canonical_model_family能把同模型别名判定为同一族
5. 计划完整性结论
这份计划只有在满足以下条件时,才算“任务可以完全还原规划设计”:
- T1~T12 实现完成并全部通过验证
- T13 的还原清单中不存在未映射设计项
- 任一 Objective 都能指向至少一条:
- 实现任务
- 自动化测试
- API 或状态字段证据
- 结果页/API 不需要额外新增未规划字段才能解释最终状态
如果 T13 审核时发现任何一项设计要求无法映射到任务或测试,这份计划必须回退修改,不能直接进入实现。
6. 推荐提交顺序
建议按以下小步提交:
feat(batch): add canonical v2 state enumsfeat(probe): add model discovery and canonical family normalizationfeat(probe): add capability profile and smoke completion routingfeat(batch): add provider id and reuse policyfeat(store): complete v2 runtime state repositoriesfeat(batch): implement v2 run setup and provision stagesfeat(batch): add confirmation worker and retry handlingfeat(batch): add validation service for final access statusfeat(batch): add result projection for v2 runs and itemsfeat(api): add batch import v2 endpointsfeat(cli): add v2 batch import commandtest(integration): cover batch import v2 flowsdocs(v2): add restoration checklist and completion gate
Plan complete and saved to docs/plans/2026-05-22-batch-auto-import-v2-implementation-plan.md. Two execution options:
1. Subagent-Driven (this session) - I dispatch fresh subagent per task, review between tasks, fast iteration
2. Parallel Session (separate) - Open new session with executing-plans, batch execution with checkpoints
Which approach?