mirror of
https://github.com/motajs/template.git
synced 2026-10-29 20:30:20 +08:00
docs(10): create advanced II phase plans
This commit is contained in:
parent
ddbc2825c4
commit
7331174fa2
190
.planning/phases/10-docs-restructure/10-21-PLAN.md
Normal file
190
.planning/phases/10-docs-restructure/10-21-PLAN.md
Normal file
@ -0,0 +1,190 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 21
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- docs/advanced/ii/custom-range.md
|
||||
- docs/advanced/ii/static-vs-dynamic-tiles.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 30000
|
||||
raw_tokens: 20000
|
||||
tasks: 3
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "本计划先向用户汇报整套高级 II 的文档文件名、侧边栏分组名与读者人格,并等待「可以执行」;文件名与分组名未经用户批准不得落盘(D-13)"
|
||||
- "`custom-range.md` 讲清 `IRange` 的职责、宿主 `IRangeHost`、内置范围与参数类型,并给出一个可运行的自定义范围类;示例说明它如何被怪物上下文与地图伤害复用"
|
||||
- "`static-vs-dynamic-tiles.md` 讲清静态图块与动态图块的区别、双向转换与事件保留,并**单独成节**讲点事件与图块事件"
|
||||
- "两篇均按 D-59 库文档风格写:先概念后示例、完整代码块、解释性注释充足(D-65)、主干与边界分离(D-61);不写中级教程的六步实操(D-58 / D-59)"
|
||||
- "只写数据端内容;渲染端可视化、编辑器与状态栏相关内容一律用 D-47 非断链占位,不写 `src/client` 路径"
|
||||
- "两篇均 kebab-case、CRLF、单 H1;无指向不存在页面的相对链接;构建 green、`build complete`、dead link = 0"
|
||||
artifacts:
|
||||
- "docs/advanced/ii/custom-range.md"
|
||||
- "docs/advanced/ii/static-vs-dynamic-tiles.md"
|
||||
- ".planning/phases/10-docs-restructure/10-21-SUMMARY.md"
|
||||
key_links:
|
||||
- "范围对象是光环、地图伤害、查询共用的底层能力,后两篇属性文档会回指本篇"
|
||||
- "图块篇的点事件/图块事件是装备、寻路与地图伤害文档的事件前提"
|
||||
- "10-25 负责把本计划产出的页面接入侧边栏,本计划不修改 `config.ts`"
|
||||
---
|
||||
|
||||
<objective>
|
||||
编写高级 II 的两篇基础篇:`custom-range.md`(自定义范围对象)与 `static-vs-dynamic-tiles.md`(静态图块与动态图块,含点事件与图块事件)。
|
||||
|
||||
Purpose: 范围对象与图块是高级 II 其它主题的公共底座——光环、地图伤害、查询都要扫描范围,装备与寻路都要和图层、图块事件打交道。先把这两篇写扎实,后续属性与战斗篇才能直接引用而不重复解释。
|
||||
Output: 两篇可构建的 `docs/advanced/ii/*.md` 正文。
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/13194/.config/opencode/gsd-core/workflows/execute-plan.md
|
||||
@C:/Users/13194/.config/opencode/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/phases/10-docs-restructure/10-CONTEXT.md
|
||||
@docs/advanced/index.md
|
||||
@docs/advanced/declarative-vs-imperative.md
|
||||
@docs/advanced/plugin-system.md
|
||||
@docs/intermediate/enemy-aura.md
|
||||
@docs/intermediate/enemy-map-damage.md
|
||||
@docs/intermediate/map-tiles.md
|
||||
@docs/intermediate/map-structure.md
|
||||
@packages/common/src/utils/types.ts
|
||||
@packages/common/src/utils/range.ts
|
||||
@packages-user/data-system/src/combat/context.ts
|
||||
@packages-user/data-system/src/combat/mapDamage.ts
|
||||
@packages-user/data-base/src/map/types.ts
|
||||
@packages-user/data-base/src/map/tile.ts
|
||||
@packages-user/data-base/src/map/staticTile.ts
|
||||
@packages-user/data-base/src/map/dynamicTile.ts
|
||||
@packages-user/data-base/src/map/mapLayer.ts
|
||||
|
||||
**命名基线(须用户批准,D-13):**
|
||||
- 高级 II 目录:`docs/advanced/ii/`
|
||||
- 本计划两篇:`custom-range.md`(自定义范围对象)、`static-vs-dynamic-tiles.md`(静态图块与动态图块)
|
||||
- 整套九篇:`ii/index.md`、`custom-range.md`、`static-vs-dynamic-tiles.md`、`equipment-system.md`、`custom-replay-command.md`、`pathfinding.md`、`adding-hero-attribute.md`、`adding-enemy-attribute.md`、`enemy-context-and-combat.md`
|
||||
|
||||
**写作规范(D-53..D-66,逐条遵守):**
|
||||
- D-59:按库文档写,先说要解决什么问题,再给完整示例与注释,最后说接入点;不写「复制什么 / 改什么」六步。
|
||||
- D-60:能删就删,不重复中级教程已讲清的内容。
|
||||
- D-61:主干只讲核心;边界情况(越界、性能取舍、非法参数)文末单独成节。
|
||||
- D-62:代码块必须是完整类/函数,不能只给类成员片段。
|
||||
- D-63:先理念、再引导语、再模板代码、最后解释。
|
||||
- D-64:只有读者需要自己实现接口时才给模板;纯调用只给示例。
|
||||
- D-65:示例注释越多越好,解释「为什么」而不是复述代码。
|
||||
- D-66:多个并列功能分开写示例,不合并成大块。
|
||||
- D-58:不写「不懂代码就改内置」一类中级提示,也不强制模板章节。
|
||||
- D-55:新增系统优先插件;需要改内置时再改。
|
||||
- D-47:指向高级 III 或尚不存在的页面时,只用代码 span(如 `高级 III · 范围性能`)或 `<!-- TODO(advanced-III): ... -->` 注释。
|
||||
|
||||
**写法口径:**
|
||||
- 两篇都面向「有较好代码能力、能读懂引擎源码、想做系统级定制,但不熟悉具体系统实现」的读者。
|
||||
- 需要读者实现接口时给完整骨架,并用 `[需改]` 标出改动点;纯接口调用只给短示例。
|
||||
- 每一篇结尾给「怎么验证」段:给出可执行的调用或控制台检查方式,并说明预期现象。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 汇报高级 II 规划基线并等待「可以执行」</name>
|
||||
<action>向用户汇报:**要做什么**(编写高级 II 的两篇基础文档:自定义范围对象、静态图块与动态图块,并在后续计划中补齐其余六篇、导览、侧边栏注册与两轮修订)、**为什么**(D-67 把高级 II 定位为「深入定制」难度层;范围对象与图块是光环、地图伤害、装备、寻路的公共底座)、**大致怎么做**(本计划写两篇;10-22 写装备与录像指令;10-23 写寻路与勇士属性;10-24 写怪物属性与怪物上下文;10-25 写导览并接入侧边栏;10-26 读者审阅;10-27 用户主导修订循环)、**影响范围**(新增 `docs/advanced/ii/` 下两篇;不修改 `config.ts`、生产代码与渲染端内容)。同时请用户确认以下命名与设定(D-13):
|
||||
1. 目录 `docs/advanced/ii/`;
|
||||
2. 九篇文件名:`index.md`、`custom-range.md`、`static-vs-dynamic-tiles.md`、`equipment-system.md`、`custom-replay-command.md`、`pathfinding.md`、`adding-hero-attribute.md`、`adding-enemy-attribute.md`、`enemy-context-and-combat.md`;
|
||||
3. 侧边栏分组显示名由「高级 II · 系统实现」改为「高级 II · 深入定制」(D-53 的分层是难度标签,不是领域分类);
|
||||
4. 高级 II 读者人格:「你是一位有较好代码能力、能读懂引擎源码的数据端作者,想做系统级定制,但还不熟悉具体系统实现;你已经读完高级 I,正在读高级 II。」
|
||||
汇报中明确:本计划的修改方式为**派发子代理执行**,范围严格限于 `files_modified` 列出的两篇文档,执行者不内联修改文件。停下等待用户明确回复「可以执行」四个字;未收到前不得创建目录、不得写任何文档、不得派发子代理。</action>
|
||||
<what-built>本计划内容:高级 II 两篇基础文档 + 整套高级 II 的命名与流程基线。</what-built>
|
||||
<how-to-verify>阅读上面的四段说明,重点确认九篇文件名、侧边栏分组名与读者人格;如要改名请直接给出替代名称。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<verify>
|
||||
<human-check>用户已确认九篇文件名与侧边栏分组名,并回复「可以执行」。</human-check>
|
||||
</verify>
|
||||
<done>用户确认命名基线并回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 编写自定义范围对象 custom-range.md</name>
|
||||
<files>docs/advanced/ii/custom-range.md</files>
|
||||
<action>写 `docs/advanced/ii/custom-range.md`,单 H1「自定义范围对象」。正文顺序:
|
||||
1. **范围对象解决什么问题**:光环、地图伤害、查询都需要「以某个点为中心,找出周围哪些格」;这些系统共用 `IRange`,因此自定义一次即可在多个系统复用。说明坐标索引约定 `index = y * width + x`。
|
||||
2. **范围对象与宿主**:`IRangeHost` 只有 `width` 与 `height`;`bindHost(host)` 决定边界;`inBound` / `inBoundIndex` 判断宿主边界。强调范围对象本身不保存地图,只保存宿主尺寸。
|
||||
3. **接口职责**:`iterateLoc(param)` 枚举范围内全部索引;`inRange(x, y, param)` 判断单点;`inRangeIndex` 是索引版;`iterate(list, param)` 过滤已有列表;`scan(list, param)` 按范围顺序命中集合;`autoDetect` 在两者间自动选择。说明 `scan` 与 `iterate` 的复杂度差异只作选型提示,不展开底层实现。
|
||||
4. **内置范围与参数**:表格列 `FullRange`、`RectRange` + `IRectRangeParam`、`ManhattanRange` + `IManhattanRangeParam`、`RayRange` + `IRayRangeParam`、`NoneRange`。每项写适用场景与参数含义(`x/y/w/h`、`cx/cy/radius`、`cx/cy/dir`)。
|
||||
5. **自定义范围**:说明继承 `BaseRange<T>` 需要实现 `estimatePointCount(param)`、`iterateLoc(param)`、`inRange(x, y, param)`;给出完整类示例——**环形范围**(曼哈顿距离恰好等于 `radius` 的空心环),参数接口 `IRingRangeParam { cx, cy, radius }`。注释解释 `estimatePointCount` 用于 `autoDetect` 估算、`iterateLoc` 必须与 `inRange` 的判定完全一致、边界用 `inBound` 收口。不要引用不存在的接口名。
|
||||
6. **绑定到系统**:给出两段短示例——怪物上下文 `context.scanRange(new RingRange(), { cx, cy, radius: 2 })`;地图伤害/光环视图在 `apply` 中通过 `range.bindHost(context)` 后调用 `range.iterateLoc(param)`。说明宿主由系统注入,自定义范围不要自己保存地图宽高。
|
||||
7. **与视图/转换器的关系**:一句话说明光环视图的 `range` 与 `getRangeParam()` 返回范围对象与参数;具体转换器写法指向 `docs/intermediate/enemy-aura.md`,不重复。
|
||||
8. **怎么验证**:给出可执行的检查——在插件或事件里用 `state.enemyContext.bindHero(...)` 后调用 `scanRange`,打印命中坐标;预期环形只命中四条边、不含中心。若当前场景没有怪物,说明可先放两只测试怪。
|
||||
9. **边界与注意**:越界坐标必须返回 `false`;`iterateLoc` 与 `inRange` 不一致会让 `autoDetect` 产生偏差;`radius` 为负时按绝对值处理的约定;范围对象可复用但 `bindHost` 后不要跨不同尺寸的地图共用。
|
||||
按 D-59 叙述,不写六步实操;示例代码完整(接口 + 类 + 调用),注释充足;加粗符合 flanking。完成后跑 prettier `--write docs/advanced/ii/custom-range.md`。Commit:`docs(10-21): 编写高级 II 自定义范围对象`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/ii/custom-range.md'; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; foreach($needle in @('IRange','IRangeHost','BaseRange','bindHost','iterateLoc','inRange','autoDetect','scanRange','ManhattanRange','NoneRange','怎么验证')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; if($raw -match 'src/client'){ Write-Error "RENDERER-CONTENT" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent $p) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '```ts'){ Write-Error "NO-TS-BLOCK" }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'RANGE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "MISSING-CONTENT", "RENDERER-CONTENT", "DEAD-LINK", "NO-TS-BLOCK", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "RANGE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>范围篇存在、CRLF、单 H1;`IRange`/宿主/接口职责/内置范围/自定义环形范围/绑定系统/验证/边界齐全;代码块完整;无渲染端内容;无死链;构建通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 编写静态图块与动态图块 static-vs-dynamic-tiles.md</name>
|
||||
<files>docs/advanced/ii/static-vs-dynamic-tiles.md</files>
|
||||
<action>写 `docs/advanced/ii/static-vs-dynamic-tiles.md`,单 H1「静态图块与动态图块」。正文顺序:
|
||||
1. **两种图块的分工**:静态图块 `IStaticTile` 是图层数组里的一格,位置固定;动态图块 `IDynamicTile` 有 `mover`、可移动、有独立实例。说明 `num()` / `raw()` / `locator` / `layer` 的共同点。
|
||||
2. **创建与获取**:静态图块通过 `layer.getTile(x, y)`;动态图块通过 `layer.createDynamic(num, x, y)` 或 `layer.transferToDynamic(x, y, keepEvent?)`;遍历用 `iterateDynamicTiles()` 与 `getDynamicTilesAt(x, y)`。
|
||||
3. **双向转换**:`IStaticTile.toDynamic()`;`IDynamicTile.toStatic()` 与 `toStaticIfSafe()`;图层级 `transferToDynamic` / `transferToStatic` / `transferToStaticIfSafe`;`moveBlock` 是「转动态 → 移动 → 转回静态」的简写,不适合高级配置。写清 `keepEvent` 的默认值与语义:保留时把原图块事件复制到目标实例,转换后源位置事件被清空;不保留时静态图块回退到原始定义的默认事件。
|
||||
4. **事件的两类归属(单独成节)**:点事件 `pointEvent()` 属于**坐标**,通过 `layer.event(x, y)` / `getPointEvent(x, y)` 访问,同一位置换图块仍保留;图块事件 `tileEvent()` 属于**图块实例**,随转换/复制走。给出对照表:归属、获取方式、是否跟随图块移动、典型用途(踩踏/进入某格 vs「这块可推动的石头被推开后事件跟着走」)。
|
||||
5. **事件视图接口**:`IReadonlyEventView` 的 `get()` / `ref()` / `dirty()`;`ILayerEventView` 的 `set(priority, eventId)` / `delete(priority)` / `reset()` / `clear()` / `markPure()`;事件值是 `IGameEventStore` 的 id,键是优先级。说明 `markPure` 建立「干净基准」,`dirty` 用于决定是否写入存档。
|
||||
6. **在代码里操作事件**:给一段完整示例——读取某格静态图块、给它的图块事件添加一个优先级事件、再用 `syncTileEvent` 复制到另一块;再给一段点事件示例——给某坐标设置事件并在换图块后读取确认仍在。注释解释「优先级冲突会覆盖并告警」「事件 id 必须已注册」。
|
||||
7. **转换的失败条件**:`toStatic` 在坐标为非整数或正在移动时返回 `null`;`toStaticIfSafe` 额外要求目标点为空;`delete()` 是 `Promise`,删除后 `onDeleteDynamic` 钩子触发。说明 `onCreateDynamic` / `onUpdateDynamicPosition` 的触发时机。
|
||||
8. **怎么验证**:用 `layer.getTile(x, y)?.toDynamic()` 创建动态图块,`step(FaceDirection.Right)` 移动一格后 `toStaticIfSafe()`;预期原位置变空、目标位置出现静态图块,图块事件跟随,点事件留在原地。给出打印语句。
|
||||
9. **边界与注意**:静态图块的 `set()` 会重置默认事件;动态图块不写回图层数组;`keepEvent` 不保留时不要误以为事件丢失是 bug;同一格多个动态图块的迭代顺序不保证。
|
||||
按 D-59 叙述,完整类/调用示例,注释充足;加粗符合 flanking。完成后跑 prettier `--write docs/advanced/ii/static-vs-dynamic-tiles.md`。Commit:`docs(10-21): 编写高级 II 静态与动态图块`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/ii/static-vs-dynamic-tiles.md'; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; foreach($needle in @('IStaticTile','IDynamicTile','toDynamic','toStatic','toStaticIfSafe','transferToDynamic','keepEvent','pointEvent','tileEvent','ILayerEventView','syncTileEvent','markPure','怎么验证')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; if($raw -match 'src/client'){ Write-Error "RENDERER-CONTENT" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent $p) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '```ts'){ Write-Error "NO-TS-BLOCK" }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'TILE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "MISSING-CONTENT", "RENDERER-CONTENT", "DEAD-LINK", "NO-TS-BLOCK", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "TILE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>图块篇存在、CRLF、单 H1;静态/动态区分、创建、双向转换、`keepEvent`、点事件与图块事件对照、事件视图接口、失败条件、验证与边界齐全;代码块完整;无渲染端内容;无死链;构建通过。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 执行者 → 子代理 | 文档由子代理按任务书编写,执行者只验收 |
|
||||
| 构建期 markdown → 静态站点 | 正文由构建期静态渲染,无运行时输入 |
|
||||
| 站点 → 浏览器读者 | 站内链接是唯一可点击指向面 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-61 | Tampering / XSS | 新增两篇正文 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-62 | Spoofing | 站内链接与接口名 | low | mitigate | 每个相对 `.md` 链接断言目标存在;接口名逐一核对源码 |
|
||||
| T-10-63 | Information Disclosure | 正文 | low | mitigate | 只引用仓库已公开路径与接口;不写密钥、账号 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 命名门禁:目录与两篇文件名与 Task 0 获批基线一致。
|
||||
- 内容门禁:范围篇覆盖 `IRange`/宿主/内置范围/自定义范围/绑定系统;图块篇覆盖静态与动态、双向转换、`keepEvent`、点事件与图块事件。
|
||||
- 风格门禁:两篇按 D-59 库文档写,完整代码块、注释充足、主干与边界分离;无中级六步实操。
|
||||
- 边界门禁:无 `src/client` 内容;高级 III 引用只用代码 span 或 `TODO(advanced-III)` 注释。
|
||||
- 结构门禁:kebab-case、CRLF、单 H1;相对链接全部可解析。
|
||||
- 构建门禁:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` 通过,`build complete`,dead link = 0。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 两篇高级 II 基础文档可读、可构建、与源码一致。
|
||||
- 范围对象与图块事件成为后续六篇可直接引用的公共基础。
|
||||
- 未越界修改 `config.ts`、生产代码与渲染端内容。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-21-SUMMARY.md` when done
|
||||
</output>
|
||||
176
.planning/phases/10-docs-restructure/10-22-PLAN.md
Normal file
176
.planning/phases/10-docs-restructure/10-22-PLAN.md
Normal file
@ -0,0 +1,176 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 22
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- docs/advanced/ii/equipment-system.md
|
||||
- docs/advanced/ii/custom-replay-command.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 32000
|
||||
raw_tokens: 21000
|
||||
tasks: 3
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "本计划在 Task 0 汇报两篇文档与文件名,并等待「可以执行」;文件名须与 10-21 Task 0 获批基线一致(D-13)"
|
||||
- "`equipment-system.md` 讲清装备实例仓、装备实例的修饰器、装备槽与 `compareEquip`、排序器与装备事件;澄清「装备修饰器不进属性存档、由重新装备恢复」这一关键事实"
|
||||
- "`custom-replay-command.md` 讲清录像指令码、`IReplayCommand`、主动/被动、注册位置与沙盒播放;明确新增指令码属于修改 `packages-user` 引擎内容(D-43)"
|
||||
- "两篇均按 D-59 库文档风格写:完整代码块、解释性注释充足、主干与边界分离;不写中级六步实操,不复述中级已讲清的基础调用"
|
||||
- "只写数据端内容;渲染端播放表现、编辑器与状态栏相关内容用 D-47 非断链占位,不写 `src/client` 路径"
|
||||
- "两篇均 kebab-case、CRLF、单 H1;无指向不存在页面的相对链接;构建 green、`build complete`、dead link = 0"
|
||||
artifacts:
|
||||
- "docs/advanced/ii/equipment-system.md"
|
||||
- "docs/advanced/ii/custom-replay-command.md"
|
||||
- ".planning/phases/10-docs-restructure/10-22-SUMMARY.md"
|
||||
key_links:
|
||||
- "装备修饰器依赖 `attribute.addModifier` 与 `ValueModifier` / `PercentageModifier`,为 10-23 的勇士属性篇铺路"
|
||||
- "录像指令的 `@shouldReplay` 与高级 I 的 `replay-consistency.md` 相接,本篇只讲自定义指令本身"
|
||||
- "10-25 统一把两篇接入侧边栏;本计划不修改 `config.ts`"
|
||||
---
|
||||
|
||||
<objective>
|
||||
编写高级 II 的装备篇与录像指令篇:`equipment-system.md`(勇士的装备系统)与 `custom-replay-command.md`(自定义录像指令)。
|
||||
|
||||
Purpose: 装备与录像都是「玩家行为必须可复现」的系统。装备篇要讲清实例仓、修饰器、排序器与对比接口;录像篇要讲清如何新增一条会被沙盒正确回放的指令。两篇都落在数据端,但都会牵动存档与一致性。
|
||||
Output: 两篇可构建的 `docs/advanced/ii/*.md` 正文。
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/13194/.config/opencode/gsd-core/workflows/execute-plan.md
|
||||
@C:/Users/13194/.config/opencode/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/phases/10-docs-restructure/10-CONTEXT.md
|
||||
@docs/advanced/index.md
|
||||
@docs/advanced/replay-consistency.md
|
||||
@docs/advanced/saveable-content.md
|
||||
@docs/intermediate/hero-equipment.md
|
||||
@docs/intermediate/hero-attribute.md
|
||||
@docs/intermediate/replay-commands.md
|
||||
@packages-user/data-base/src/hero/equipment.ts
|
||||
@packages-user/data-base/src/hero/equipStore.ts
|
||||
@packages-user/data-base/src/hero/modifier.ts
|
||||
@packages-user/data-base/src/hero/attribute.ts
|
||||
@packages-user/data-base/src/hero/types.ts
|
||||
@packages-user/data-common/src/replay/types.ts
|
||||
@packages-user/data-common/src/replay/system.ts
|
||||
@packages-user/data-common/src/replay/sandbox.ts
|
||||
@src/data/replay/commands.ts
|
||||
@src/data/replay/initialize.ts
|
||||
|
||||
**写作规范(D-53..D-66):** 库文档风格(D-59);简洁、不重复中级(D-60);主干与边界分离(D-61);代码块结构完整(D-62);先理念再模板(D-63);只在需要实现接口时给模板(D-64);注释充足(D-65);并列功能分开示例(D-66);不写中级「改内置还是自己写」提示(D-58);需要改内置时说明优先插件(D-55)。
|
||||
|
||||
**源码事实(写作时以此为准):**
|
||||
- 装备实例仓 `HeroEquipsStore`:`add(item)` 返回 uid;`delete(uid)`;`get(uid)`;`instancesOf(equip)` / `instances()` 按排序器输出;`count(item)`;`useSorter(comparer | null)`。
|
||||
- `EquipmentState`:`uid`、`item`、`setValue` / `setPercentage`、`getValue` / `getPercentage`、`buildModifiers()`、`getModifiers()`;钩子 `IEquipmentStateHooks.onChangeModifier`。
|
||||
- `HeroEquipment`:`slots`、`canEquipTo` → `EquipStatus.CanEquip` / `CannotEquip` / `NeedReplace`、`equip(uid, slot, autoUnload)`、`unequip(slot)`、`equipped`、`getEquipped`、`getEquips`、`compareEquip`。
|
||||
- 装备修饰器通过 `attribute.addModifier(name, modifier, false)` 进入属性,第三个参数表示不进属性存档;读档时 `HeroEquipment.loadState` 先清空再重新装备恢复,期间 `replaySystem.disable()` / `revert()`。
|
||||
- `IEquipmentSorter.compare(handler)` 返回负值 A 在前、正值 B 在前、0 按 uid 升序。
|
||||
- 录像:`ReplayCode` 是 `packages-user/data-common` 中的 `const enum`,数值属于录像格式;`IReplayCommand` 有 `type`、`execute(step)`、可选 `finalize()`;`ReplayCommandResult` 为 `Success` / `Failed` / `Ignored`;注册用 `replaySystem.registerCommand(code, command)`。
|
||||
- 指令基类 `BaseReplayCommand` 已做参数数量与类型校验,子类实现 `wrappedExecute`;`paramTypes` 是类型名字符串数组。
|
||||
- 沙盒:`createReplaySandbox({ route, reseter, startIndex?, save? })`;`play` / `step` / `pause` / `resume` / `stop`;被动录像通过 `sandbox.getPassive()` 取得,必须调用 `next(status)` 才推进。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 汇报装备篇与录像篇计划并等待「可以执行」</name>
|
||||
<action>向用户汇报:**要做什么**(编写 `equipment-system.md` 与 `custom-replay-command.md`)、**为什么**(D-67 把装备系统与自定义录像指令列为高级 II 的深入定制主题;两者都直接牵动存档与录像一致性)、**大致怎么做**(装备篇按「实例仓 → 实例与修饰器 → 装备槽与行为 → 对比与排序 → 装备事件」组织;录像篇按「指令码 → 指令对象 → 主动/被动 → 注册 → 沙盒」组织;两篇都给完整示例与验证段)、**影响范围**(新增两篇文档;不修改 `config.ts`、生产代码与渲染端内容)。请用户确认两篇文件名与 10-21 的命名基线一致,并说明:若 10-21 已批准整套命名,本计划沿用;若用户尚未批准,请在此一并确认。汇报中明确:本计划的修改方式为**派发子代理执行**,范围严格限于 `files_modified` 列出的两篇文档。停下等待用户明确回复「可以执行」四个字;未收到前不得写任何文件、不得派发子代理。</action>
|
||||
<what-built>本计划内容:高级 II 装备系统篇与自定义录像指令篇。</what-built>
|
||||
<how-to-verify>阅读上面的四段说明,确认两篇文件名;如对装备事件或录像示例的落点有要求,请一并说明。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<verify>
|
||||
<human-check>用户已确认两篇文件名并回复「可以执行」。</human-check>
|
||||
</verify>
|
||||
<done>用户确认文件名并回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 编写勇士的装备系统 equipment-system.md</name>
|
||||
<files>docs/advanced/ii/equipment-system.md</files>
|
||||
<action>写 `docs/advanced/ii/equipment-system.md`,单 H1「勇士的装备系统」。正文顺序:
|
||||
1. **装备与道具的区别**:装备是 `ItemCategory.Equipment`,但运行时由独立系统管理;道具表只提供原始定义 `IItemRawData.equip`(`slots`、`value`、`percentage`、`loadEvent`、`unloadEvent`)。说明为什么装备需要「实例」:同一件装备可以有多份,每份可以有不同的强化数值。
|
||||
2. **实例仓**:`IHeroEquipsStore` 的 `add(item)` 返回 uid、`delete(uid)`、`get(uid)`、`instancesOf(equip)`、`instances()`、`count(item)`;`add` 的参数可以是图块数字或字符串 id。给出「发放一件装备并拿到 uid」的完整示例。
|
||||
3. **装备实例的修饰器**:`EquipmentState` 的 `setValue` / `setPercentage` 会更新内部修饰器;`getModifiers()` 输出 `[属性名, 修饰器]`;修饰器类型是 `ValueModifier` 与 `PercentageModifier`。说明 `percentage` 的 `modify` 是「基础值 × 百分比」,与 `ValueModifier` 的加算不同。给出「给实例加 5 点攻击、10% 防御」的示例并打印最终属性。
|
||||
4. **装备槽与装备行为**:`slots` 是字符串数组;`canEquipTo(uid, slot)` 返回 `EquipStatus`,slot 可传索引或名称;`equip(uid, slot, autoUnload)` 返回被替换下来的 uid;`unequip(slot)`;`equipped` / `getEquipped` / `getEquips`。说明 `autoUnload` 的语义(同一件装备已戴在别的槽时是否自动卸下)。
|
||||
5. **修饰器与存档的关键约定**:装备修饰器以 `save = false` 加入勇士属性,因此**不进入属性存档**;读档时 `HeroEquipment.loadState` 先清空装备映射,再按存档重新 `equip`,由此恢复修饰器。强调:如果自己绕过 `equip` 直接往属性上加装备修饰器,读档后会不一致。给出正确的发放/穿戴顺序。
|
||||
6. **装备对比**:`compareEquip(equipA, equipB, slot)` 返回「A 相对 B」的属性差;`-1` 表示该侧无装备;内部用属性克隆试算,不改变真实属性。给出一段对比两件武器攻击/防御差的示例,并说明结果可能只包含有差异的键。
|
||||
7. **排序器**:`IEquipmentSorter.compare(handler)` 的 `handler` 含 `equipA` / `equipB` / `store` / `state`;返回负值 A 在前、正值 B 在前、0 按 uid。`store.useSorter(sorter)` 后 `instances()` / `instancesOf()` 按此排序。给出一个「按攻击加成降序」的排序器完整类。
|
||||
8. **装备事件**:`IEquipmentStateHooks.onChangeModifier(modifiers)` 在修饰器增加、删除、数值修改时触发,适合做「装备强化后重算某系统」;`IItemRawData.equip.loadEvent` / `unloadEvent` 目前只是数据字段,引擎不会自动执行,需要事件时用 `onChangeModifier` 或在调用 `equip` / `unequip` 的地方自行处理。不要把它写成已内置的自动钩子。
|
||||
9. **怎么验证**:发一件装备 → 穿戴 → 打印 `hero.attribute.getFinalAttribute('atk')` → 卸下 → 再打印;预期差值等于装备的 `value` 加 `percentage` 计算值。再读档一次,确认差值仍在。
|
||||
10. **边界与注意**:`add` 失败返回 `-1`;`canEquipTo` 为 `CannotEquip` 时 `equip` 直接返回;`compareEquip` 两侧都不存在时返回空对象并告警;排序器只影响展示顺序,不影响装备槽。
|
||||
按 D-59 叙述,完整类/调用示例,注释充足;加粗符合 flanking。完成后跑 prettier `--write docs/advanced/ii/equipment-system.md`。Commit:`docs(10-22): 编写高级 II 勇士装备系统`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/ii/equipment-system.md'; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; foreach($needle in @('IHeroEquipsStore','EquipmentState','instancesOf','IHeroEquipment','canEquipTo','EquipStatus','compareEquip','IEquipmentSorter','useSorter','onChangeModifier','ValueModifier','PercentageModifier','getFinalAttribute','怎么验证')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; if($raw -match 'src/client'){ Write-Error "RENDERER-CONTENT" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent $p) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '```ts'){ Write-Error "NO-TS-BLOCK" }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'EQUIP-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "MISSING-CONTENT", "RENDERER-CONTENT", "DEAD-LINK", "NO-TS-BLOCK", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "EQUIP-OK"</fails_when>
|
||||
</verify>
|
||||
<done>装备篇存在、CRLF、单 H1;实例仓、实例修饰器、装备槽、存档约定、`compareEquip`、排序器、装备事件、验证与边界齐全;代码块完整;无渲染端内容;无死链;构建通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 编写自定义录像指令 custom-replay-command.md</name>
|
||||
<files>docs/advanced/ii/custom-replay-command.md</files>
|
||||
<action>写 `docs/advanced/ii/custom-replay-command.md`,单 H1「自定义录像指令」。正文顺序:
|
||||
1. **什么时候需要自定义指令**:玩家直接产生、会影响最终成绩的行为必须进入录像;高级 I 的 `replay-consistency.md` 讲了「给已有方法补标记」,本篇讲「行为本身就不在引擎里,需要一条新指令」。回指高级 I 篇,不重复标记规则。
|
||||
2. **指令码是格式的一部分**:`ReplayCode` 位于 `packages-user/data-common/src/replay/types.ts`,是 `const enum`,数值只增不改;新增指令码意味着修改 `packages-user` 引擎内容,升级时要有合并成本的预期(D-43)。`ReplayCommandWidth.Uint8` 下指令码范围是 0–255,超出要切 `Uint16`。
|
||||
3. **指令对象**:`IReplayCommand` 的 `type`、`execute(step)`、可选 `finalize()`;`step` 提供 `command` / `params` / `index`;返回值是 `ReplayCommandResult`。解释 `finalize` 的用途:连续多步同指令时,在最后一步之后统一收尾(例如连续移动后再启动一次动画)。
|
||||
4. **主动录像与被动录像**:`ReplayCommandType.Active` 自己执行逻辑;`Passive` 不自己执行,由外部通过 `IReplaySandbox.getPassive()` 取得参数,处理完后必须调用 `next(status)` 才会推进。说明被动录像适合「需要渲染端配合、耗时不可预测」的行为;渲染端具体表现按 D-47 留占位,不写实现。
|
||||
5. **用基类减少样板**:`BaseReplayCommand` 已实现参数数量与类型校验,子类只需给 `name`、`paramTypes`、`wrappedExecute`;给出完整主动指令类示例——`ReplayUseSkill`,参数 `[number skillId, number x, number y]`,在 `wrappedExecute` 中校验技能是否可用并执行,成功后返回 `Success`,失败返回 `Failed`。注释解释「参数校验失败基类会直接返回 `Failed`」「不要在 `wrappedExecute` 里再重复校验参数类型」。
|
||||
6. **注册**:在 `src/data/replay/initialize.ts` 的 `initializeReplay` 中 `replay.registerCommand(ReplayCode.UseSkill, new ReplayUseSkill(state))`;说明注册必须在沙盒播放前完成,且同一指令码重复注册会覆盖。给出注册片段与 `ReplayCode` 新增项。
|
||||
7. **记录**:在真正执行该行为的入口方法上加 `@shouldReplay('...')`,并在方法内用 `state.replaySystem.array.add(ReplayCode.UseSkill, [skillId, x, y])` 或 `replaySystem.record(...)` 记录;强调不要在被动触发路径里重复记录(必要时 `disable` / `revert`)。高级 I 已讲标记规则,这里只给与自定义指令配合的写法。
|
||||
8. **沙盒播放**:`createReplaySandbox({ route, reseter, startIndex, save })` 创建沙盒;`play` / `step` / `pause` / `resume` / `stop` 控制播放;`getReplayed()` 取得已播放步数;`onStep` 钩子可做逐步观察。说明 `reseter` 负责把游戏状态重置到起始存档,否则回放会叠加在当前状态上。
|
||||
9. **怎么验证**:给出最小验证流程——构造一条只含 `UseSkill` 的录像数组,创建沙盒播放一步,确认技能效果发生且 `getReplayed()` 为 1;再播放到结束,确认 `ended` 为 `true`。如果技能依赖地图,说明先准备固定地图与固定英雄属性。
|
||||
10. **边界与注意**:指令码不要复用已删除的旧值;参数类型必须与 `paramTypes` 一致(`number` / `string` / `boolean` / `bigint`);被动指令忘记 `next` 会卡住;`finalize` 返回 `Failed` 会中止后续播放;沙盒播放期间不要再次记录录像。
|
||||
按 D-59 叙述,完整类/调用示例,注释充足;加粗符合 flanking。完成后跑 prettier `--write docs/advanced/ii/custom-replay-command.md`。Commit:`docs(10-22): 编写高级 II 自定义录像指令`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/ii/custom-replay-command.md'; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; foreach($needle in @('IReplayCommand','ReplayCommandType','ReplayCommandResult','registerCommand','BaseReplayCommand','paramTypes','finalize','IReplaySandbox','getPassive','createReplaySandbox','ReplayCode','shouldReplay','ReplayCommandWidth','怎么验证')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; if($raw -match 'src/client'){ Write-Error "RENDERER-CONTENT" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent $p) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '```ts'){ Write-Error "NO-TS-BLOCK" }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'REPLAY-CMD-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "MISSING-CONTENT", "RENDERER-CONTENT", "DEAD-LINK", "NO-TS-BLOCK", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "REPLAY-CMD-OK"</fails_when>
|
||||
</verify>
|
||||
<done>录像指令篇存在、CRLF、单 H1;指令码约束、`IReplayCommand`、主动/被动、基类、注册、记录、沙盒、验证与边界齐全;代码块完整;无渲染端内容;无死链;构建通过。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 执行者 → 子代理 | 文档由子代理按任务书编写,执行者只验收 |
|
||||
| 构建期 markdown → 静态站点 | 正文由构建期静态渲染,无运行时输入 |
|
||||
| 站点 → 浏览器读者 | 站内链接是唯一可点击指向面 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-64 | Tampering / XSS | 新增两篇正文 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-65 | Spoofing | 站内链接与接口名 | low | mitigate | 相对链接断言目标存在;`ReplayCode` 数值与注册位置核对源码 |
|
||||
| T-10-66 | Repudiation | 录像一致性叙述 | low | mitigate | 明确指令码只增不改、注册必须在播放前;不把未实现行为写成已内置 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 命名门禁:两篇文件名与 10-21 获批基线一致。
|
||||
- 内容门禁:装备篇覆盖实例仓、实例修饰器、装备槽、存档约定、对比、排序器、装备事件;录像篇覆盖指令码、指令对象、主动/被动、注册、记录、沙盒。
|
||||
- 事实门禁:装备修饰器不进属性存档、读档重新装备恢复;`loadEvent` / `unloadEvent` 不由引擎自动执行;指令码只增不改——三处不得写反。
|
||||
- 边界门禁:无 `src/client` 内容;渲染端播放表现只用占位。
|
||||
- 结构门禁:kebab-case、CRLF、单 H1;相对链接全部可解析。
|
||||
- 构建门禁:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` 通过,`build complete`,dead link = 0。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 读者能按装备篇正确发放、穿戴、对比装备并理解读档恢复路径。
|
||||
- 读者能按录像篇新增一条主动指令并完成注册与最小回放验证。
|
||||
- 未越界修改 `config.ts`、生产代码与渲染端内容。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-22-SUMMARY.md` when done
|
||||
</output>
|
||||
173
.planning/phases/10-docs-restructure/10-23-PLAN.md
Normal file
173
.planning/phases/10-docs-restructure/10-23-PLAN.md
Normal file
@ -0,0 +1,173 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 23
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- docs/advanced/ii/pathfinding.md
|
||||
- docs/advanced/ii/adding-hero-attribute.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 32000
|
||||
raw_tokens: 21000
|
||||
tasks: 3
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "本计划在 Task 0 汇报两篇文档与文件名,并等待「可以执行」;文件名须与 10-21 Task 0 获批基线一致(D-13)"
|
||||
- "`pathfinding.md` 只讲流程:绑定移动器 → 构建图(图层、通行谓词、损失函数、方向组)→ 求最短路 → 交给移动器;不讲 Dijkstra 内部实现"
|
||||
- "`adding-hero-attribute.md` 讲清修改 `IHeroAttr` 后必须同步的默认值与内容配置,并用攻速(每回合造成 `atkSpeed%` 伤害)与护甲(百分比减伤)两个例子接入伤害计算器"
|
||||
- "属性篇的编辑器新增项与状态栏显示用 D-47 非断链占位,不写渲染端实现"
|
||||
- "两篇均按 D-59 库文档风格写:完整代码块、解释性注释充足、主干与边界分离;不写中级六步实操"
|
||||
- "两篇均 kebab-case、CRLF、单 H1;无指向不存在页面的相对链接;构建 green、`build complete`、dead link = 0"
|
||||
artifacts:
|
||||
- "docs/advanced/ii/pathfinding.md"
|
||||
- "docs/advanced/ii/adding-hero-attribute.md"
|
||||
- ".planning/phases/10-docs-restructure/10-23-SUMMARY.md"
|
||||
key_links:
|
||||
- "寻路篇连接到 `src/data/hook/path.ts` 的瞬移钩子,与勇士位置移动系统相接"
|
||||
- "勇士属性篇是 10-24 怪物属性篇的对照:同一套「改接口 → 补默认值 → 接入系统」流程"
|
||||
- "10-25 统一把两篇接入侧边栏;本计划不修改 `config.ts`"
|
||||
---
|
||||
|
||||
<objective>
|
||||
编写高级 II 的寻路篇与勇士属性篇:`pathfinding.md`(自动寻路)与 `adding-hero-attribute.md`(新增勇士属性)。
|
||||
|
||||
Purpose: 寻路是「流程型」主题,读者只需要知道各段由谁负责、在哪里替换;新增属性是「系统型」主题,读者需要改接口、补默认值、再接进伤害计算器。两篇都要求读者理解系统边界,而不是背接口。
|
||||
Output: 两篇可构建的 `docs/advanced/ii/*.md` 正文。
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/13194/.config/opencode/gsd-core/workflows/execute-plan.md
|
||||
@C:/Users/13194/.config/opencode/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/phases/10-docs-restructure/10-CONTEXT.md
|
||||
@docs/advanced/index.md
|
||||
@docs/advanced/declarative-vs-imperative.md
|
||||
@docs/intermediate/hero-location.md
|
||||
@docs/intermediate/hero-attribute.md
|
||||
@docs/intermediate/enemy-calculator.md
|
||||
@packages-user/data-system/src/path/types.ts
|
||||
@packages-user/data-system/src/path/system.ts
|
||||
@packages-user/data-system/src/path/finder.ts
|
||||
@packages-user/data-system/src/path/graph.ts
|
||||
@packages-user/data-common/src/types.ts
|
||||
@packages-user/data-base/src/hero/attribute.ts
|
||||
@packages-user/data-base/src/hero/modifier.ts
|
||||
@packages-user/data-base/src/hero/types.ts
|
||||
@packages-user/data-state/src/shared.ts
|
||||
@src/data/initialize.ts
|
||||
@src/data/hook/path.ts
|
||||
@src/data/enemy/calculator.ts
|
||||
@src/content/core.jsonc
|
||||
|
||||
**源码事实(写作时以此为准):**
|
||||
- 寻路系统挂在 `state.pathfinding`(`IPathfindingSystem`):`finder`、`mover`、`getPath`、`moveTo`、`teleportTo`、`interrupt`、`useMover`、`useFallbackPolicy`。
|
||||
- `IPathFinder` 的配置入口:`useMapLayer`、`useCostFunction`、`usePassPredicate`、`useFaceHandler`;`find(start, target)` 返回 `IPathfindingResult`,状态为 `Success` / `InvalidInput` / `NoPath` / `TargetUnder`。
|
||||
- `moveTo` 对每一步调用 `mover.step(step.dir)` 后 `mover.start()`;`teleportTo` 在策略允许时直接 `mover.tp(last.to.x, last.to.y)`;瞬移前后触发 `onBeforeTeleportTo` / `onAfterTeleportTo`,钩子实现见 `src/data/hook/path.ts`。
|
||||
- 初始化在 `src/data/initialize.ts`:`usePassPredicate(predicate)`、`useMapLayer(null)`、`pathfinding.useMover(state.hero.location.mover)`。
|
||||
- 勇士属性接口 `IHeroAttr` 在 `packages-user/data-common/src/types.ts`;默认值 `HERO_DEFAULT_ATTRIBUTE` 在 `packages-user/data-state/src/shared.ts`;实际初始值来自 `src/content/core.jsonc` 的 `init.hero.attribute`,由 `state.hero.createAttribute(...)` 创建。
|
||||
- 属性读写:`getBaseAttribute(name)` / `getFinalAttribute(name)`;修饰器 `addModifier(name, modifier, save?)`;内置 `ValueModifier` 与 `PercentageModifier`(后者按基础值计算百分比)。
|
||||
- 伤害计算器 `IDamageCalculator.calculate(handler)` 返回 `{ damage, turn }`,实现位于 `src/data/enemy/calculator.ts`。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 汇报寻路篇与勇士属性篇计划并等待「可以执行」</name>
|
||||
<action>向用户汇报:**要做什么**(编写 `pathfinding.md` 与 `adding-hero-attribute.md`)、**为什么**(D-67 把自动寻路与新增勇士属性列为高级 II 的深入定制主题;前者讲系统分工,后者讲「改接口 → 补默认值 → 接入计算」的完整链路)、**大致怎么做**(寻路篇按「绑定移动器 → 构建图 → 求路径 → 交给移动器」讲流程,不展开算法;属性篇以攻速与护甲为例,改 `IHeroAttr`、补默认值与内容配置、改伤害计算器,编辑器和状态栏留占位)、**影响范围**(新增两篇文档;不修改 `config.ts`、生产代码与渲染端内容)。请用户确认两篇文件名与 10-21 基线一致,并确认属性示例采用「攻速 + 护甲」这组(D-67 暂定)。汇报中明确:本计划的修改方式为**派发子代理执行**,范围严格限于 `files_modified` 列出的两篇文档。停下等待用户明确回复「可以执行」四个字;未收到前不得写任何文件、不得派发子代理。</action>
|
||||
<what-built>本计划内容:高级 II 自动寻路篇与新增勇士属性篇。</what-built>
|
||||
<how-to-verify>阅读上面的四段说明,确认文件名与「攻速 + 护甲」示例;如希望换成别的属性例子,请直接说明。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<verify>
|
||||
<human-check>用户已确认两篇文件名与属性示例并回复「可以执行」。</human-check>
|
||||
</verify>
|
||||
<done>用户确认文件名与示例并回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 编写自动寻路 pathfinding.md</name>
|
||||
<files>docs/advanced/ii/pathfinding.md</files>
|
||||
<action>写 `docs/advanced/ii/pathfinding.md`,单 H1「自动寻路」。正文顺序:
|
||||
1. **寻路系统的职责**:它只负责「从 A 到 B 怎么走」,不负责移动动画本身;真正移动由移动器执行。参与对象:`IPathfindingSystem`、`IPathFinder`、`IMapGraphBuilder`、`IPassPredicate`、`IObjectMover`。
|
||||
2. **完整流程(主干)**:用一张编号列表讲清四段——① 绑定移动器(`useMover`);② 构建图(`useMapLayer` 选图层,`usePassPredicate` 判通行,`useCostFunction` 给每格代价,`useFaceHandler` 定邻域方向);③ 求最短路径(`finder.find(start, target)` 返回 `IPathfindingResult`);④ 交给移动器(`moveTo` 逐步 `step` 后 `start`;`teleportTo` 直接 `tp` 到终点)。附一张 mermaid 流程图,节点只写职责名,不写算法细节。
|
||||
3. **结果状态**:`PathfindingStatus.Success` / `InvalidInput` / `NoPath` / `TargetUnder` 的含义与处理建议;`path` 只在 `Success` 时非空;每一步是 `IPathfindingStep`,含 `dir` / `from` / `to`。
|
||||
4. **`moveTo` 与 `teleportTo`**:`moveTo` 总是逐步移动;`teleportTo` 会先询问 `useFallbackPolicy`,策略返回 `true` 表示不允许瞬移、退回逐步;未设置策略时必定逐步。说明 `IPathfindingController` 的 `controller.onEnd` 可用于等待移动结束,`interrupt()` 可中止当前移动。
|
||||
5. **初始化在哪里**:`src/data/initialize.ts` 已把通行谓词、图层与勇士移动器接好;读者通常只需要改损失函数或策略,不需要重建系统。给出「初始化后替换损失函数」的片段。
|
||||
6. **自定义损失函数**:`PathCostFunction` 接收 `ILayerLocation`,返回进入该格的代价;默认每格 1;`Infinity` 合法表示禁行,`NaN` 或负数会告警并回退为 1。给出一个完整示例——沼泽代价 3、普通地面 1、墙 `Infinity`,并说明「代价只影响选路,不改变通行判定」。
|
||||
7. **自定义瞬移策略**:`PathFallbackPolicy` 接收完整路径,返回 `true` 表示禁止瞬移;给出示例——路径超过 20 步或经过某类图块时改为逐步走。说明策略只在 `teleportTo` 生效。
|
||||
8. **瞬移钩子**:`onBeforeTeleportTo` / `onAfterTeleportTo` 在 `src/data/hook/path.ts` 中已留好空实现;给出在瞬移前后做记录或补触发的示例,并提醒「瞬移也会触发,不只是普通寻路」。
|
||||
9. **怎么验证**:给出一段可执行检查——`state.pathfinding.getPath({ x, y })` 打印 `status` 与步数;再 `moveTo` 并 `await` 控制器结束;预期英雄沿最短路径到达,`NoPath` 时不会移动。若目标点不可达,先检查 `usePassPredicate` 与图层。
|
||||
10. **边界与注意**:`getPath` 在未绑定移动器时返回 `InvalidInput`;目标点在脚下返回 `TargetUnder`;连续调用 `moveTo` 时若上一次未结束会返回 `null`;寻路使用事件层还是其它图层由 `useMapLayer` 决定,换楼层后要重新绑定。
|
||||
按 D-59 叙述,完整类/函数示例,注释充足;明确「不展开 Dijkstra 内部」;加粗符合 flanking。完成后跑 prettier `--write docs/advanced/ii/pathfinding.md`。Commit:`docs(10-23): 编写高级 II 自动寻路`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/ii/pathfinding.md'; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; foreach($needle in @('IPathfindingSystem','getPath','moveTo','teleportTo','useMover','useFallbackPolicy','IPathFinder','IPassPredicate','PathCostFunction','PathfindingStatus','onBeforeTeleportTo','怎么验证')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; if($raw -match 'src/client'){ Write-Error "RENDERER-CONTENT" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent $p) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '```ts'){ Write-Error "NO-TS-BLOCK" }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'PATH-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "MISSING-CONTENT", "RENDERER-CONTENT", "DEAD-LINK", "NO-TS-BLOCK", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "PATH-OK"</fails_when>
|
||||
</verify>
|
||||
<done>寻路篇存在、CRLF、单 H1;四段流程、结果状态、`moveTo`/`teleportTo`、初始化、损失函数、瞬移策略、钩子、验证与边界齐全;不展开算法内部;无渲染端内容;无死链;构建通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 编写新增勇士属性 adding-hero-attribute.md</name>
|
||||
<files>docs/advanced/ii/adding-hero-attribute.md</files>
|
||||
<action>写 `docs/advanced/ii/adding-hero-attribute.md`,单 H1「新增勇士属性」。正文顺序:
|
||||
1. **新增属性意味着什么**:属性不只是数据字段,它会被道具、装备、修饰器、伤害计算与状态栏读取;新增一个属性要同时考虑「类型、默认值、初始值、计算接入、显示」五处。说明本篇只写数据端,显示与编辑器按 D-47 留占位。
|
||||
2. **改接口**:在 `packages-user/data-common/src/types.ts` 的 `IHeroAttr` 增加字段,示例为 `atkSpeed`(攻速,百分比)与 `armor`(护甲,百分比减伤),并写 jsDoc 注释说明取值范围与语义。强调字段名一旦定下会被存档、内容与插件引用,要按 D-13 先确认命名。
|
||||
3. **补默认值与初始值**:`packages-user/data-state/src/shared.ts` 的 `HERO_DEFAULT_ATTRIBUTE` 必须补齐新字段;`src/content/core.jsonc` 的 `init.hero.attribute` 也要补,否则 `createAttribute` 拿到的对象缺键,读取会得到 `undefined` 并在数值运算中变成 `NaN`。给出两段完整对象片段。
|
||||
4. **读取与修饰**:`getBaseAttribute` / `getFinalAttribute` 的区别;通过 `addModifier(name, modifier, save?)` 挂 `ValueModifier` / `PercentageModifier`;给出「某装备给 15% 攻速」的示例,并说明 `save` 的取舍——装备修饰器应由装备系统负责恢复,不要重复写入属性存档(回指 `equipment-system.md`)。
|
||||
5. **接入伤害计算(主干示例)**:先说明 `IDamageCalculator.calculate(handler)` 返回 `{ damage, turn }`,内置实现在 `src/data/enemy/calculator.ts`;给出修改思路——攻速让每回合伤害按 `atkSpeed / 100` 缩放,护甲让最终伤害乘 `1 - armor / 100`(夹在 0 到 1 之间)。给出一段完整的计算器类骨架,注释标明 `[需改]` 的两处;说明「先算基础伤害,再乘攻速,再乘护甲减免」的顺序,并提醒回合数要随每回合伤害同步计算。
|
||||
6. **与装备/道具的关系**:装备的 `value` / `percentage` 记录可以直接写新属性名;道具效果同理;不需要额外注册。给出一个 `IItemRawData.equip` 的完整片段,`value: { atkSpeed: 0 }`、`percentage: { atkSpeed: 15 }`。
|
||||
7. **编辑器与状态栏(D-47 占位)**:说明编辑器新增属性项与状态栏显示需要在对应端实现,当前不在本篇范围;用代码 span `编辑器 · 新增英雄属性项` 与 `渲染端 · 状态栏`,并加 `<!-- TODO(advanced-II): 编辑器属性项与状态栏显示 -->` 注释。不要写 `src/client` 实现。
|
||||
8. **怎么验证**:给出可执行检查——在 `core.jsonc` 把 `atkSpeed` 设为 100、`armor` 设为 0,打印 `state.hero.attribute.getFinalAttribute('atkSpeed')`;再穿一件 +15% 攻速的装备,确认最终值变为 115;最后用固定怪物调用 `getDamageInfo`,确认伤害随攻速变化、随护甲下降。说明需要先准备固定属性的怪物。
|
||||
9. **边界与注意**:所有创建英雄属性的位置都要补键(默认值、内容配置、测试夹具);`Partial<IHeroAttr>` 不会报缺键,但运行时仍会读到 `undefined`;百分比属性统一用「15 表示 15%」;不要把新属性写进 `moneys` 之类的既有计算;新增字段会影响存档结构,读旧档时要给缺失键兜底。
|
||||
按 D-59 叙述,完整对象/类示例,注释充足;加粗符合 flanking。完成后跑 prettier `--write docs/advanced/ii/adding-hero-attribute.md`。Commit:`docs(10-23): 编写高级 II 新增勇士属性`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/ii/adding-hero-attribute.md'; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; foreach($needle in @('IHeroAttr','HERO_DEFAULT_ATTRIBUTE','atkSpeed','armor','getFinalAttribute','addModifier','PercentageModifier','IDamageCalculator','calculate','编辑器','状态栏','TODO(','怎么验证')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; if($raw -match 'src/client'){ Write-Error "RENDERER-CONTENT" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent $p) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '```ts'){ Write-Error "NO-TS-BLOCK" }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'HERO-ATTR-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "MISSING-CONTENT", "RENDERER-CONTENT", "DEAD-LINK", "NO-TS-BLOCK", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "HERO-ATTR-OK"</fails_when>
|
||||
</verify>
|
||||
<done>勇士属性篇存在、CRLF、单 H1;五处改动、默认值与内容配置、读取与修饰、伤害计算接入、装备/道具衔接、D-47 占位、验证与边界齐全;代码块完整;无渲染端内容;无死链;构建通过。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 执行者 → 子代理 | 文档由子代理按任务书编写,执行者只验收 |
|
||||
| 构建期 markdown → 静态站点 | 正文由构建期静态渲染,无运行时输入 |
|
||||
| 站点 → 浏览器读者 | 站内链接是唯一可点击指向面 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-67 | Tampering / XSS | 新增两篇正文 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-68 | Spoofing | 属性字段与默认值 | low | mitigate | 字段名、`HERO_DEFAULT_ATTRIBUTE` 与 `core.jsonc` 三处逐一核对源码 |
|
||||
| T-10-69 | Information Disclosure | 正文 | low | mitigate | 只引用仓库已公开路径与接口;不写密钥、账号 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 命名门禁:两篇文件名与 10-21 获批基线一致;新增属性名在 Task 0 已向用户说明。
|
||||
- 内容门禁:寻路篇覆盖四段流程、状态、两种移动方式、损失函数、瞬移策略与钩子;属性篇覆盖类型、默认值、初始值、修饰、伤害计算与验证。
|
||||
- 边界门禁:寻路篇不展开算法内部;属性篇编辑器/状态栏只用 D-47 占位,无 `src/client` 内容。
|
||||
- 结构门禁:kebab-case、CRLF、单 H1;相对链接全部可解析。
|
||||
- 构建门禁:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` 通过,`build complete`,dead link = 0。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 读者能说清寻路各段由谁负责,并能替换损失函数或瞬移策略。
|
||||
- 读者能完整新增一个勇士属性并让它真正影响伤害计算,且知道显示与编辑器不在本篇范围。
|
||||
- 未越界修改 `config.ts`、生产代码与渲染端内容。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-23-SUMMARY.md` when done
|
||||
</output>
|
||||
179
.planning/phases/10-docs-restructure/10-24-PLAN.md
Normal file
179
.planning/phases/10-docs-restructure/10-24-PLAN.md
Normal file
@ -0,0 +1,179 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 24
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- docs/advanced/ii/adding-enemy-attribute.md
|
||||
- docs/advanced/ii/enemy-context-and-combat.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 34000
|
||||
raw_tokens: 22000
|
||||
tasks: 3
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "本计划在 Task 0 汇报两篇文档与文件名,并等待「可以执行」;文件名须与 10-21 Task 0 获批基线一致(D-13)"
|
||||
- "`adding-enemy-attribute.md` 讲清修改 `IEnemyAttr` 后要同步的默认模板与内容数据,并用攻速/护甲示例接入伤害、光环与查询"
|
||||
- "`enemy-context-and-combat.md` 讲清 buildup 管线、优先级、缓存与脏标记,以及伤害系统与光环/查询/最终的交互顺序"
|
||||
- "属性篇的编辑器新增项与怪物图鉴用 D-47 非断链占位;战斗篇不把未内置的查询效果写成已内置"
|
||||
- "两篇均按 D-59 库文档风格写:完整代码块、解释性注释充足、主干与边界分离;不写中级六步实操"
|
||||
- "两篇均 kebab-case、CRLF、单 H1;无指向不存在页面的相对链接;构建 green、`build complete`、dead link = 0"
|
||||
artifacts:
|
||||
- "docs/advanced/ii/adding-enemy-attribute.md"
|
||||
- "docs/advanced/ii/enemy-context-and-combat.md"
|
||||
- ".planning/phases/10-docs-restructure/10-24-SUMMARY.md"
|
||||
key_links:
|
||||
- "怪物属性篇与 10-23 的勇士属性篇构成对照:改接口 → 补数据 → 接入计算"
|
||||
- "怪物上下文篇是高级 II 的收束篇,统合光环、查询、最终、伤害与地图伤害的时序"
|
||||
- "10-25 统一把两篇接入侧边栏;本计划不修改 `config.ts`"
|
||||
---
|
||||
|
||||
<objective>
|
||||
编写高级 II 的怪物属性篇与战斗深入篇:`adding-enemy-attribute.md`(新增怪物属性)与 `enemy-context-and-combat.md`(怪物上下文与战斗深入)。
|
||||
|
||||
Purpose: 怪物属性是数据端最常改的扩展点之一,但只改字段不会自动影响战斗;必须先理解上下文构建与伤害缓存,才能把属性接到正确阶段。最后一篇把 buildup、优先级、脏标记与伤害系统串起来,作为高级 II 数据端的收束。
|
||||
Output: 两篇可构建的 `docs/advanced/ii/*.md` 正文。
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/13194/.config/opencode/gsd-core/workflows/execute-plan.md
|
||||
@C:/Users/13194/.config/opencode/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/phases/10-docs-restructure/10-CONTEXT.md
|
||||
@docs/advanced/index.md
|
||||
@docs/intermediate/enemy-specials.md
|
||||
@docs/intermediate/enemy-aura.md
|
||||
@docs/intermediate/enemy-query.md
|
||||
@docs/intermediate/enemy-final.md
|
||||
@docs/intermediate/enemy-calculator.md
|
||||
@docs/intermediate/enemy-map-damage.md
|
||||
@docs/intermediate/combat-overview.md
|
||||
@docs/advanced/ii/adding-hero-attribute.md
|
||||
@packages-user/data-common/src/types.ts
|
||||
@packages-user/data-base/src/enemy/enemy.ts
|
||||
@packages-user/data-base/src/enemy/types.ts
|
||||
@packages-user/data-system/src/combat/context.ts
|
||||
@packages-user/data-system/src/combat/damage.ts
|
||||
@packages-user/data-system/src/combat/types.ts
|
||||
@src/data/enemy/aura.ts
|
||||
@src/data/enemy/calculator.ts
|
||||
@src/data/enemy/initialize.ts
|
||||
@src/content/defaults/enemy.jsonc
|
||||
@src/content/enemy.jsonc
|
||||
|
||||
**源码事实(写作时以此为准):**
|
||||
- `IEnemyAttr` 在 `packages-user/data-common/src/types.ts`,当前含 `name` / `hp` / `atk` / `def` / `money` / `exp` / `point` / `guard`;`guard` 是支援来源的集合,不是内容文件里的普通数值。
|
||||
- 怪物模板是 `IEnemyRawData`(`num` + `attribute` + `special`);默认模板在 `src/content/defaults/enemy.jsonc`,实际内容在 `src/content/enemy.jsonc`。
|
||||
- `Enemy` 的 `getAttribute` / `setAttribute` / `addAttribute`;`IReadonlyEnemy` 只暴露 `getAttribute` 与 `cloneAttributes`。
|
||||
- `EnemyContext.buildup()` 的顺序是:视图 `reset` → `buildupSpecials`(全局光环 + 特殊属性转光环 + 按优先级处理特殊查询)→ `buildupBase`(按优先级从高到低执行基础光环)→ `buildupQuery`(常规查询)→ `buildupFinal`(最终效果);结束后把伤害系统与地图伤害全部标脏。
|
||||
- 优先级:数值越大越先执行;特殊属性产生的光环不能反向影响当前或更高的优先级;局部刷新 `refreshEnemy` 只重算命中该怪物的光环与查询。
|
||||
- 脏标记:`markDirty(view)` 把视图加入 `dirtyEnemy` 并同步伤害系统;`requestRefresh(view)` 决定全量 `buildup` 还是局部 `refreshEnemy`;`needTotallyRefresh` 表示必须全量重建。
|
||||
- 伤害系统:`IDamageSystem.getDamageInfo(view)` 用 `view.getComputedEnemy()` 计算;`IDamageCalculator.calculate(handler)` 返回 `{ damage, turn }`;`getCriticalLimit` 提供临界计算上界;`DamageSystem.markDirty` / `markAllDirty` 维护缓存。
|
||||
- 地图伤害按「计算后怪物」计算,因此光环新增的特殊属性会影响地图伤害;这一点在 `enemy-map-damage` 中级篇已提过,本篇只讲时序。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 汇报怪物属性篇与战斗深入篇计划并等待「可以执行」</name>
|
||||
<action>向用户汇报:**要做什么**(编写 `adding-enemy-attribute.md` 与 `enemy-context-and-combat.md`)、**为什么**(D-67 要求新增怪物属性接入战斗/光环/查询,并要求用一篇收束怪物上下文、优先级、缓存与伤害系统)、**大致怎么做**(属性篇按「改接口 → 补默认模板与内容 → 接入伤害/光环/查询 → 图鉴占位」组织;战斗篇按「上下文对象 → buildup 五段 → 优先级 → 脏标记 → 伤害系统交互」组织;两篇都给完整示例与验证段)、**影响范围**(新增两篇文档;不修改 `config.ts`、生产代码与渲染端内容)。请用户确认两篇文件名与 10-21 基线一致,并确认怪物属性示例同样采用「攻速 + 护甲」(D-67 暂定)。汇报中明确:本计划的修改方式为**派发子代理执行**,范围严格限于 `files_modified` 列出的两篇文档。停下等待用户明确回复「可以执行」四个字;未收到前不得写任何文件、不得派发子代理。</action>
|
||||
<what-built>本计划内容:高级 II 新增怪物属性篇与怪物上下文/战斗深入篇。</what-built>
|
||||
<how-to-verify>阅读上面的四段说明,确认文件名与「攻速 + 护甲」示例;如希望换例子或调整战斗篇深度,请直接说明。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<verify>
|
||||
<human-check>用户已确认两篇文件名与示例并回复「可以执行」。</human-check>
|
||||
</verify>
|
||||
<done>用户确认文件名与示例并回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 编写新增怪物属性 adding-enemy-attribute.md</name>
|
||||
<files>docs/advanced/ii/adding-enemy-attribute.md</files>
|
||||
<action>写 `docs/advanced/ii/adding-enemy-attribute.md`,单 H1「新增怪物属性」。正文顺序:
|
||||
1. **怪物属性在哪里**:`IEnemyAttr` 是所有怪物共享的属性形状;具体值来自 `IEnemyRawData.attribute`,模板在 `src/content/enemy.jsonc`,默认模板在 `src/content/defaults/enemy.jsonc`。说明 `guard` 是运行时由支援光环写入的集合,不应该在内容文件里手填。
|
||||
2. **改接口**:在 `packages-user/data-common/src/types.ts` 的 `IEnemyAttr` 增加 `atkSpeed`(攻速,百分比)与 `armor`(护甲,百分比减伤),写 jsDoc 说明语义与范围。强调字段名会被存档、内容、光环与查询引用,按 D-13 先确认命名。
|
||||
3. **补数据**:`src/content/defaults/enemy.jsonc` 的 `attribute` 补齐新字段;`src/content/enemy.jsonc` 的每个怪物条目也要补,否则读取到 `undefined` 会让伤害计算出现 `NaN`。给出两段完整 JSON 片段(默认模板与一个怪物条目)。
|
||||
4. **读取与修改**:`Enemy.getAttribute` / `setAttribute` / `addAttribute`;只读场景用 `IReadonlyEnemy.getAttribute`。给出「把某只怪物的护甲设为 20」的示例。
|
||||
5. **接入伤害计算**:`IDamageCalculator.calculate(handler)` 通过 `handler.enemy.getAttribute('atkSpeed')` / `'armor'` 读取计算后怪物;给出完整计算器骨架,说明攻速缩放每回合伤害、护甲按百分比减伤,并用 `Math.max` / `Math.min` 收口;强调计算的是 `getComputedEnemy()`,不是原始模板。
|
||||
6. **接入光环**:在 `IAuraView.apply(handler, baseEnemy)` 中用 `handler.enemy.addAttribute('armor', 10)` 给范围内怪物加护甲;说明光环修改的是计算后怪物,`baseEnemy` 用于读取原始值;具体转换器/视图写法回指 `docs/intermediate/enemy-aura.md`。
|
||||
7. **接入查询**:`IEnemyCommonQueryEffect.apply(handler, special, query)` 中通过 `query()` 取得上下文后统计周围怪物,再 `handler.enemy.addAttribute('atkSpeed', n)`;说明查询效果需要 `registerCommonQueryEffect` 注册,且引擎 `src` 未内置查询实现,示例只给最小骨架。特殊查询 `IEnemySpecialQueryEffect` 的写法回指中级查询篇,不重复。
|
||||
8. **接入最终效果**:`IEnemyFinalEffect.apply(handler)` 只影响自身、不依赖外部;说明它适合做「最后再补一点护甲」这类收尾,`priority` 越大越先执行。
|
||||
9. **编辑器与图鉴(D-47 占位)**:编辑器新增属性项与怪物图鉴显示需要在对应端实现,当前不在本篇范围;用代码 span `编辑器 · 新增怪物属性项` 与 `怪物图鉴`,并加 `<!-- TODO(advanced-II): 编辑器属性项与怪物图鉴 -->` 注释。不要写 `src/client` 实现。
|
||||
10. **怎么验证**:把某怪物 `armor` 设为 50、`atkSpeed` 设为 100,打印 `getDamageInfo` 的 `damage`;再把 `armor` 改为 0,确认伤害翻倍;最后给该怪物加一个 +10 护甲的光环,确认伤害再次下降。
|
||||
11. **边界与注意**:内容条目与默认模板都要补键;`guard` 不要写进内容;新增字段会进入怪物存档,读旧档要兜底;光环/查询修改的是计算后怪物,刷新前不要拿它当原始数据;百分比统一用整数表示。
|
||||
按 D-59 叙述,完整对象/类示例,注释充足;加粗符合 flanking。完成后跑 prettier `--write docs/advanced/ii/adding-enemy-attribute.md`。Commit:`docs(10-24): 编写高级 II 新增怪物属性`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/ii/adding-enemy-attribute.md'; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; foreach($needle in @('IEnemyAttr','atkSpeed','armor','getAttribute','setAttribute','IAuraView','registerCommonQueryEffect','IEnemyFinalEffect','编辑器','图鉴','TODO(','怎么验证')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; if($raw -match 'src/client'){ Write-Error "RENDERER-CONTENT" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent $p) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '```ts'){ Write-Error "NO-TS-BLOCK" }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'ENEMY-ATTR-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "MISSING-CONTENT", "RENDERER-CONTENT", "DEAD-LINK", "NO-TS-BLOCK", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "ENEMY-ATTR-OK"</fails_when>
|
||||
</verify>
|
||||
<done>怪物属性篇存在、CRLF、单 H1;接口、默认模板与内容、读取修改、伤害/光环/查询/最终接入、D-47 占位、验证与边界齐全;代码块完整;无渲染端内容;无死链;构建通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 编写怪物上下文与战斗深入 enemy-context-and-combat.md</name>
|
||||
<files>docs/advanced/ii/enemy-context-and-combat.md</files>
|
||||
<action>写 `docs/advanced/ii/enemy-context-and-combat.md`,单 H1「怪物上下文与战斗深入」。正文顺序:
|
||||
1. **上下文是什么**:`IEnemyContext` 是当前楼层所有怪物视图的容器,提供 `width` / `height` / `floor` / `indexer` / `scanRange` / `iterateEnemy` / `getEnemyByLoc` / `getEnemyLocator` / `getDamageSystem` / `getMapDamage`;说明它不存档,切楼层由 `setFloor` / `resize` 维护。
|
||||
2. **三种怪物对象**:`getBaseEnemy()` 原始模板、`getComputedEnemy()` 计算后结果、`getModifiableEnemy()` 可改但需手动 `markDirty`;`reset()` 把计算后怪物退回原始状态。强调伤害与地图伤害都读计算后怪物。
|
||||
3. **buildup 五段(主干)**:用编号列表讲清 `buildup()` 的顺序——① 所有视图 `reset`;② `buildupSpecials`:全局光环入表、特殊属性转光环、按优先级从高到低执行特殊查询;③ `buildupBase`:按优先级从高到低执行基础光环;④ `buildupQuery`:常规查询;⑤ `buildupFinal`:最终效果;结束后 `damageSystem.markAllDirty()` 与 `mapDamage.refreshAll()`。附 mermaid 流程图,节点只写阶段名。
|
||||
4. **优先级的边界**:数值越大越先执行;特殊属性新产生的光环只能影响之后的阶段,不能反过来影响当前或更高优先级(开发环境会告警);同一阶段不允许删除同优先级或更高优先级的已生效光环。给出一个「优先级 20 的光环给周围怪物加一个优先级 10 的光环」的可成立示例,并说明反过来为什么不成立。
|
||||
5. **局部刷新与全量重建**:`markDirty(view)` 后 `requestRefresh(view)`;若 `needTotallyRefresh` 命中则回到全量 `buildup`,否则 `refreshEnemy` 只重算命中该怪物的光环与查询;`requestedCommonContext` 记录「查询过上下文」的怪物,局部刷新时一并重算。说明为什么不能只改字段不刷新。
|
||||
6. **伤害系统与缓存**:`IDamageSystem.getDamageInfo(view)` 用 `view.getComputedEnemy()` 调计算器;`DamageSystem.markDirty` / `markAllDirty` 维护缓存,`getCriticalLimit` 只提供临界上界;说明「改了怪物属性但没有标脏」会读到旧伤害。给出修改后标脏并重新取伤害的完整片段。
|
||||
7. **与光环/查询/最终的交互**:用一张表列四个阶段「谁改谁、是否依赖外部、是否需要标脏」;说明地图伤害按计算后怪物计算,因此光环新增的特殊属性会进入地图伤害判定;光环/查询/最终都不要直接改 `getBaseEnemy()`。
|
||||
8. **一个完整的收束示例**:注册一个最终效果(给所有怪物 +5 护甲)与一个基础光环(给范围内怪物 +10 攻速),调用 `context.buildup()` 后打印某只怪的 `getComputedEnemy().getAttribute('armor')` 与 `getDamageInfo` 的 `damage`;再改基础属性并 `markDirty` + `requestRefresh`,确认结果更新。
|
||||
9. **怎么验证**:给出可执行检查——固定一只怪物与英雄属性,记录 `buildup` 前后的 `getComputedEnemy()` 属性与伤害;预期光环/最终效果只影响计算后怪物,原始模板不变;`markDirty` 后伤害缓存刷新。
|
||||
10. **边界与注意**:`buildup` 在未绑定英雄时会告警并直接返回;`getModifiableEnemy` 的修改不会自动同步;`clear` / `destroy` 的时机;不要在高频循环里反复全量 `buildup`;开发环境的优先级告警不是致命错误,但通常说明设计有问题。
|
||||
按 D-59 叙述,完整类/调用示例,注释充足;不重复中级篇已讲的光环/查询接口细节,只讲时序与交互;加粗符合 flanking。完成后跑 prettier `--write docs/advanced/ii/enemy-context-and-combat.md`。Commit:`docs(10-24): 编写高级 II 怪物上下文与战斗深入`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/ii/enemy-context-and-combat.md'; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; foreach($needle in @('IEnemyContext','buildup','buildupSpecials','buildupBase','buildupQuery','buildupFinal','markDirty','requestRefresh','IEnemyView','getComputedEnemy','IDamageSystem','getDamageInfo','getCriticalLimit','needTotallyRefresh','怎么验证')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; if($raw -match 'src/client'){ Write-Error "RENDERER-CONTENT" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent $p) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '```ts'){ Write-Error "NO-TS-BLOCK" }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'CONTEXT-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "MISSING-CONTENT", "RENDERER-CONTENT", "DEAD-LINK", "NO-TS-BLOCK", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "CONTEXT-OK"</fails_when>
|
||||
</verify>
|
||||
<done>战斗篇存在、CRLF、单 H1;上下文职责、三种怪物对象、buildup 五段、优先级边界、局部刷新、伤害缓存、交互表、收束示例、验证与边界齐全;不重复中级接口细节;无渲染端内容;无死链;构建通过。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 执行者 → 子代理 | 文档由子代理按任务书编写,执行者只验收 |
|
||||
| 构建期 markdown → 静态站点 | 正文由构建期静态渲染,无运行时输入 |
|
||||
| 站点 → 浏览器读者 | 站内链接是唯一可点击指向面 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-70 | Tampering / XSS | 新增两篇正文 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-71 | Spoofing | 阶段顺序与优先级 | low | mitigate | buildup 顺序、优先级边界与脏标记逐条核对 `context.ts` |
|
||||
| T-10-72 | Information Disclosure | 正文 | low | mitigate | 只引用仓库已公开路径与接口;不写密钥、账号 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 命名门禁:两篇文件名与 10-21 获批基线一致。
|
||||
- 内容门禁:属性篇覆盖接口、默认模板与内容、读取修改、四种接入点、D-47 占位;战斗篇覆盖上下文职责、三种视图、buildup 五段、优先级、局部刷新、伤害缓存与交互表。
|
||||
- 事实门禁:`buildup` 顺序、优先级边界、地图伤害按计算后怪物、`markDirty` 与缓存刷新——四处不得写反。
|
||||
- 边界门禁:无 `src/client` 内容;不把未内置的查询效果写成已内置。
|
||||
- 结构门禁:kebab-case、CRLF、单 H1;相对链接全部可解析。
|
||||
- 构建门禁:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` 通过,`build complete`,dead link = 0。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 读者能新增怪物属性并让它进入伤害、光环与查询三个阶段。
|
||||
- 读者能解释 buildup 的顺序、优先级边界与脏标记为何必须,能定位「改了属性但伤害没变」的原因。
|
||||
- 未越界修改 `config.ts`、生产代码与渲染端内容。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-24-SUMMARY.md` when done
|
||||
</output>
|
||||
193
.planning/phases/10-docs-restructure/10-25-PLAN.md
Normal file
193
.planning/phases/10-docs-restructure/10-25-PLAN.md
Normal file
@ -0,0 +1,193 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 25
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: [10-21, 10-22, 10-23, 10-24]
|
||||
files_modified:
|
||||
- docs/advanced/ii/index.md
|
||||
- docs/advanced/index.md
|
||||
- docs/advanced/ii/custom-range.md
|
||||
- docs/advanced/ii/static-vs-dynamic-tiles.md
|
||||
- docs/advanced/ii/equipment-system.md
|
||||
- docs/advanced/ii/custom-replay-command.md
|
||||
- docs/advanced/ii/pathfinding.md
|
||||
- docs/advanced/ii/adding-hero-attribute.md
|
||||
- docs/advanced/ii/adding-enemy-attribute.md
|
||||
- docs/advanced/ii/enemy-context-and-combat.md
|
||||
- docs/.vitepress/config.ts
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 30000
|
||||
raw_tokens: 20000
|
||||
tasks: 4
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "高级 II 九篇在站点上可见:`docs/advanced/ii/index.md` 导览 + 八篇正文;侧边栏「高级 II · 深入定制」组恰好挂 9 个链接"
|
||||
- "`docs/.vitepress/config.ts` 只新增/改写 `sidebar['/advanced/']` 中高级 II 分组与条目,不改 nav、`/intermediate/`、`base`、`outDir`、搜索与 mermaid 配置"
|
||||
- "导览讲清「深入定制」的定位、前置(高级 I + 中级相关篇目)、三条阅读路径与每篇能做什么;高级 III 只用代码 span 或 `TODO(advanced-III)` 占位"
|
||||
- "八篇正文末尾都有返回高级 II 导览的链接;`docs/advanced/index.md` 中「只收录高级 I」的过时说明改为指向已存在的高级 II"
|
||||
- "侧边栏分组名以 Task 0 用户确认的为准;若用户要求保留「系统实现」,按用户决定执行,不擅自坚持「深入定制」"
|
||||
- "全部文件 kebab-case、CRLF、单 H1;prettier 检查通过;构建 green、`build complete`、dead link = 0"
|
||||
artifacts:
|
||||
- "docs/advanced/ii/index.md"
|
||||
- "docs/.vitepress/config.ts(仅 `/advanced/` 侧边栏)"
|
||||
- "docs/advanced/index.md(仅高级 II 说明与链接)"
|
||||
- ".planning/phases/10-docs-restructure/10-25-SUMMARY.md"
|
||||
key_links:
|
||||
- "本计划把 10-21..10-24 的八篇接入站点,是 10-26 读者审阅的前提"
|
||||
- "导览的阅读路径是 D-67 推荐顺序的唯一入口"
|
||||
- "高级 I 导览中的高级 II 占位在此替换为真实链接"
|
||||
---
|
||||
|
||||
<objective>
|
||||
编写高级 II 导览并接入站点:新增 `docs/advanced/ii/index.md`,在 `docs/.vitepress/config.ts` 的 `/advanced/` 侧边栏建立「高级 II · 深入定制」组并挂九篇,补八篇互链与高级 I 导览中的高级 II 入口,最后做格式与构建收口。
|
||||
|
||||
Purpose: 八篇正文写好但站点没有入口等于不存在。导览还要承担 D-67 的定位、前置与阅读路径,并说明哪些内容属于高级 III 或渲染端。
|
||||
Output: `docs/advanced/ii/index.md`、侧边栏注册、跨篇链接与构建产物。
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/13194/.config/opencode/gsd-core/workflows/execute-plan.md
|
||||
@C:/Users/13194/.config/opencode/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/phases/10-docs-restructure/10-CONTEXT.md
|
||||
@docs/advanced/index.md
|
||||
@docs/advanced/ii/custom-range.md
|
||||
@docs/advanced/ii/static-vs-dynamic-tiles.md
|
||||
@docs/advanced/ii/equipment-system.md
|
||||
@docs/advanced/ii/custom-replay-command.md
|
||||
@docs/advanced/ii/pathfinding.md
|
||||
@docs/advanced/ii/adding-hero-attribute.md
|
||||
@docs/advanced/ii/adding-enemy-attribute.md
|
||||
@docs/advanced/ii/enemy-context-and-combat.md
|
||||
@docs/.vitepress/config.ts
|
||||
@docs/intermediate/enemy-aura.md
|
||||
@docs/intermediate/enemy-map-damage.md
|
||||
@docs/intermediate/hero-equipment.md
|
||||
@docs/intermediate/replay-commands.md
|
||||
@docs/intermediate/enemy-calculator.md
|
||||
|
||||
**注册口径:**
|
||||
- 现有 `sidebar['/advanced/']` 有三组:高级 I(7 项,已展开)、高级 II(空,`collapsed: true`)、高级 III(空,`collapsed: true`)。
|
||||
- 本计划把高级 II 组填充为 9 项,并将 `collapsed` 设为 `false`;分组名建议「高级 II · 深入定制」(D-53 的 II = 深入定制,是难度标签而非领域分类)。
|
||||
- 链接用绝对路径、不带 `.md`,与现有条目一致;不添加指向不存在页面的链接。
|
||||
- `nav` 保持现状(中级教程、高级教程),不新增导航项。
|
||||
|
||||
**导览口径:**
|
||||
- 定位:高级 II 是「深入定制」难度层;读者需要能读懂引擎源码,目标是把某个具体系统改到自己的需求上。
|
||||
- 前置:高级 I 全部(尤其 `declarative-vs-imperative`、`plugin-system`、`saveable-content`、`replay-consistency`)+ 中级相关篇目(`enemy-aura`、`enemy-query`、`enemy-final`、`enemy-calculator`、`enemy-map-damage`、`hero-equipment`、`hero-attribute`、`replay-commands`、`map-tiles`)。
|
||||
- 阅读路径建议三条:系统定制(范围 → 图块 → 装备 → 勇士属性 → 怪物属性 → 上下文与战斗)、行为扩展(录像指令 → 寻路)、按需查阅(哪篇解决什么问题)。
|
||||
- 边界:只写数据端;渲染端、编辑器、状态栏、怪物图鉴按 D-47 占位;原理与架构留给高级 III。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 汇报导览与侧边栏注册计划并确认分组名</name>
|
||||
<action>向用户汇报:**要做什么**(编写 `docs/advanced/ii/index.md` 高级 II 导览;在 `/advanced/` 侧边栏建立高级 II 组并挂九篇;补八篇互链与高级 I 导览入口;跑 prettier 与构建)、**为什么**(八篇正文需要站点入口;导览要讲清「深入定制」的定位、前置与阅读路径)、**大致怎么做**(先写导览,再改 `config.ts`,最后做互链与构建收口;高级 III 仍只用占位)、**影响范围**(新增导览;修改 `config.ts` 的 `/advanced/` 侧边栏;轻微编辑八篇末尾链接与 `docs/advanced/index.md` 的说明;不改 nav、生产代码与渲染端内容)。请用户最终确认:
|
||||
1. 九篇文件名(与 10-21 基线一致);
|
||||
2. 侧边栏分组名「高级 II · 深入定制」——若用户认为「系统实现」更合适,按用户名称执行;
|
||||
3. 导览中的三条阅读路径是否可接受。
|
||||
汇报中明确:本计划的修改方式为**派发子代理执行**,范围严格限于 `files_modified` 列出的导览、八篇正文、`docs/advanced/index.md` 与 `config.ts` 的 `/advanced/` 侧边栏。停下等待用户明确回复「可以执行」四个字;未收到前不得改任何文件。</action>
|
||||
<what-built>本计划内容:高级 II 导览 + 侧边栏注册 + 互链收口 + 全量构建。</what-built>
|
||||
<how-to-verify>阅读上面的四段说明,确认分组名与三条阅读路径;如要改分组名或路径,请直接给出。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<verify>
|
||||
<human-check>用户已确认分组名与文件名,并回复「可以执行」。</human-check>
|
||||
</verify>
|
||||
<done>用户确认分组名与文件名并回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 编写高级 II 导览 ii/index.md</name>
|
||||
<files>docs/advanced/ii/index.md</files>
|
||||
<action>写 `docs/advanced/ii/index.md`,单 H1「高级 II 导览」。正文顺序:
|
||||
1. **高级 II 是给谁的**:面向能读懂引擎源码、想把某个具体系统改到需求上的数据端作者;不是引擎编写者;本层只写数据端。
|
||||
2. **难度层定位**:用一段说明 I / II / III 的区别(I 扩展基础、II 深入定制、III 原理与架构),并强调分层是难度标签,不是领域分类;高级 III 只用代码 span `高级 III · ...` 或 `<!-- TODO(advanced-III): ... -->` 占位,不写真实链接。
|
||||
3. **前置**:列出高级 I 的六篇(真实链接 `../declarative-vs-imperative.md` 等)与中级相关篇目(`../../intermediate/...`);说明至少要读过高级 I 的声明式、插件、可存档、录像一致性四篇。
|
||||
4. **本层包含什么**:一张九行表(导览 + 八篇),每行写「篇名 / 一句话能让你做什么 / 前置」;八篇都给真实相对链接(`./custom-range.md`、`./static-vs-dynamic-tiles.md`、`./equipment-system.md`、`./custom-replay-command.md`、`./pathfinding.md`、`./adding-hero-attribute.md`、`./adding-enemy-attribute.md`、`./enemy-context-and-combat.md`)。
|
||||
5. **阅读路径**:三条——系统定制(范围 → 图块 → 装备 → 勇士属性 → 怪物属性 → 上下文与战斗)、行为扩展(录像指令 → 寻路)、按需查阅(说明每篇解决什么问题);说明三条路径都建议先读范围对象。
|
||||
6. **本层不覆盖什么**:渲染端与编辑器、状态栏、怪物图鉴、高级 III 的原理与架构、API 全量参考;只作边界说明,不展开。
|
||||
7. **优先插件**:按 D-55 说明新增系统优先用插件承载,只有不得不改内置系统时才改 `packages` / `packages-user`;给一张「需求类型 / 优先做法 / 何时才改内置」的小表。
|
||||
8. **怎么验证你读懂了**:给可操作清单——能说出范围对象如何绑定宿主;能区分点事件与图块事件;能解释装备修饰器为什么不进属性存档;能说出录像指令主动/被动的差别;能画出 buildup 的五段顺序。
|
||||
9. 结尾给返回[高级 I 导览](../index.md)的链接。
|
||||
不要写「后续再讲」式元叙述;不要写渲染端正文;加粗符合 flanking。完成后跑 prettier `--write docs/advanced/ii/index.md`。Commit:`docs(10-25): 编写高级 II 导览`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/ii/index.md'; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; foreach($needle in @('高级 II','深入定制','阅读路径','前置','优先','插件','怎么验证')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; foreach($target in @('../index.md','./custom-range.md','./static-vs-dynamic-tiles.md','./equipment-system.md','./custom-replay-command.md','./pathfinding.md','./adding-hero-attribute.md','./adding-enemy-attribute.md','./enemy-context-and-combat.md')){ if($raw -notmatch [regex]::Escape($target)){ Write-Error "MISSING-LINK $target" } }; if($raw -match 'src/client'){ Write-Error "RENDERER-CONTENT" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent $p) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; Write-Output 'INDEX-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "MISSING-CONTENT", "MISSING-LINK", "RENDERER-CONTENT", or "DEAD-LINK"; or output lacks "INDEX-OK"</fails_when>
|
||||
</verify>
|
||||
<done>导览存在、CRLF、单 H1;定位、前置、九篇表、三条路径、边界、D-55 原则、自检清单、八篇真实链接与返回高级 I 链接齐全;无渲染端内容;无死链。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 在 config.ts 注册高级 II 侧边栏</name>
|
||||
<files>docs/.vitepress/config.ts</files>
|
||||
<action>在 `docs/.vitepress/config.ts` 的 `sidebar['/advanced/']` 中,把现有高级 II 空组替换为:
|
||||
- `text` 使用 Task 0 用户确认的分组名(默认「高级 II · 深入定制」);
|
||||
- `collapsed: false`;
|
||||
- `items` 依次为:`高级 II 导览` → `/advanced/ii/`;`自定义范围对象` → `/advanced/ii/custom-range`;`静态图块与动态图块` → `/advanced/ii/static-vs-dynamic-tiles`;`勇士的装备系统` → `/advanced/ii/equipment-system`;`自定义录像指令` → `/advanced/ii/custom-replay-command`;`自动寻路` → `/advanced/ii/pathfinding`;`新增勇士属性` → `/advanced/ii/adding-hero-attribute`;`新增怪物属性` → `/advanced/ii/adding-enemy-attribute`;`怪物上下文与战斗深入` → `/advanced/ii/enemy-context-and-combat`。
|
||||
保持高级 I 组与高级 III 组不变;不新增 nav 项;不改 `/intermediate/`、`base`、`outDir`、搜索与 mermaid 配置。用 `git diff -- docs/.vitepress/config.ts` 自查,确认只动了 `/advanced/` 下的高级 II 组。Commit:`docs(10-25): 注册高级 II 侧边栏`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $cfg='docs/.vitepress/config.ts'; if(-not(Test-Path -LiteralPath $cfg)){ Write-Error "MISSING $cfg" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $cfg)); if($raw -notmatch "'/advanced/':"){ Write-Error "NO-ADVANCED-KEY" }; if($raw -notmatch "text:\s*'高级 II · (深入定制|系统实现)'"){ Write-Error "NO-GROUP-II" }; $targets=[regex]::Matches($raw,"link:\s*'/advanced/ii/([^']*)'") | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique; if($targets.Count -ne 9){ Write-Error ("II-LINK-COUNT " + $targets.Count) }; $want=@('','custom-range','static-vs-dynamic-tiles','equipment-system','custom-replay-command','pathfinding','adding-hero-attribute','adding-enemy-attribute','enemy-context-and-combat') | Sort-Object -Unique; if(($targets -join ',') -ne ($want -join ',')){ Write-Error ("II-LINKS " + ($targets -join ',')) }; if($raw -notmatch "text:\s*'高级 I · 扩展基础'"){ Write-Error "NO-GROUP-I" }; if($raw -notmatch "text:\s*'高级 III · 原理与架构'"){ Write-Error "NO-GROUP-III" }; Write-Output 'CONFIG-II-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "NO-ADVANCED-KEY", "NO-GROUP-II", "II-LINK-COUNT", "II-LINKS", "NO-GROUP-I", or "NO-GROUP-III"; or output lacks "CONFIG-II-OK"</fails_when>
|
||||
</verify>
|
||||
<done>`/advanced/` 侧边栏高级 II 组恰好 9 个目标(导览 + 八篇),分组名正确;高级 I / III 组与其它配置未动。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: 互链收口、格式与全量构建</name>
|
||||
<files>docs/advanced/ii/*.md(八篇正文), docs/advanced/index.md</files>
|
||||
<action>(1) 在八篇正文末尾各加一行「返回[高级 II 导览](./index.md)」,链接数量保持克制(D-17)。(2) 检查八篇中指向高级 I / 中级 / 高级 II 的链接:已存在页面改真实相对链接(`../index.md`、`../../intermediate/...`、`./...`),高级 III 仍用代码 span 或 `TODO(advanced-III)`,不产生死链。(3) 修改 `docs/advanced/index.md`:把「当前站点只收录高级 I」这类过时说明改为「高级 II 已可阅读」,并把高级 II 一行改为指向 `./ii/` 的真实链接;高级 III 仍保持占位;不要重写整篇导览。(4) 对 `docs/advanced/**/*.md` 跑 prettier `--write`,再 `--check` 确认通过。(5) 跑全量构建 `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,确认九篇都被构建、`build complete`、dead link = 0。Commit:`docs(10-25): 收口高级 II 互链与构建`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $names=@('custom-range','static-vs-dynamic-tiles','equipment-system','custom-replay-command','pathfinding','adding-hero-attribute','adding-enemy-attribute','enemy-context-and-combat'); foreach($n in $names){ $p="docs/advanced/ii/$n.md"; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; if($raw -notmatch [regex]::Escape('./index.md')){ Write-Error "NO-BACK-LINK $p" }; if($raw -match 'src/client'){ Write-Error "RENDERER-CONTENT $p" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent $p) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK $p -> " + $m.Groups[1].Value) } } }; $ai=[IO.File]::ReadAllText((Resolve-Path 'docs/advanced/index.md')); if($ai -notmatch '\./ii/'){ Write-Error "ADV-INDEX-NO-II-LINK" }; $cfg=[IO.File]::ReadAllText((Resolve-Path 'docs/.vitepress/config.ts')); $targets=[regex]::Matches($cfg,"link:\s*'/advanced/ii/([^']*)'") | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique; if($targets.Count -ne 9){ Write-Error ("II-LINK-COUNT " + $targets.Count) }; $ErrorActionPreference='Continue'; $pf = cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/index.md docs/advanced/ii/index.md docs/advanced/ii/custom-range.md docs/advanced/ii/static-vs-dynamic-tiles.md docs/advanced/ii/equipment-system.md docs/advanced/ii/custom-replay-command.md docs/advanced/ii/pathfinding.md docs/advanced/ii/adding-hero-attribute.md docs/advanced/ii/adding-enemy-attribute.md docs/advanced/ii/enemy-context-and-combat.md" 2>&1; $pfe=$LASTEXITCODE; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($pfe -ne 0){ $pf | Write-Output; Write-Error "PRETTIER-FAIL" }; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'FINAL-II-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "NO-BACK-LINK", "RENDERER-CONTENT", "DEAD-LINK", "ADV-INDEX-NO-II-LINK", "II-LINK-COUNT", "PRETTIER-FAIL", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "FINAL-II-OK"</fails_when>
|
||||
</verify>
|
||||
<done>九篇文件齐全、CRLF、单 H1;八篇有返回导览链接;高级 I 导览已指向高级 II;侧边栏 9 个目标正确;prettier 检查通过;构建 green、`build complete`、dead link = 0。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 执行者 → 子代理 | 导览、正文链接与配置由子代理按任务书修改 |
|
||||
| 构建期 markdown → 静态站点 | 正文与侧边栏由构建期静态处理,无运行时输入 |
|
||||
| 配置层 → 构建产物 | `config.ts` 的侧边栏决定可浏览页面集合 |
|
||||
| 站点 → 浏览器读者 | 站内链接是唯一可点击指向面 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-73 | Tampering / XSS | 导览与八篇正文 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-74 | Spoofing | 侧边栏链接 | low | mitigate | 断言 9 个 `/advanced/ii/*` 目标与页面文件名一一对应 |
|
||||
| T-10-75 | Denial of Service | 站点构建 | low | mitigate | 全量构建门禁,dead link 即失败 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 注册门禁:`sidebar['/advanced/']` 高级 II 组恰好 9 个目标,分组名与 Task 0 确认一致。
|
||||
- 导览门禁:定位、前置、九篇表、三条路径、边界、D-55、自检清单齐全;八篇真实链接可解析。
|
||||
- 互链门禁:八篇末尾均有返回导览链接;高级 I 导览指向 `./ii/`;高级 III 无真实链接。
|
||||
- 格式门禁:prettier `--check` 对十篇通过。
|
||||
- 结构门禁:kebab-case、CRLF、单 H1。
|
||||
- 构建门禁:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` 通过,`build complete`,dead link = 0。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 高级 II 九篇在站点上可见、可导航、可互相跳转。
|
||||
- 导览讲清「深入定制」定位、前置、阅读路径与边界。
|
||||
- 构建通过,无死链;配置改动仅限 `/advanced/` 侧边栏。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-25-SUMMARY.md` when done
|
||||
</output>
|
||||
172
.planning/phases/10-docs-restructure/10-26-PLAN.md
Normal file
172
.planning/phases/10-docs-restructure/10-26-PLAN.md
Normal file
@ -0,0 +1,172 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 26
|
||||
type: execute
|
||||
wave: 3
|
||||
depends_on: [10-25]
|
||||
files_modified:
|
||||
- .planning/phases/10-docs-restructure/10-26-READER-REVIEW.md
|
||||
- docs/advanced/ii/index.md
|
||||
- docs/advanced/ii/custom-range.md
|
||||
- docs/advanced/ii/static-vs-dynamic-tiles.md
|
||||
- docs/advanced/ii/equipment-system.md
|
||||
- docs/advanced/ii/custom-replay-command.md
|
||||
- docs/advanced/ii/pathfinding.md
|
||||
- docs/advanced/ii/adding-hero-attribute.md
|
||||
- docs/advanced/ii/adding-enemy-attribute.md
|
||||
- docs/advanced/ii/enemy-context-and-combat.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 34000
|
||||
raw_tokens: 22000
|
||||
tasks: 4
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "读者人格是「有较好代码能力、能读懂引擎源码、想做系统级定制,但还不熟悉具体系统实现」的数据端作者——标准高于高级 I,且不复用高级 I 或中级教程的人格(D-46 第 1 阶段 / D-57)"
|
||||
- "`10-26-READER-REVIEW.md` 逐字记录人格提示词、审阅范围(高级 II 九篇)、问题清单,并按阻断性与非阻断归类、定位到文件与章节"
|
||||
- "阻断性问题被迭代修正(上限 3 轮);每轮后构建 green、`build complete`、dead link = 0;达到上限仍有阻断问题时停下交用户"
|
||||
- "迭代结束再派发一个独立子代理(同一人格)复审,记录逐条标注「已消除」或「仍存在」;结束标记为「阻断性问题 = 0」或「阻断性问题余项:已交用户」"
|
||||
- "只改 `docs/advanced/ii/*.md` 与审阅记录;不改 `config.ts`、生产代码与渲染端内容;被改文件保持 kebab-case、CRLF、单 H1"
|
||||
- "非阻断问题只记录、不擅自扩大范围(D-14);不因读者提问删掉 D-67 要求覆盖的内容"
|
||||
artifacts:
|
||||
- ".planning/phases/10-docs-restructure/10-26-READER-REVIEW.md"
|
||||
- "docs/advanced/ii/*.md(仅存在阻断性问题的篇目)"
|
||||
- ".planning/phases/10-docs-restructure/10-26-SUMMARY.md"
|
||||
key_links:
|
||||
- "执行 D-46 第 1 阶段:独立的高代码能力读者人格 → 问题清单 → 迭代修正 → 复审"
|
||||
- "复审通过后,10-27 才进入用户主导的修订循环;本计划不替用户收尾"
|
||||
- "审阅对象是 10-25 已接入侧边栏的九篇,而不是未注册草稿"
|
||||
---
|
||||
|
||||
<objective>
|
||||
执行高级 II 的独立读者审阅:派发一个与高级 I、中级教程都**不同人格**的独立子代理(有较好代码能力、能读懂引擎源码、想做系统级定制、但还不熟悉具体系统实现的数据端作者),通读九篇高级 II,产出问题清单;据此迭代修正阻断性问题,最后再派一个同人格独立子代理复审,直到阻断问题为零或把余项交给用户。
|
||||
|
||||
Purpose: 高级 II 的读者会真的去改接口、接系统、调优先级,所以问题不再是「这段代码放哪」,而是「这条链路是否成立」「改完之后为什么没生效」「优先级/脏标记/存档是否被写反」。这批问题只能由具备源码阅读能力的读者视角发现。
|
||||
Output: `10-26-READER-REVIEW.md`(人格、问题、迭代、复审)与针对阻断问题的文档修正。
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/13194/.config/opencode/gsd-core/workflows/execute-plan.md
|
||||
@C:/Users/13194/.config/opencode/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/phases/10-docs-restructure/10-CONTEXT.md
|
||||
@.planning/phases/10-docs-restructure/10-19-READER-REVIEW.md
|
||||
@.planning/phases/10-docs-restructure/10-25-PLAN.md
|
||||
@docs/advanced/ii/index.md
|
||||
@docs/advanced/ii/custom-range.md
|
||||
@docs/advanced/ii/static-vs-dynamic-tiles.md
|
||||
@docs/advanced/ii/equipment-system.md
|
||||
@docs/advanced/ii/custom-replay-command.md
|
||||
@docs/advanced/ii/pathfinding.md
|
||||
@docs/advanced/ii/adding-hero-attribute.md
|
||||
@docs/advanced/ii/adding-enemy-attribute.md
|
||||
@docs/advanced/ii/enemy-context-and-combat.md
|
||||
|
||||
**人格设定(D-46 / D-57,不得复用高级 I 或中级人格):**
|
||||
> 你是一位有较好代码能力、能读懂引擎源码的数据端作者,想做系统级定制,但还不熟悉具体系统实现。你已经读完高级 I,正在读高级 II,想判断自己能不能按文档完成一次真正的系统改造。
|
||||
|
||||
**与高级 I 审阅的区别:**
|
||||
- 高级 I 人格是「了解基本语法但代码能力一般」;本计划**禁止**复用该人格或其提示词,也不使用中级的「不懂代码」人格。
|
||||
- 高级 II 读者能读源码、能判断接口调用是否成立;问题应落在「流程/时序是否讲对」「改了接口后还有哪些地方必须同步」「优先级与脏标记是否会导致不生效」「存档/录像/装备的恢复路径是否自洽」。
|
||||
- 审阅对象是 `docs/advanced/ii/` 的九篇,不是高级 I 的七篇。
|
||||
|
||||
**流程约定:**
|
||||
- 迭代上限 **3 轮**;每轮后跑构建门禁;达到上限仍有阻断问题时停下向用户汇报。
|
||||
- 只改 `docs/advanced/ii/*.md`;不触碰 `config.ts`、生产代码与渲染端内容。
|
||||
- 命令一律经 `cmd /c "..."` 执行(本机 PowerShell 禁止 `pnpm.ps1`)。
|
||||
- 修正须守住 D-53..D-67 与 D-59..D-66;不因读者提问删掉要求覆盖的内容。
|
||||
- 阻断性判定口径:读者据此**无法完成系统改造**,或会对**流程/时序/系统边界产生实质误解**。
|
||||
- 非阻断问题只记录,不修(D-14),留给 10-27 的用户循环决定。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报读者审阅计划并确认人格</name>
|
||||
<action>向用户汇报:**要做什么**(派发独立子代理,以「有较好代码能力、能读懂引擎源码、想做系统级定制,但还不熟悉具体系统实现」的人格通读高级 II 九篇,产出问题清单,据此迭代修正并复审)、**为什么**(D-46 第 1 阶段要求子代理自审;D-57 允许高级 II 提高标准,且不能复用高级 I / 中级教程的人格)、**大致怎么做**(Task 1 派发 persona 子代理并落清单;Task 2 按清单修阻断问题,最多 3 轮、每轮构建;Task 3 复跑同人格审阅确认阻断清零)、**影响范围**(可能修改 `docs/advanced/ii/*.md` 中确有阻断问题的篇目 + 新增 `.planning` 下的审阅记录;不改站点配置与生产代码)。请用户逐字确认人格提示词:「你是一位有较好代码能力、能读懂引擎源码的数据端作者,想做系统级定制,但还不熟悉具体系统实现。你已经读完高级 I,正在读高级 II,想判断自己能不能按文档完成一次真正的系统改造。」汇报中明确:该人格标准高于高级 I(要求能读源码),但不是引擎作者;本计划的修改方式为**派发子代理执行**,范围严格限于 `docs/advanced/ii/*.md` 与 `.planning/.../10-26-READER-REVIEW.md`。停下等待用户明确回复「可以执行」四个字;未收到前不得派发子代理或改任何文件。</action>
|
||||
<what-built>本计划内容:高级 II 独立高代码能力读者审阅 → 问题清单 → 迭代修正 → 复审确认。</what-built>
|
||||
<how-to-verify>阅读上面的四段说明;确认人格提示词高于高级 I、审阅范围是高级 II 九篇、迭代上限 3 轮;如对人格有修改请直接给出。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<verify>
|
||||
<human-check>用户已确认人格提示词并回复「可以执行」。</human-check>
|
||||
</verify>
|
||||
<done>用户确认人格并回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 派发独立读者子代理并产出问题清单</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-26-READER-REVIEW.md</files>
|
||||
<action>派发一个**独立子代理**(不与执行者共享上下文),其人格与任务设定逐字为:「你是一位有较好代码能力、能读懂引擎源码的数据端作者,想做系统级定制,但还不熟悉具体系统实现。你已经读完高级 I,正在读高级 II,想判断自己能不能按文档完成一次真正的系统改造。」让子代理按 `docs/advanced/ii/index.md` 的推荐顺序通读九篇(导览、范围、图块、装备、录像、寻路、勇士属性、怪物属性、上下文与战斗),允许它打开文档引用的源码核对,然后回答:「读完后你有哪些问题?哪些地方你照做会卡住、会改错接口、会把流程/时序理解反,或者会让存档/录像/伤害结果不一致?」子代理**只以读者身份提问**、不修改文件、不代入引擎作者视角。把原始提问与执行者的归类写进 `.planning/phases/10-docs-restructure/10-26-READER-REVIEW.md`:开头逐字记录高级 II 人格提示词与九篇范围,并单独写一行「未复用高级 I 人格」标记;每条问题附文件与章节定位;按**阻断性**与**非阻断**分组。阻断性口径:读者据此无法完成系统改造,或会对流程/时序/系统边界产生实质误解。**本轮不改文档**。Commit:`docs(10-26): 记录高级 II 读者问题清单`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $f='.planning/phases/10-docs-restructure/10-26-READER-REVIEW.md'; if(-not(Test-Path -LiteralPath $f)){ Write-Error "MISSING $f" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $f)); foreach($needle in @('较好代码能力','能读懂引擎源码','系统级定制','不熟悉具体系统实现','已读完高级 I','未复用高级 I 人格','阻断','非阻断','docs/advanced/ii/index.md')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; $flat=$raw -replace '\s',''; $persona='你是一位有较好代码能力、能读懂引擎源码的数据端作者,想做系统级定制,但还不熟悉具体系统实现。你已经读完高级I,正在读高级II,想判断自己能不能按文档完成一次真正的系统改造。'; if(-not $flat.Contains($persona)){ Write-Error "MISSING-PERSONA-PROMPT" }; Write-Output 'REVIEW-II-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING" or "MISSING-CONTENT"; or output lacks "REVIEW-II-OK"</fails_when>
|
||||
</verify>
|
||||
<done>审阅记录存在;逐字含高级 II 人格提示词与九篇范围;问题按阻断/非阻断归类并定位到文件章节;标记未复用高级 I 人格。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 按清单迭代修正阻断问题(上限 3 轮)</name>
|
||||
<files>docs/advanced/ii/*.md(仅存在阻断性问题的篇目), .planning/phases/10-docs-restructure/10-26-READER-REVIEW.md</files>
|
||||
<action>逐个消除 `10-26-READER-REVIEW.md` 中的**阻断性问题**:对每个阻断问题,修改对应 `docs/advanced/ii/*.md` 的相应段落,使一个「能读源码但不熟悉具体系统实现」的读者也能完成改造——补「这条链路还缺哪一步」「改完要标脏/刷新才生效」「优先级为什么会拦住新光环」「读档/录像时哪条路径负责恢复」;必要时补完整示例或调整章节顺序。只改确有阻断问题的篇目;非阻断问题不修、仅保留在清单中供用户判断(D-14)。每轮修正后:(a) 跑构建门禁 `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,须 green、`build complete`、dead link = 0;(b) 在 `10-26-READER-REVIEW.md` 追加该轮记录(本轮修了哪些问题、依据、构建结论)。迭代**最多 3 轮**:3 轮后仍有阻断问题时,停下把余项列给用户,不继续循环。守住 D-53..D-67 与 D-59..D-66,不因读者提问删掉要求覆盖的内容;不改 `config.ts`、生产代码与渲染端内容。每轮 Commit:`docs(10-26): 迭代修正高级 II 阻断问题(第N轮)`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $names=@('index','custom-range','static-vs-dynamic-tiles','equipment-system','custom-replay-command','pathfinding','adding-hero-attribute','adding-enemy-attribute','enemy-context-and-combat'); foreach($n in $names){ $p="docs/advanced/ii/$n.md"; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" } }; $f='.planning/phases/10-docs-restructure/10-26-READER-REVIEW.md'; if(([IO.File]::ReadAllText((Resolve-Path -LiteralPath $f))) -notmatch '轮|迭代'){ Write-Error "NO-ROUND-LOG" }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'ITERATE-II-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "NO-ROUND-LOG", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "ITERATE-II-OK"</fails_when>
|
||||
</verify>
|
||||
<done>阻断问题按最多 3 轮修正完毕(余项已交用户);每轮记录进 `10-26-READER-REVIEW.md`;构建 green、`build complete`、dead link = 0。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: 复跑同人格审阅确认阻断清零</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-26-READER-REVIEW.md</files>
|
||||
<action>再次派发一个**独立子代理**(人格提示词逐字同 Task 1,不得换回高级 I 或中级人格),让它重新通读九篇并回答「现在还有哪些看不懂、照做会卡住、会把流程/时序理解反,或会让存档/录像/伤害结果不一致的地方?」。把复审结果追加进 `10-26-READER-REVIEW.md`:对照首轮清单,逐条标注「已消除」或「仍存在」;若仍有**阻断性**问题,回到 Task 2 再修(受 3 轮上限约束)。收敛时二选一写结构化结束标记:阻断问题清零记「阻断性问题 = 0,审阅通过」;3 轮上限后仍有阻断问题记「阻断性问题余项:已交用户」并列出余项与用户处置。复审记录同样保留「未复用高级 I 人格」标记。最后跑一次构建门禁确认 green。本任务不引入新功能,只收敛理解性问题。Commit:`docs(10-26): 复审高级 II 读者视角(收敛)`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $f='.planning/phases/10-docs-restructure/10-26-READER-REVIEW.md'; if(-not(Test-Path -LiteralPath $f)){ Write-Error "MISSING $f" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $f)); foreach($needle in @('较好代码能力','能读懂引擎源码','系统级定制','不熟悉具体系统实现','已读完高级 I','已消除','仍存在','未复用高级 I 人格')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; $flat=$raw -replace '\s',''; $persona='你是一位有较好代码能力、能读懂引擎源码的数据端作者,想做系统级定制,但还不熟悉具体系统实现。你已经读完高级I,正在读高级II,想判断自己能不能按文档完成一次真正的系统改造。'; if(-not $flat.Contains($persona)){ Write-Error "MISSING-PERSONA-PROMPT" }; if($raw -notmatch '阻断性问题 = 0' -and $raw -notmatch '阻断性问题余项:已交用户'){ Write-Error "NO-TERMINAL-MARKER" }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'RECHECK-II-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "MISSING-CONTENT", "NO-TERMINAL-MARKER", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "RECHECK-II-OK"</fails_when>
|
||||
</verify>
|
||||
<done>复审结果记录进 `10-26-READER-REVIEW.md`(逐条已消除/仍存在);结束标记为「阻断性问题 = 0」或「阻断性问题余项:已交用户」;构建 green、`build complete`、dead link = 0。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 执行者 → 子代理 | 读者人格子代理以独立上下文阅读九篇并提问,不修改文件 |
|
||||
| 构建期 markdown → 静态站点 | 正文由构建期静态渲染,无运行时输入 |
|
||||
| 站点 → 浏览器读者 | 站内链接是唯一可点击指向面 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-76 | Tampering / XSS | 被改高级 II 正文 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-77 | Spoofing | 审阅记录 / 链接 | low | mitigate | 记录逐字保留人格与来源;链接检查断言目标存在 |
|
||||
| T-10-78 | Information Disclosure | 审阅记录 / 正文 | low | mitigate | 只引用仓库已公开路径与接口;不写密钥、账号 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 机制门禁:`10-26-READER-REVIEW.md` 含高级 II 人格提示词(按空白归一化后逐字包含完整提示词)、九篇范围、首轮问题清单、每轮迭代记录、复审对照结果。
|
||||
- 人格门禁:记录含「较好代码能力」「能读懂引擎源码」「系统级定制」「不熟悉具体系统实现」「已读完高级 I」与「未复用高级 I 人格」标记。
|
||||
- 迭代门禁:上限 3 轮;结束标记为「阻断性问题 = 0」或「阻断性问题余项:已交用户」。
|
||||
- 构建门禁:每轮及收尾均 `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` 通过。
|
||||
- 结构门禁:九篇仍 kebab-case、CRLF、单 H1。
|
||||
- 越界门禁:`git status` 仅含 `docs/advanced/ii/*.md` 的修改与新增的 `.planning` 审阅记录。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 高级 II 的读者人格审阅按 D-46 第 1 阶段落地:独立子代理 + 更高标准人格 + 问题清单 + 迭代 + 复审。
|
||||
- 阻断性问题清零(或余项已明确交用户);构建通过。
|
||||
- 没有复用高级 I / 中级人格,没有越界改动。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-26-SUMMARY.md` when done
|
||||
</output>
|
||||
184
.planning/phases/10-docs-restructure/10-27-PLAN.md
Normal file
184
.planning/phases/10-docs-restructure/10-27-PLAN.md
Normal file
@ -0,0 +1,184 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 27
|
||||
type: execute
|
||||
wave: 4
|
||||
depends_on: [10-26]
|
||||
files_modified:
|
||||
- .planning/phases/10-docs-restructure/10-27-USER-REVIEW.md
|
||||
- .planning/phases/10-docs-restructure/10-27-SUMMARY.md
|
||||
- docs/advanced/ii/index.md
|
||||
- docs/advanced/ii/custom-range.md
|
||||
- docs/advanced/ii/static-vs-dynamic-tiles.md
|
||||
- docs/advanced/ii/equipment-system.md
|
||||
- docs/advanced/ii/custom-replay-command.md
|
||||
- docs/advanced/ii/pathfinding.md
|
||||
- docs/advanced/ii/adding-hero-attribute.md
|
||||
- docs/advanced/ii/adding-enemy-attribute.md
|
||||
- docs/advanced/ii/enemy-context-and-combat.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 30000
|
||||
raw_tokens: 20000
|
||||
tasks: 5
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "用户主导的修订循环被真正执行:用户以本领域视角逐条提出问题 → 派子代理修改 → 回报 → 用户继续提,直到用户明确确认满意"
|
||||
- "`10-27-USER-REVIEW.md` 记录每一轮用户意见、落点、处置与构建结论;最终确认以独占一行的 `用户最终确认:通过` 或 `用户最终确认:满意` 记录"
|
||||
- "收尾由用户确认触发,不由 AI 自行收尾;用户未确认前不得写 SUMMARY 或宣称完成"
|
||||
- "最终九篇全部 kebab-case、CRLF、单 H1;prettier 检查通过;构建 green、`build complete`、dead link = 0"
|
||||
- "不改 `config.ts`、生产代码与渲染端内容;越界问题只记录、不实施(D-14)"
|
||||
- "修订中不删除 D-67 要求覆盖的内容,也不把高级 III 的占位改成死链"
|
||||
artifacts:
|
||||
- ".planning/phases/10-docs-restructure/10-27-USER-REVIEW.md"
|
||||
- ".planning/phases/10-docs-restructure/10-27-SUMMARY.md"
|
||||
- "docs/advanced/ii/*.md"
|
||||
key_links:
|
||||
- "执行 D-46 第 2 阶段 / D-57 的用户主导修订循环;这是高级 II 收尾的唯一合法出口"
|
||||
- "10-26 已消除独立读者人格的阻断问题;本计划处理用户以领域视角提出的问题"
|
||||
- "最终构建产物与审阅记录共同构成 D-09 的人工验收依据"
|
||||
---
|
||||
|
||||
<objective>
|
||||
执行高级 II 的用户主导修订循环并收尾:把构建好的站点交给用户,由用户以本领域视角逐条提出问题;AI 派子代理修改、回报;用户继续提,直到用户明确确认满意。只有用户确认后才做最终格式与构建收口并写 SUMMARY。
|
||||
|
||||
Purpose: D-46 / D-57 明确要求:子代理审阅之后,必须再经用户主导的修订循环,且必须获用户确认才可收尾,不得由 AI 自行收尾。本计划就是这条出口。
|
||||
Output: `10-27-USER-REVIEW.md`(用户意见与处置)、最终文档修订、`10-27-SUMMARY.md`。
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/13194/.config/opencode/gsd-core/workflows/execute-plan.md
|
||||
@C:/Users/13194/.config/opencode/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/phases/10-docs-restructure/10-CONTEXT.md
|
||||
@.planning/phases/10-docs-restructure/10-26-READER-REVIEW.md
|
||||
@.planning/phases/10-docs-restructure/10-20-USER-REVIEW.md
|
||||
@docs/advanced/ii/index.md
|
||||
@docs/advanced/ii/custom-range.md
|
||||
@docs/advanced/ii/static-vs-dynamic-tiles.md
|
||||
@docs/advanced/ii/equipment-system.md
|
||||
@docs/advanced/ii/custom-replay-command.md
|
||||
@docs/advanced/ii/pathfinding.md
|
||||
@docs/advanced/ii/adding-hero-attribute.md
|
||||
@docs/advanced/ii/adding-enemy-attribute.md
|
||||
@docs/advanced/ii/enemy-context-and-combat.md
|
||||
|
||||
**流程约定(D-46 第 2 阶段 / D-57):**
|
||||
- 用户主导:用户提问题,AI 不替用户判断「这不算问题」;用户说改就改,用户说不用改就不改。
|
||||
- 每轮:记录用户意见 → 派子代理逐条修改 → 回报 → 等用户继续提或确认。
|
||||
- 条目状态固定为 `状态:待处理` / `状态:已处理` / `状态:越界-不实施`(可带列表符号);最终确认单独写一行 `用户最终确认:通过` 或 `用户最终确认:满意`。收尾只检查以状态标记开头的条目行与这行最终标记,不把历史轮次里的字样当作未完成项。
|
||||
- **必须获用户明确确认(「通过」或「满意」)后才可收尾**;AI 不得自行收尾。
|
||||
- 用户指出的越界内容(超出高级 II 九篇范围)只记录、不实施(D-14)。
|
||||
- 修订不得删除 D-67 要求覆盖的内容;高级 III 仍只用代码 span 或 `TODO(advanced-III)` 注释。
|
||||
- 命令一律经 `cmd /c "..."` 执行;只改 `docs/advanced/ii/*.md` 与审阅记录,不改 `config.ts`、生产代码与渲染端内容。
|
||||
|
||||
**交付方式:**
|
||||
- 本地预览:`cmd /c "pnpm docs:preview"`(预览 `public/_docs`);或直接浏览构建产物。
|
||||
- 审阅对象:高级 II 九篇;重点看内容是否正确、链路是否成立、时序/优先级是否讲对、是否有死链。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报用户修订循环计划并等待「可以执行」</name>
|
||||
<action>向用户汇报:**要做什么**(执行高级 II 的用户主导修订循环:用户逐条提问题 → AI 派子代理修改 → 回报 → 用户继续提,直到用户确认满意;确认后做最终格式与构建收口)、**为什么**(D-46 第 2 阶段 / D-57 要求必须经用户循环且获用户确认才可收尾,不得由 AI 自行收尾)、**大致怎么做**(Task 1 交付构建产物并收集用户意见;Task 2 逐条修改并记录;Task 3 回报并等待用户继续提或确认;用户确认后 Task 4 做最终 prettier / 构建 / SUMMARY)、**影响范围**(可能修改 `docs/advanced/ii/*.md` + 新增 `.planning` 下的用户审阅记录与 SUMMARY;不改 `config.ts`、生产代码与渲染端内容)。汇报中明确:本计划的修改方式为**派发子代理执行**,范围严格限于 `docs/advanced/ii/*.md` 与 `.planning/.../10-27-USER-REVIEW.md`、`10-27-SUMMARY.md`。停下等待用户明确回复「可以执行」四个字;未收到前不得交付审阅、不得派发修改子代理、不得写任何文件。</action>
|
||||
<what-built>本计划内容:高级 II 用户主导修订循环 + 用户确认后的收尾。</what-built>
|
||||
<how-to-verify>阅读上面的四段说明,确认「用户确认后才收尾」的流程;如对交付方式(预览命令 / 审阅范围)有要求请一并说明。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<verify>
|
||||
<human-check>用户已回复「可以执行」。</human-check>
|
||||
</verify>
|
||||
<done>用户回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 1: 交付高级 II 构建产物,收集用户意见</name>
|
||||
<action>先跑 `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` 确认构建 green、`build complete`、dead link = 0,然后向用户交付:九篇的路径清单、`ii/index.md` 的阅读顺序、本地预览方式 `cmd /c "pnpm docs:preview"`,以及 10-26 的读者审阅结论摘要(阻断问题已清零、非阻断残项列表)。请用户以本领域视角逐条提出:内容是否正确、是否有讲错或讲漏、照着做会不会卡住、时序与优先级是否讲对、系统边界(数据端 only、插件优先、存档/录像/伤害关系)是否讲清、有没有死链或排版问题。**不要替用户判断某条算不算问题**;用户提出的每一条都记录到 `.planning/phases/10-docs-restructure/10-27-USER-REVIEW.md`(意见、定位、`状态:待处理`)。若用户直接回复「通过」或「满意」,就在记录中写独占一行的 `用户最终确认:通过` 或 `用户最终确认:满意`,并跳到 Task 3 的确认分支;否则本任务结束时带着用户意见清单进入 Task 2。Commit:`docs(10-27): 记录用户修订意见(第1轮)`。</action>
|
||||
<what-built>构建后的高级 II 九篇与预览方式;10-26 的读者审阅结论摘要。</what-built>
|
||||
<how-to-verify>本地 `cmd /c "pnpm docs:preview"` 预览,或直接阅读 `docs/advanced/ii/*.md`;按「内容正确 / 链路可照做 / 时序与优先级 / 链接与排版」四类逐条提问。</how-to-verify>
|
||||
<resume-signal>请逐条列出问题(可带文件名与章节);若已满意,请直接回复「通过」或「满意」。</resume-signal>
|
||||
<verify>
|
||||
<human-check>用户已给出至少一条具体意见,或明确回复「通过」/「满意」。</human-check>
|
||||
</verify>
|
||||
<done>用户意见已记录,或用户已明确确认通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 按用户意见逐条派子代理修改并回报</name>
|
||||
<files>docs/advanced/ii/*.md(按用户意见定位), .planning/phases/10-docs-restructure/10-27-USER-REVIEW.md</files>
|
||||
<action>读取 `10-27-USER-REVIEW.md` 中 `状态:待处理` 的用户意见,逐条派子代理修改对应 `docs/advanced/ii/*.md`:每条意见都要落到具体文件与段落;用户说改哪里就改哪里,不擅自扩大范围;用户指出但属于高级 III、渲染端或生产代码的内容只记录、不实施(D-14),状态行写 `状态:越界-不实施`。每轮修改后:(a) 跑构建门禁 `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,须 green、`build complete`、dead link = 0;(b) 在 `10-27-USER-REVIEW.md` 把该条状态行改为 `状态:已处理` 并写明处置与改动文件;(c) 向用户回报本轮改了哪些、怎么改的、构建结论。若用户又提出新意见,追加到记录并继续处理——这个循环由 Task 2 / Task 3 反复执行,直到用户确认。守住 D-67 与 D-59..D-66;不删除要求覆盖的内容;不把高级 III 占位改成死链;不改 `config.ts`、生产代码与渲染端内容。每轮 Commit:`docs(10-27): 按用户意见修订高级 II(第N轮)`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $f='.planning/phases/10-docs-restructure/10-27-USER-REVIEW.md'; if(-not(Test-Path -LiteralPath $f)){ Write-Error "MISSING $f" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $f)); if($raw -notmatch '状态:已处理' -and $raw -notmatch '状态:越界-不实施' -and $raw -notmatch '用户最终确认:通过' -and $raw -notmatch '用户最终确认:满意'){ Write-Error "NO-DISPOSITION" }; if($raw -notmatch 'docs/advanced/ii/'){ Write-Error "NO-TARGET" }; $names=@('index','custom-range','static-vs-dynamic-tiles','equipment-system','custom-replay-command','pathfinding','adding-hero-attribute','adding-enemy-attribute','enemy-context-and-combat'); foreach($n in $names){ $p="docs/advanced/ii/$n.md"; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $body=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($body -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($body,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" } }; $ErrorActionPreference='Continue'; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'FIX-II-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "NO-DISPOSITION", "NO-TARGET", "LF-ONLY", "H1-COUNT", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "FIX-II-OK"</fails_when>
|
||||
</verify>
|
||||
<done>本轮用户意见逐条落到具体段落;记录中状态与改动文件清楚;构建 green、`build complete`、dead link = 0。</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 3: 回报修改结果,等待用户继续提或确认满意</name>
|
||||
<action>向用户回报本轮处置:逐条列出用户意见 → 改了哪个文件/段落 → 怎么改的 → 构建结论;附上更新后的预览方式。然后请用户继续审阅:如果还有问题,请继续逐条提出(执行者回到 Task 2 继续改,循环不限轮次,直到用户满意);如果已经满意,请明确回复「通过」或「满意」。**只有用户明确回复「通过」或「满意」才可进入 Task 4 收尾**;用户沉默、只回复「可以」、或回复其它内容(包括「不满意」「未通过」)都不算确认,不得自行收尾。确认后在 `10-27-USER-REVIEW.md` 写独占一行的 `用户最终确认:通过` 或 `用户最终确认:满意`,并附用户原文。Commit:`docs(10-27): 记录用户确认结论`(仅在用户确认后提交)。</action>
|
||||
<what-built>按用户意见修订后的高级 II 九篇与逐条处置说明。</what-built>
|
||||
<how-to-verify>按上一轮的预览方式重新查看改动处;确认每条意见都已处理,且没有引入新的错误或死链。</how-to-verify>
|
||||
<resume-signal>若仍有问题,请逐条列出;若已满意,请回复「通过」或「满意」。</resume-signal>
|
||||
<verify>
|
||||
<human-check>用户已明确回复「通过」或「满意」(不含「不满意」「未通过」),或提出了新的具体意见。</human-check>
|
||||
</verify>
|
||||
<done>用户确认满意(记录原文),或用户提出了新的具体意见(回到 Task 2)。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 4: 用户确认后的最终格式、构建与 SUMMARY</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-27-USER-REVIEW.md, .planning/phases/10-docs-restructure/10-27-SUMMARY.md, docs/advanced/ii/*.md</files>
|
||||
<action>**前置:`10-27-USER-REVIEW.md` 已有独占一行的 `用户最终确认:通过` 或 `用户最终确认:满意`;未确认则不得执行本任务。** (1) 最终格式:`node node_modules/prettier/bin/prettier.cjs --write "docs/advanced/ii/*.md"`,再 `--check` 确认通过。(2) 最终构建:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,确认 green、`build complete`、dead link = 0。(3) 最终结构:九篇 kebab-case、CRLF、单 H1;`config.ts` 的 `/advanced/ii/` 侧边栏仍为 9 个目标;无渲染端内容;高级 III 仍无真实链接。(4) 在 `10-27-USER-REVIEW.md` 记录最终结论:`用户最终确认:通过/满意` 标记与用户原文、最终构建结论、非阻断残项(只记录,标注 `状态:越界-不实施` 或保留为非阻断说明,不写 `状态:待处理`)。(5) 写 `10-27-SUMMARY.md`,含:九篇清单、用户循环轮次与处置摘要、10-26 读者审阅结论、最终构建 / prettier / 链接结论、未处理项(仅越界或非阻断)、以及「用户已确认收尾」的明确记录。Commit:`docs(10-27): 高级 II 用户修订循环收尾`。</action>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $rev='.planning/phases/10-docs-restructure/10-27-USER-REVIEW.md'; $sum='.planning/phases/10-docs-restructure/10-27-SUMMARY.md'; if(-not(Test-Path -LiteralPath $rev)){ Write-Error "MISSING $rev" }; if(-not(Test-Path -LiteralPath $sum)){ Write-Error "MISSING $sum" }; $r=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $rev)); if($r -notmatch '(?m)^\s*(?:[-*]\s*)?用户最终确认:(通过|满意)\s*$'){ Write-Error "NO-USER-CONFIRMATION" }; if($r -match '(?m)^\s*(?:[-*]\s*)?状态:待处理\s*$'){ Write-Error "PENDING-USER-ITEMS" }; $s=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $sum)); foreach($needle in @('用户','确认','构建','九篇')){ if($s -notmatch [regex]::Escape($needle)){ Write-Error "SUMMARY-MISSING $needle" } }; $names=@('index','custom-range','static-vs-dynamic-tiles','equipment-system','custom-replay-command','pathfinding','adding-hero-attribute','adding-enemy-attribute','enemy-context-and-combat'); foreach($n in $names){ $p="docs/advanced/ii/$n.md"; if(-not(Test-Path -LiteralPath $p)){ Write-Error "MISSING $p" }; $body=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($body -match "(?<!`r)`n"){ Write-Error "LF-ONLY $p" }; if(([regex]::Matches($body,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $p" }; if($body -match 'src/client'){ Write-Error "RENDERER-CONTENT $p" } }; $cfg=[IO.File]::ReadAllText((Resolve-Path 'docs/.vitepress/config.ts')); $targets=[regex]::Matches($cfg,"link:\s*'/advanced/ii/([^']*)'") | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique; if($targets.Count -ne 9){ Write-Error ("II-LINK-COUNT " + $targets.Count) }; $ErrorActionPreference='Continue'; $pf = cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/ii/index.md docs/advanced/ii/custom-range.md docs/advanced/ii/static-vs-dynamic-tiles.md docs/advanced/ii/equipment-system.md docs/advanced/ii/custom-replay-command.md docs/advanced/ii/pathfinding.md docs/advanced/ii/adding-hero-attribute.md docs/advanced/ii/adding-enemy-attribute.md docs/advanced/ii/enemy-context-and-combat.md" 2>&1; $pfe=$LASTEXITCODE; $out = cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build" 2>&1; $ec=$LASTEXITCODE; $ErrorActionPreference='Stop'; if($pfe -ne 0){ $pf | Write-Output; Write-Error "PRETTIER-FAIL" }; if($ec -ne 0){ $out | Write-Output; Write-Error "BUILD-FAIL" }; $txt=($out | ForEach-Object { $_.ToString() }) -join "`n"; if($txt -notmatch 'build complete'){ Write-Error "NO-BUILD-COMPLETE" }; if($txt -match 'dead link'){ Write-Error "DEAD-LINK" }; Write-Output 'CLOSEOUT-II-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "NO-USER-CONFIRMATION", "PENDING-USER-ITEMS", "SUMMARY-MISSING", "LF-ONLY", "H1-COUNT", "RENDERER-CONTENT", "II-LINK-COUNT", "PRETTIER-FAIL", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "CLOSEOUT-II-OK"</fails_when>
|
||||
</verify>
|
||||
<done>用户确认已记录且无待处理项;九篇结构正确、无渲染端内容;prettier 检查通过;构建 green、`build complete`、dead link = 0;SUMMARY 写明用户确认收尾。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 用户 → 执行者 | 用户意见是修订输入;执行者不得替用户判断某条不算问题 |
|
||||
| 执行者 → 子代理 | 修改子代理按用户意见改文档,不扩大范围 |
|
||||
| 构建期 markdown → 静态站点 | 正文由构建期静态渲染,无运行时输入 |
|
||||
| 站点 → 浏览器读者 | 站内链接是唯一可点击指向面 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-79 | Tampering / XSS | 用户修订后的高级 II 正文 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-80 | Spoofing | 用户确认记录 | low | mitigate | 只把用户明确回复的「通过」/「满意」记为确认;沉默或其它回复不算 |
|
||||
| T-10-81 | Information Disclosure | 审阅记录 / 正文 | low | mitigate | 只引用仓库已公开路径与接口;不写密钥、账号 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 机制门禁:`10-27-USER-REVIEW.md` 含每轮用户意见、定位、处置、构建结论与最终确认原文。
|
||||
- 确认门禁:记录含独占一行的 `用户最终确认:通过` 或 `用户最终确认:满意`;条目级 `状态:待处理` 为 0(越界项记 `状态:越界-不实施`)。
|
||||
- 构建门禁:每轮及收尾均 `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` 通过。
|
||||
- 格式门禁:prettier `--check` 对九篇通过。
|
||||
- 结构门禁:九篇 kebab-case、CRLF、单 H1;侧边栏 9 个目标正确。
|
||||
- 越界门禁:不改 `config.ts`、生产代码与渲染端内容;越界意见只记录、不实施。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 用户主导修订循环真实执行,用户明确确认满意;AI 未自行收尾。
|
||||
- 最终九篇可构建、可预览、无死链、无渲染端内容。
|
||||
- 审阅记录与 SUMMARY 完整,能支撑 D-09 的人工验收。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-27-SUMMARY.md` when done
|
||||
</output>
|
||||
Loading…
Reference in New Issue
Block a user