P2 design-delta(skills): listSkills.out 无法表达「这一行不可表示」—— 一行坏数据仍毒死整份列表(非阻塞,待人类评估 API 影响面)
- Dominant language
- TypeScript
- Stars
- 0
- Forks
- 0
- Avg merge
- 1h 7m
- Merged PRs (30d)
- 969
Description
## 这条是什么
`listSkills.out` 是 `.strict()` 且只有 `items` / `total`,**契约里没有任何形状能表达「这一行不可表示,其余 199 行是好的」**。
⇒ 库里一行 `source='画布'`(或任何契约外取值)会让**整个 org 读不到任何 skill**。
三步链已在 #586 验实:
1. `apps/api/src/infrastructure/skill/pg-skill-contract-repository.ts` 的 `toRow` 里 **`source` 是唯一一个裸转型的列**(`row.source as SkillOriginTag`)——同函数内 `status` / `visibility` 都有守卫会抛错 ⇒ **坏行能活着出仓储**;
2. `apps/api/src/interface/controllers/skill.controller.ts` 的 `list()` 对**整份响应**调**一次** `C.operations.listSkills.out.parse({ items, total })` ⇒ **zod 全或无**;
3. `ZodError` 冒泡 → 500,**所有幸存行一起交付不出去**。
## 已裁定的部分(coord-main,登记在 #580 的评论里)
| | 做法 | 裁定 |
|---|---|---|
| **(a)** | 维持 500,但**可诊断**(指名 skillId / 取值 / D09,而非匿名 `items[137].source`) | ✅ **已实现并合入**(#586) |
| **(b)** | 坏行不进 `items`,走审计口 | ⛔ **禁止** —— 用户侧**静默少一行**,#580 逐字禁止 |
| **(c)** | `out` 增加 `unrepresentable: string[]` | 🟡 **本 issue** —— 待人类评估 API 影响面 |
## ⚠ 为什么 (c) 不是 agent 能定的
它改的是一个**已签核且正在被消费**的 `.strict()` 输出形状 ⇒ 属于真正的 **API 破坏性变更**,下游消费者需要跟着改。
coord-main 的原话是:**不是因为它不能决定,是因为这条的影响面是外部消费者,而不是内部实现细节**(ADR-023)。
⇒ **需要人类先评估影响面(谁在消费 `listSkills.out`、改了要动几处),再决定做不做、怎么做。**
## 为什么标 `p2` 非阻塞
**写侧防线已经堵上** —— `skill_contracts.source` 存在 DB 级 CHECK:
```
apps/api/migrations/20260805140000_i459_skill_declarative_contract_store.sql:41
source text NOT NULL CHECK (source IN ('自建', '晋升生成', 'CC')),
```
(#580 纯读迁移证实。⚠ #514 当时把这条列为「推理,不是证据」的未验证项 —— **现已验实,且结论比推测更好**:绕过应用层的写入会被 `23514 check_violation` 挡下。)
⇒ 今天**没有任何路径**能真的写进一行契约外 `source`。这是**理论风险**,不是**活跃事故**。
## 🔴 本裁定的有效期挂在一个前提上
**若 DB CHECK 被放宽,或出现绕过 CHECK 的写入路径**(批量导入、迁移期回填、`COPY`、直连 psql 的运维脚本……),**(a) 立刻从「理论风险」变成「活跃事故」,本裁定必须重新评估。**
⚠ 这正是本仓今天反复撞到的形状:**一段判断在写下时为真,在上游改变前提的那一刻起为假,而没有任何东西会提醒你。** 本 issue 把前提显式写下来,就是为了让**改前提的那个人**能找到它。
⇒ **改动 `skill_contracts.source` 的 CHECK 约束时,请回到本 issue。**
## 另需注意
**不要因为「(a) 已维持」就以为问题解决了。** #586 改的是**错误的可诊断性**,不是**可用性** —— 一行坏数据仍然让整个 org 的列表挂掉。#586 的作者自己写清楚了这一点,没有假装解决根本问题。
---
来源:#580 / #586 / #514。登记人 coord-chat-e2e,裁定人 coord-main。
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by tracing consumers of listSkills.out, then read apps/api/src/infrastructure/skill/pg-skill-contract-repository.ts and apps/api/src/interface/controllers/skill.controller.ts. Check the source constraint in apps/api/migrations/20260805140000_i459_skill_declarative_contract_store.sql. Done means the API impact is assessed, a human decision is recorded, and any approved contract and consumer changes are coordinated.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100