fix(delivery): avoid false deployed timestamp
Rules-Applied: R07,R13
This commit is contained in:
parent
8bc33aaab9
commit
dbc1b068ab
@ -203,6 +203,8 @@ GET /health/version
|
||||
|
||||
v1 不新增 deployment history 表。最近部署时间和过程状态来自 Gitea status 的时间字段,当前运行事实来自实时探针;连续历史、受控部署和审计记录等真实需求出现后,再评估 SQLite record。
|
||||
|
||||
`deployed_at` 仅在对应部署 context 成功时取其状态更新时间;失败、等待和跳过的状态不能用其更新时间冒充部署完成时间。
|
||||
|
||||
### 系统边界变更
|
||||
|
||||
- **Enterprise System HTTP 契约**:新增 `/health/version`;Docker 镜像携带不可变 revision,回滚镜像自然恢复对应旧 revision。
|
||||
@ -347,6 +349,15 @@ v1 不新增 deployment history 表。最近部署时间和过程状态来自 Gi
|
||||
- acceptance: "[MCP] `get_delivery_status(project_id)` 对固定 fixture 调用 → Gitea success + probe healthy + SHA 相同返回 deployed;成功部署但 SHA 不同返回 drifted;deploy skipped 且运行旧 SHA 返回 skipped;deploy failure 同时保留实际运行 SHA;来源不可达返回 unavailable;证据不足返回 unknown,且现有 Git/PR/CI/review 顶层字段保持兼容。"
|
||||
- progress_note: "2026-09-23:真实 Enterprise run #529 的 deploy-api 为 skipped;聚合层新增 skipped 与旧运行 SHA 回归测试,避免普通文档提交误报 drifted。隔离分支 cargo test --lib 187/187;正式版尚未加载新代码,production 探针当前为 401,待 PR/发布后做 MCP 端到端验收。"
|
||||
|
||||
### ✅ P3-D1. 修正 deployment 时间证据与接入说明
|
||||
- status: done
|
||||
- complexity: M
|
||||
- files: src-tauri/src/commands/delivery_status.rs, src-tauri/resources/onboarding/git-workflow.md.tmpl, docs/ai-context/git-workflow.md
|
||||
- depends: P3-D. deployment 双证据聚合与 MCP 契约
|
||||
- acceptance: "agent_verify: [cargo test] aggregate_deployment 的 success context 保留 deployed_at,skipped/failed/pending context 的 deployed_at 为 null 且仍保留运行 SHA;[文档对照] 接入模板与 docs/ai-context/git-workflow.md 均说明 drifted 仅限成功部署后 SHA 不一致、skipped 不误报漂移;[git diff] diff --check 通过。"
|
||||
- done_note: "2026-09-23:聚合回归测试覆盖 success/skipped/failed/pending 的 deployed_at 与运行 SHA;cargo test --lib 187/187、scoped rustfmt、模板与说明文件对照、git diff --check 均通过。"
|
||||
- retrospective: "【复盘】部署时间证据与接入说明;任务类型: MCP工具;复杂度: M;实际轮数: 1;是否返工: 是;返工原因: skipped context 的更新时间被误写为 deployed_at,模板未同步 skipped 边界;有效策略: 对同一 fixture 同时断言状态、运行 SHA 和时间字段,并逐字对照分发模板;遗留风险: 正式版未加载本 PR,跨项目 MCP 实机仍待合并后终验;auto_applied_rules: R07,R13;rule_effectiveness: R07=有效,R13=未验证。"
|
||||
|
||||
### 📋 P3-E. Enterprise production 端到端验收与观测基线
|
||||
- status: todo
|
||||
- complexity: M
|
||||
|
||||
@ -109,6 +109,7 @@ PR 别攒太大(建议每 3~5 个 commit 或几百行内就开一次),否
|
||||
- `unavailable`:对应外部来源暂时不可达;保留本地验证与 Gitea 网页作为降级路径。
|
||||
- `not_configured`:项目尚未声明 `.lian-jing/capabilities.yaml` 的 production delivery profile;这是正常的可选接入状态,不代表部署失败。
|
||||
- `deployed` 仅表示 Gitea 部署 context 成功、公开版本探针健康且运行 SHA 与默认分支目标 SHA 完全一致;成功部署后 SHA 不一致才标 `drifted`。本次提交部署 job 被跳过时标 `skipped` 并保留实际运行 SHA,不误报漂移。技术健康不替代业务验收。
|
||||
- `deployment.data.deployed_at` 仅在部署 context 成功时取其状态更新时间;`skipped`、`failed`、`pending` 不提供部署完成时间,实际运行 SHA 仍单独保留。
|
||||
- production profile 由项目维护,炼境接入包不会覆盖;只声明公开 HTTP(S) JSON URL、精确 Gitea context 和字段名,不放凭证或命令。
|
||||
- 结果是交付上下文,不替代项目自己的测试、业务验收或发布决定。
|
||||
|
||||
|
||||
@ -108,7 +108,8 @@ PR 别攒太大(建议每 3~5 个 commit 或几百行内就开一次),否
|
||||
- `unknown`:炼境尚未获得该项证据,**不是成功**;例如 CI 成功不能代替 production 健康。
|
||||
- `unavailable`:对应外部来源暂时不可达;保留本地验证与 Gitea 网页作为降级路径。
|
||||
- `not_configured`:项目尚未声明 `.lian-jing/capabilities.yaml` 的 production delivery profile;这是正常的可选接入状态,不代表部署失败。
|
||||
- `deployed` 仅表示 Gitea 部署 context 成功、公开版本探针健康且运行 SHA 与默认分支目标 SHA 完全一致;`drifted` 表示运行 SHA 不一致。技术健康不替代业务验收。
|
||||
- `deployed` 仅表示 Gitea 部署 context 成功、公开版本探针健康且运行 SHA 与默认分支目标 SHA 完全一致;成功部署后 SHA 不一致才标 `drifted`。本次提交部署 job 被跳过时标 `skipped` 并保留实际运行 SHA,不误报漂移。技术健康不替代业务验收。
|
||||
- `deployment.data.deployed_at` 仅在部署 context 成功时取其状态更新时间;`skipped`、`failed`、`pending` 不提供部署完成时间,实际运行 SHA 仍单独保留。
|
||||
- production profile 由项目维护,炼境接入包不会覆盖;只声明公开 HTTP(S) JSON URL、精确 Gitea context 和字段名,不放凭证或命令。
|
||||
- 结果是交付上下文,不替代项目自己的测试、业务验收或发布决定。
|
||||
|
||||
|
||||
@ -409,7 +409,11 @@ fn aggregate_deployment(
|
||||
components
|
||||
.values()
|
||||
.next()
|
||||
.and_then(|(_, deploy, _)| deploy.data["status"]["updated_at"].as_str())
|
||||
.and_then(|(_, deploy, _)| {
|
||||
(deploy.state == "success")
|
||||
.then(|| deploy.data["status"]["updated_at"].as_str())
|
||||
.flatten()
|
||||
})
|
||||
.map(str::to_owned)
|
||||
})
|
||||
.flatten();
|
||||
@ -851,6 +855,7 @@ delivery:
|
||||
assert_eq!(deployed.state, "deployed");
|
||||
assert_eq!(deployed.data["target_revision"], target);
|
||||
assert_eq!(deployed.data["running_revision"], target);
|
||||
assert_eq!(deployed.data["deployed_at"], "2026-09-22T03:38:55Z");
|
||||
assert_eq!(deployed.data["evidence"].as_array().unwrap().len(), 2);
|
||||
|
||||
let drifted = aggregate_fixture("success", "healthy", Some(&old));
|
||||
@ -859,10 +864,11 @@ delivery:
|
||||
let failed = aggregate_fixture("failure", "healthy", Some(&old));
|
||||
assert_eq!(failed.state, "failed");
|
||||
assert_eq!(failed.data["running_revision"], old);
|
||||
assert_eq!(
|
||||
aggregate_fixture("pending", "healthy", Some(&old)).state,
|
||||
"pending"
|
||||
);
|
||||
assert!(failed.data["deployed_at"].is_null());
|
||||
let pending = aggregate_fixture("pending", "healthy", Some(&old));
|
||||
assert_eq!(pending.state, "pending");
|
||||
assert_eq!(pending.data["running_revision"], old);
|
||||
assert!(pending.data["deployed_at"].is_null());
|
||||
assert_eq!(
|
||||
aggregate_fixture("success", "unhealthy", Some(&target)).state,
|
||||
"unhealthy"
|
||||
@ -874,6 +880,7 @@ delivery:
|
||||
let skipped = aggregate_fixture("skipped", "healthy", Some(&old));
|
||||
assert_eq!(skipped.state, "skipped");
|
||||
assert_eq!(skipped.data["running_revision"], old);
|
||||
assert!(skipped.data["deployed_at"].is_null());
|
||||
assert_eq!(
|
||||
aggregate_fixture("skipped", "unavailable", None).state,
|
||||
"unavailable"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user