AT-C020 后半:模板生命周期「审计可追溯」没有实现——canvas_templates 无 actor 列、无审计表、零测试引用
- Dominant language
- TypeScript
- Stars
- 0
- Forks
- 0
- Avg merge
- 1h 7m
- Merged PRs (30d)
- 969
Description
> 来自 #3005(AT 验收 B4+B5)。实测 SHA `94f6dda037ca94da73d1b0a6359763a54db832a3`。
## 结论
验收方案 §8.2 的 AT-C020 通过标准是「组织模板创建→编辑→试跑→发布→采用→归档→恢复,**每次状态转换合法且审计可追溯**」。
前半(**合法**)实现得很扎实,后半(**审计可追溯**)**没有实现**:状态转换发生过、由谁在什么时候做的,库里不存在这份记录。按 #3005 的定级纪律「缺实现 ⇒ BLOCKED,不是 FAIL」。
## 证据(实测,非推断)
### ① 表里没有 actor
在真实 PostgreSQL 16 上跑完全部迁移后 `\d canvas_templates`(本地隔离库 `wsx_agent6_at_b4b5`):
```
org_id | key | version | display_name | status | archived_from | builtin | visibility |
owner_team_id | underlying_type | sections | created_at | updated_at | tags | title |
footer | prompt_text | layout_source | size | recommend_after
```
- 没有 `created_by` / `updated_by` / `published_by` / `archived_by` 任何一列。
- `updated_at` 是**覆盖式**的,只保留最后一次;中间的 draft→trial→published→archived 各是谁按的、什么时候按的,写完即丢。
- `archived_from` 记的是**从哪个状态归档的**(用于 restore 落点),不是谁归档的,也不带时刻。
### ② 没有审计表、没有一行代码提审计
```
grep -rn "audit" apps/api/src/application/canvas apps/api/src/interface/controllers/canvas-template.controller.ts → 0 命中
grep -rln "audit" apps/api/tests/canvas/ → 0 命中
```
40 个 `tests/canvas/*.test.ts`(448 条用例、本轮实测全绿)里,**没有任何一条**断言过状态转换留痕。
### ③ 状态机本身是穷举过的(所以缺口只在后半)
`apps/api/tests/canvas/publish-archive-instance-version.test.ts` 把「四态 × 四动作」16 格**全穷举**(不抽样),并单独测了 archived 的三种前驱各自 restore 的落点;`template-lifecycle-http.test.ts` 在真实库上验了发布互斥(部分唯一索引,不是应用层自觉)、租户隔离、归档筛选。**转换合法性这半边没有问题**,本 issue 只针对留痕。
## 影响
- AT-C020 本轮只能记 **BLOCKED**(后半无实现)。
- 连带影响:模板是组织级共享资产,「谁把 v3 发布上去的 / 谁把还在用的模板归档了」目前**无法回答**,出事只能靠人回忆。
## 建议修法(未自行动手)
两条路,都是新契约面,请裁决:
- **A. 通用审计表**:`canvas_template_audit(org_id, key, version, from_status, to_status, actor_id, at, request_id)`,在 `publishTemplate` / `archiveTemplate` / `restoreTemplate` / `mintTemplateVersion` 的同一个事务里落行。好处是与将来别的资产共用一套形状;代价是多一张表 + RLS。
- **B. 行内 actor 列**:给 `canvas_templates` 加 `created_by` / `last_transition_by` / `last_transition_at`。便宜,但**只留得住最后一次**,回答不了「v1 是谁归档的」——若 AT-C020 的「可追溯」指的是完整链路,B 不满足。
无论选哪条,验收侧都要补一条**反证**:伪造 actor(用另一个 fixture 用户的 id 写入)必须失败,否则留痕可以被调用方自述,等于没有。
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by resolving whether the audit contract uses a shared table or row-level fields, then inspect publishTemplate, archiveTemplate, restoreTemplate, and mintTemplateVersion under apps/api/src/application/canvas and their controller entry point. Use publish-archive-instance-version.test.ts and template-lifecycle-http.test.ts as the existing lifecycle references, and add coverage for complete transition history and forged actors. Done means AT-C020 transitions are auditable and the tests prove actor attribution cannot be self-asserted.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- postgresql, typescript
- Domain
- backend, databases, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100