Files
mindfulness/spec_kit/Text Wrap/modules/breakpoint-candidates/plan.md
2026-02-10 11:39:33 +08:00

163 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# breakpoint-candidates技术计划
## 1. 计划目标
基于 `spec.md``设计说明文档/文档换行算法.md v1.2.1`,实现“候选断点生成与裁剪”模块,输出**可控规模、确定性排序**的 breakpoints 集合,保证:
- EN/TC 断点生成口径一致(断点 `pos` 均是 token 边界索引)
- 同输入必定同输出(去重、排序、裁剪与过滤全流程确定性)
- 候选规模上限严格生效(尤其 TC
- 支持约束过滤:`forbiddenBreakRanges`(必须)与 `protectedPhrases`(可选优化)
本模块只产出 breakpoints不做组合搜索与评分。
## 2. 默认技术决策(本计划采用)
- **输出结构**`Array<{ pos, kind, priority }>`,最终按 `pos` 升序
- **去重策略**:同一 `pos` 若出现多个来源候选,保留 `priority` 更高者priority 相同按 `kind` 固定序优先)
- **裁剪策略TC**:先按“候选重要性排序”截断到 `tcMaxCandidateBreaks`,再按 `pos` 升序输出
- **过滤策略**
- 必做:`forbiddenBreakRanges` 命中直接剔除
- 可选优化:若已计算 `protectedPhrases` 的 span可在生成阶段剔除 span 内断点(否则交给评分阶段强惩罚淘汰)
## 3. 输入/输出与关键口径
### 3.1 输入(来自上游)
- `tokens: Token[]`
- ENWORD tokens不包含 SPACE token
- TCgrapheme cluster tokens允许包含空格 cluster `" "`,用于 SPACE 断点)
- `lang: 'TC' | 'EN'`
- `maxLines: number`
- `constraints?: { protectedPhrases?: string[]; forbiddenBreakRanges?: Array<{ start: number; end: number }> }`
- `config: { tcMaxCandidateBreaks: number; tcPunctuations: string[]; balanceRange: number }`
### 3.2 输出(确定性)
- `breakpoints: Array<{ pos: number; kind: 'PUNCT' | 'SPACE' | 'BALANCE' | 'OTHER'; priority: number }>`
- `pos`token 边界索引,范围 `0..N`
- **最终输出必须按 `pos` 升序**
- `meta?: { pruned: boolean; originalCount: number; finalCount: number }`
### 3.3 断点边界定义(统一口径)
- 候选断点只生成在“行内断点”位置:`pos ∈ [1, N-1]`
- `pos=0``pos=N` 由搜索器作为“起止边界”处理(不作为候选断点输出)
## 4. 生成规则(按语言)
### 4.1 EN词边界kind=SPACE
#### 规则
- tokens 仅为 WORD不生成 SPACE token
- 对每个词边界产生候选断点:
-`i in 1..N-1` 生成 `pos=i, kind='SPACE'`
- `priority` 固定为基础值(建议 `priority=10`
#### 验收要点
- `"I am so tired"`N=4→ breakpoints.pos 必为 `[1,2,3]`(升序)
### 4.2 TC标点/空格/BALANCE
#### 4.2.1 标点后断点kind=PUNCT最高优先级
-`tokens[i].text` 属于 `tcPunctuations`
- 生成 `pos=i+1, kind='PUNCT'`
- `priority` 建议最高(例如 `priority=30`
#### 4.2.2 空格后断点kind=SPACE中优先级
-`tokens[i].text === ' '`
- 生成 `pos=i+1, kind='SPACE'`
- `priority` 建议中等(例如 `priority=20`
> 注:若上游对 TC 也做了空白 NORMALIZE则空格通常不会连写断点仍保持确定性。
#### 4.2.3 BALANCE 断点kind=BALANCE低优先级
目的:在无标点时,仍在“接近理想位置”附近提供少量断点,提升可解性与观感。
**理想位置计算(确定性简化版)**
- `N = tokens.length`
- `targetLines = min(maxLines, N)`(至少 1且不超过 N
-`lineIndex in 1..targetLines-1`
- `idealPos = round((N * lineIndex) / targetLines)`
- 在区间 `[idealPos - balanceRange, idealPos + balanceRange]` 生成少量候选 `pos`
**生成细则**
- 候选 `pos` 必须落在 `[1, N-1]`
- 去重前可以允许重复(后续统一去重)
- `priority` 建议最低(例如 `priority=5`
> 裁决补充口径BALANCE 断点允许落在 emotionPhrase/protectedPhrases 的 span 内;是否可用由评分阶段强惩罚决定(本模块不做语义裁决)。
## 5. 过滤、去重、裁剪与排序(必须确定性)
### 5.1 forbiddenBreakRanges 过滤(必须)
-`pos` 落在任一 `forbiddenBreakRanges` 的区间内(按项目约定:`start <= pos <= end` 或半开区间,必须写死一种),则剔除该 breakpoint
- 过滤必须发生在最终输出前,保证确定性
### 5.2 去重(必须)
同一 `pos` 可能来自多个来源(例如 PUNCT 与 BALANCE
-`priority` 更高者
- `priority` 相同则按固定 kind 序:`PUNCT > SPACE > BALANCE > OTHER`
### 5.3 TC 规模上限裁剪(必须)
`lang=TC` 且候选数超过 `tcMaxCandidateBreaks`
1. 计算每个 pos 到最近 `idealPos` 的距离 `distToIdeal`(若无 idealPos 列表则设为大值)
2. 按以下 key 排序后截断(排序必须固定):
- `priority` 降序
- `distToIdeal` 升序
- `pos` 升序
3. 取前 `tcMaxCandidateBreaks`
最后再按 `pos` 升序输出(输出顺序固定)。
### 5.4 meta 输出
- `originalCount`:过滤/去重/裁剪前的候选数量
- `finalCount`:最终输出数量
- `pruned`是否发生过裁剪finalCount < originalCount
## 6. 测试计划Vitest
### 6.1 EN 断点生成
- 输入 tokens=[I, am, so, tired] → pos=[1,2,3](确定性)
### 6.2 TC 标点/空格断点
- `tokens=['我','好','累','','😮‍💨']``tcPunctuations` 包含 ``
- 必须包含 `pos=4(kind=PUNCT)`
### 6.3 BALANCE 与裁剪
- 构造无标点长文本balanceRange>0 且 `tcMaxCandidateBreaks` 很小
- 验证裁剪后数量上限生效
- 验证输出仍按 pos 升序
### 6.4 forbiddenBreakRanges
- 给定 ranges断言命中区间内的 pos 一律被剔除
### 6.5 确定性(关键)
- 同输入多次调用 breakpoints 输出完全一致(包括 meta
## 7. 完成定义DoD
- EN/TC 候选断点生成口径与裁剪规则写死并实现
- 去重/排序/裁剪/过滤流程完全确定性
- TC 上限 `tcMaxCandidateBreaks` 生效
- 单测覆盖EN/TC 基础、BALANCE、裁剪、forbiddenBreakRanges、确定性