boardx / boardx/workspacex

P2 design-delta(skills): listSkills.out 无法表达「这一行不可表示」—— 一行坏数据仍毒死整份列表(非阻塞,待人类评估 API 影响面)

Open
#591 0 comments 0 reactions 0 assignees View on GitHub
backlog p2
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.