mirror of
https://github.com/motajs/template.git
synced 2026-10-29 20:30:20 +08:00
docs(10): 规划高级 III 数据端文档(10-33..10-41)
This commit is contained in:
parent
dc2cc0b2b7
commit
21dea3adf0
191
.planning/phases/10-docs-restructure/10-33-PLAN.md
Normal file
191
.planning/phases/10-docs-restructure/10-33-PLAN.md
Normal file
@ -0,0 +1,191 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 33
|
||||
type: execute
|
||||
wave: 5
|
||||
depends_on: [10-32]
|
||||
files_modified:
|
||||
- docs/advanced/index.md
|
||||
- docs/advanced/i/index.md
|
||||
- docs/advanced/i/declarative-vs-imperative.md
|
||||
- docs/advanced/i/plugin-system.md
|
||||
- docs/advanced/i/saveable-content.md
|
||||
- docs/advanced/i/replay-consistency.md
|
||||
- docs/advanced/i/data-stores.md
|
||||
- docs/advanced/i/hooks-in-depth.md
|
||||
- docs/advanced/i/mover.md
|
||||
- docs/advanced/i/face-handler.md
|
||||
- docs/advanced/ii/index.md
|
||||
- docs/advanced/ii/custom-replay-command.md
|
||||
- docs/advanced/ii/static-vs-dynamic-tiles.md
|
||||
- docs/.vitepress/config.ts
|
||||
- .planning/phases/10-docs-restructure/10-33-SUMMARY.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 42000
|
||||
raw_tokens: 28000
|
||||
tasks: 3
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "高级 I 的导览与八篇正文全部位于 `docs/advanced/i/`,`docs/advanced/` 下不再残留这九个旧文件(D-53 分层落位)"
|
||||
- "`docs/advanced/index.md` 被重建为「高级教程总导览」:说明 I / II / III 三层、各自定位与入口;I 与 II 用真实相对链接,III 只写层名与主题、暂不放链接(页面尚未创建,任何链接都会成为死链)"
|
||||
- "`docs/.vitepress/config.ts` 的 `nav` 仍指向 `/advanced/`;`sidebar['/advanced/']` 的 I 组九项改指 `/advanced/i/...`,并在组前新增一条指向 `/advanced/` 的总导览入口;II 组十项与 III 组 `items: []` 原样不动"
|
||||
- "移动后所有相对链接仍可解析:I 内页 `../intermediate/x.md` 改为 `../../intermediate/x.md`、`./ii/x.md` 改为 `../ii/x.md`;II 内页引用 I 的九处改为 `../i/x.md`;`ii/index.md` 的返回导览改为 `../i/index.md`"
|
||||
- "任一计划边界处都不存在死链:配置里每个 `/advanced/...` 目标都能映射到实际文件(`x.md` 或 `x/index.md`);III 组仍为空、总导览不含 `./iii/` 链接"
|
||||
- "prettier `--check` 通过;构建 green、输出 `build complete`、无 `dead link`;九个 I 页与总导览仍是 CRLF、单 H1、kebab-case 文件名"
|
||||
- "不改 `src/`、`packages*/`、`docs/advanced/ii/` 的正文内容(除链接路径外)、`docs/intermediate/` 与其它站点配置;越界问题只记录、不实施(D-14)"
|
||||
artifacts:
|
||||
- "docs/advanced/i/index.md"
|
||||
- "docs/advanced/i/declarative-vs-imperative.md"
|
||||
- "docs/advanced/i/plugin-system.md"
|
||||
- "docs/advanced/i/saveable-content.md"
|
||||
- "docs/advanced/i/replay-consistency.md"
|
||||
- "docs/advanced/i/data-stores.md"
|
||||
- "docs/advanced/i/hooks-in-depth.md"
|
||||
- "docs/advanced/i/mover.md"
|
||||
- "docs/advanced/i/face-handler.md"
|
||||
- "docs/advanced/index.md(新建,高级教程总导览)"
|
||||
- "docs/.vitepress/config.ts(I 组改指 `/advanced/i/`,新增总导览项,III 组保持空)"
|
||||
- ".planning/phases/10-docs-restructure/10-33-SUMMARY.md"
|
||||
key_links:
|
||||
- "采用 `git mv` 移动而不是复制重写:保住 9 个文件的 CRLF 与 git 历史(dev.md:116 要求 CRLF)"
|
||||
- "移动与「新总导览 + config 改写」必须在同一任务内完成:中间态下 `nav: '/advanced/'` 与 I 组链接都会指向不存在的页面,任何构建都会失败"
|
||||
- "III 组的 `items: []` 与总导览的 III 行保持无链接,是保证「每个计划边界都无死链」的关键;由 10-39 在页面齐备后统一注册"
|
||||
---
|
||||
|
||||
<objective>
|
||||
把高级教程改成三层目录结构的第一半:用 `git mv` 把高级 I 的导览与八篇正文移入 `docs/advanced/i/`,把 `docs/advanced/index.md` 重建为「高级教程总导览」,同步改写 `config.ts` 的 I 组链接并新增总导览入口,最后修掉全站因移动而失效的相对链接并跑通构建。
|
||||
|
||||
Purpose: 高级 III 需要与高级 I / II 同级的位置(`docs/advanced/iii/`),而当前高级 I 的九篇直接摊在 `docs/advanced/` 根目录、其导览又占用了 `/advanced/` 这个总入口。不先分层,「高级教程」这个 nav 会一直指向某一层的导览,III 也没有可并列的落点。
|
||||
Output: `docs/advanced/i/` 下的九个既有页面、重建的总导览、更新后的侧边栏、修好的互链、`10-33-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-ADVANCED-III-RESEARCH.md
|
||||
@docs/advanced/index.md
|
||||
@docs/advanced/ii/index.md
|
||||
@docs/.vitepress/config.ts
|
||||
@AGENTS.md
|
||||
@dev.md
|
||||
|
||||
**现状(已核实):**
|
||||
- `docs/advanced/` 根目录有 9 个 I 页:`index.md`(高级 I 导览)与八个正文,全部 CRLF、单 H1。
|
||||
- `docs/advanced/ii/` 有 10 个文件;其中 `ii/index.md` 有 6 处「`../` + 高级 I 页文件名」与 1 处 `../index.md`(返回高级 I 导览),`ii/custom-replay-command.md` 有 2 处 `../replay-consistency.md`,`ii/static-vs-dynamic-tiles.md` 有 1 处 `../mover.md`。这是**全部**跨层入链,`docs/intermediate/` 内只有 HTML 注释占位、没有真实链接。
|
||||
- I 页内部:`../intermediate/x.md` 与 `./ii/x.md` 两类跨层相对链接,规划时实测为 16 处与 7 处(数量会随页面增补变化,执行时以 grep 实测为准),**一律全部改写**,不按固定条数核对;八篇正文各有 `返回[高级 I 导览](./index.md)。`(移动后同层不变,勿改)。
|
||||
- `config.ts`:`nav` 已是 `{ text: '高级教程', link: '/advanced/' }`;`sidebar['/advanced/']` 含 I 组 9 项(首项 `高级 I 导览` → `/advanced/`,其余 8 项 → `/advanced/文件名`)、II 组 10 项、III 组 `items: []`。
|
||||
|
||||
**硬约束:**
|
||||
- 只动 `files_modified` 列出的文件;不改 II 的正文语义(仅链接路径)、不改 `intermediate/`、不改生产代码。
|
||||
- 移动用 `git mv`;改链接只替换行内路径文本,不重写整行、不动行尾,保持 CRLF。
|
||||
- 新总导览不写「模板」「[需改]」,不写源码叙述(D-80),不写「渲染端以后讲」式元叙述(D-16);边界单列文末(D-61);加粗遵守 flanking(D-52)。高级教程不设模板章节:D-83 在本层取代 D-50 / D-63 / D-64 的模板要求,因此「模板顺序」「模板 vs 纯接口」的取舍不适用于高级 I / II / III。
|
||||
- 命令一律经 `cmd /c "..."` 执行(本机 PowerShell 禁止 `pnpm.ps1`)。
|
||||
- research 文件是权威事实源,但行号可能漂移;涉及事实以当前源码/文档为准。注释与实现不一致的条目(research 的 C1–C8)**只在对话中报告**(D-72),不写进文档。
|
||||
|
||||
**研究开放问题处置(Q1–Q6,本批统一口径;research 文件本身不修改,以本表为准):**
|
||||
- Q1「目录 / 文件名是否批准」→ 已按用户派发批准:`docs/advanced/iii/` + 8 个 kebab-case 文件名,见各计划 `files_modified`(D-13 的批准即本批派发范围)。
|
||||
- Q2「高级 I 是否同批移动」→ 同批,由本计划执行;因此 III 页一律使用 `../i/...`,不使用 `../...`。
|
||||
- Q3「`anon-tokyo` 是否独立成页」→ 独立成页(10-36);若成稿明显偏短,只向用户提出,不自行合并。
|
||||
- Q4「`plugin-internals` 是否独立成页」→ 独立成页(10-38);与 `core-state` 的边界是「排序 / 失败 / 状态」对「装配顺序」。
|
||||
- Q5「渲染端加载小节写到什么深度」→ 只写接口级消费(10-34),不写场景结构与 UI 生命周期。
|
||||
- Q6「C1–C8 是否要修」→ 本批不修、不写入文档;只在各计划 Task 0 汇报(D-72)。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报目录重构计划并等待「可以执行」</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-CONTEXT.md(D-12、D-13、D-53、D-60、D-61、D-82)、.planning/phases/10-docs-restructure/10-ADVANCED-III-RESEARCH.md(Page Split、Pitfall 7)、docs/.vitepress/config.ts</read_first>
|
||||
<action>向用户汇报四段并等待批准(D-12):**要做什么**——把高级 I 的导览与八篇正文用 `git mv` 移入 `docs/advanced/i/`,把 `docs/advanced/index.md` 重建为「高级教程总导览」,把 `config.ts` 的 I 组九项改指 `/advanced/i/...`、在组前加一条指向 `/advanced/` 的总导览入口,并修掉全部因移动而失效的相对链接(两类路径全部改写,条数以执行时实测为准),最后格式化 + 全量构建。**为什么**——高级 III 需要与 I / II 并列的落点;当前 `/advanced/` 被 I 导览占用,「高级教程」这个 nav 没有真正的总入口,III 也无处安放。**大致怎么做**——Task 1 原子地完成「移动 + 新总导览 + config 改写 + 链接修正 + prettier --write」(中间态必然死链,故不单独构建);Task 2 跑 prettier `--check` 与全量构建并写 SUMMARY。**影响范围**——9 个文件换目录(内容除链接外不变)、新增 1 个总导览、`config.ts` 的 I 组与新增一项、3 个 II 文件的链接路径;不动 II 正文语义、不动 intermediate、不动生产代码。汇报中明确:**III 组此刻仍保持 `items: []`、总导览的 III 行不放链接**,页面齐备后由 10-39 统一注册,因此本轮不会出现死链;同时列出 research 的 C1–C8 注释/实现疑点(只对话报告,不写入文档,D-72),并说明本计划的修改方式为**派发子代理执行**,范围严格限定在 `files_modified` 与 SUMMARY。停下等待用户明确回复「可以执行」四个字;未收到前不得移动文件、不得改配置、不得派发修改子代理。</action>
|
||||
<what-built>本计划内容:高级 I 九页移入 `docs/advanced/i/`、重建高级教程总导览、侧边栏改指与全站互链修正。</what-built>
|
||||
<how-to-verify>阅读上面的四段说明,确认移动范围、总导览结构(含 III 行刻意无链接)与 config 改动;如不同意请给出替代。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<acceptance_criteria>用户已回复「可以执行」;若对总导览标题、总导览在侧边栏的显示名或 I 组顺序提出替代,已按用户意见修正计划。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已回复「可以执行」,且未对目录名(`docs/advanced/i/`)、总导览标题或侧边栏顺序提出替代。</human-check>
|
||||
</verify>
|
||||
<done>用户回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 移动九页、重建总导览、改写 config 与全部互链</name>
|
||||
<files>docs/advanced/i/*.md(9 个)、docs/advanced/index.md、docs/.vitepress/config.ts、docs/advanced/ii/index.md、docs/advanced/ii/custom-replay-command.md、docs/advanced/ii/static-vs-dynamic-tiles.md</files>
|
||||
<read_first>docs/advanced/index.md(现有 I 导览全文)、docs/advanced/ii/index.md:31-48 与 :128、docs/advanced/ii/custom-replay-command.md:3-5、docs/advanced/ii/static-vs-dynamic-tiles.md:149、docs/.vitepress/config.ts:20-23 与 :154-240、docs/advanced/mover.md:52-73,456、docs/advanced/face-handler.md:112</read_first>
|
||||
<action>(1) **移动**:先 `New-Item -ItemType Directory -Path docs/advanced/i -Force`,再对 `index.md`、`declarative-vs-imperative.md`、`plugin-system.md`、`saveable-content.md`、`replay-consistency.md`、`data-stores.md`、`hooks-in-depth.md`、`mover.md`、`face-handler.md` 逐个执行 `git mv docs/advanced/文件名 docs/advanced/i/文件名`(一次一个,便于核对)。移动后 `docs/advanced/` 根目录**不再有**这 9 个旧文件。
|
||||
(2) **修链接(只改路径文本)**:在 `docs/advanced/i/` 的九个文件里把 `](../intermediate/` **全部**改为 `](../../intermediate/`,把 `](./ii/` **全部**改为 `](../ii/`(两类的实际条数以执行时 grep 实测为准,不要按固定数字核对);`返回[高级 I 导览](./index.md)。` 保持不变(同层仍指向 `i/index.md`)。改完逐文件确认仍是 CRLF、单 H1,且没有把 `i/index.md` 的自身标题改成总导览标题。
|
||||
(3) **新建总导览**:用 Write 重写 `docs/advanced/index.md`(原文件已移走,这是新建)。要求:单 H1 `# 高级教程总导览`;引言说明高级教程面向有高度定制需求、需要理解引擎设计的作者,按**难度**分 I / II / III 三层(不是领域分类);`## 三层结构` 用表格给三层定位与主题,I 行链接 `[高级 I · 扩展基础](./i/)`、II 行链接 `[高级 II · 深入定制](./ii/)`、III 行**只写层名与主题、不放链接**;`## 怎么选阅读路径` 给「先 I 后 II、III 按需」与「按目标跳读」两条建议;`## 前置` 链接中级三篇(`../intermediate/data-vs-render.md`、`../intermediate/code-basics.md`、`../intermediate/data-plugins.md`);`## 与中级教程的分工` 一段说明中级讲「怎么用」、高级讲「怎么扩展」;`## 边界与注意` 放文末,写渲染端与编辑器内容为占位、不重复中级已讲内容、III 的入口随该层页面就绪开放。不要出现「模板」「[需改]」,不要出现 `src/client`,不要写「源码第 X 行」这类叙述。
|
||||
(4) **改 config**:`sidebar['/advanced/']` 的第一项改为 `{ text: '高级教程总导览', link: '/advanced/' }`(置于三个分组之前,作为键内的首项);I 组的 `高级 I 导览` 改指 `/advanced/i/`,其余八项改指 `/advanced/i/declarative-vs-imperative` 等;**新增**九项的总导览项后 I 组仍是 9 项;II 组 10 项逐字不动;III 组保持 `collapsed: true` 与 `items: []`;`nav` 不动。
|
||||
(5) **改 II 入链**:`ii/index.md` 六处「`../` + 高级 I 页文件名」改为「`../i/` + 同名」,末尾 `../index.md` → `../i/index.md`(语义仍是「返回高级 I 导览」);`ii/custom-replay-command.md` 两处 `../replay-consistency.md` → `../i/replay-consistency.md`;`ii/static-vs-dynamic-tiles.md` 一处 `../mover.md` → `../i/mover.md`。不改这些文件的其它文字。
|
||||
(6) 跑 `node node_modules/prettier/bin/prettier.cjs --write` 覆盖:新总导览、`docs/advanced/i/*.md`、`docs/advanced/ii/index.md`、`docs/advanced/ii/custom-replay-command.md`、`docs/advanced/ii/static-vs-dynamic-tiles.md`、`docs/.vitepress/config.ts`。格式化后重新确认 CRLF 未被改写为 LF。Commit:`docs(10-33): 高级 I 移入 i/ 并重建总导览与侧边栏`。</action>
|
||||
<acceptance_criteria>九个 I 页在 `docs/advanced/i/` 下且 `docs/advanced/` 根目录无旧文件;总导览为单 H1、含三层表(I/II 有链接、III 无链接)、前置链接与文末边界;config 中 I 组 9 个目标全为 `/advanced/i/...`、存在总导览项、II 组 10 项未变、III 组为空;II 的九处跨层链接与一处返回导览全部改指 `../i/...`;所有相对 `.md` 链接与目录链接可解析;prettier 已格式化。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; if(-not(Test-Path -LiteralPath 'docs/advanced/i')){ Write-Error 'MISSING-DIR' }; if(-not(Test-Path -LiteralPath 'docs/advanced/i/index.md')){ Write-Error 'MISSING i/index.md' }; $moved=@('declarative-vs-imperative','plugin-system','saveable-content','replay-consistency','data-stores','hooks-in-depth','mover','face-handler'); foreach($n in $moved){ if(-not(Test-Path -LiteralPath "docs/advanced/i/$n.md")){ Write-Error "MISSING i/$n.md" }; if(Test-Path -LiteralPath "docs/advanced/$n.md"){ Write-Error "STALE-ROOT $n.md" } }; if(-not(Test-Path -LiteralPath 'docs/advanced/index.md')){ Write-Error 'MISSING-ROOT-INDEX' }; $iGuide=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/advanced/i/index.md')); if($iGuide -notmatch '# 高级 I 导览'){ Write-Error 'NO-I-GUIDE-TITLE' }; $rootGuide=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/advanced/index.md')); if($rootGuide -match '(?m)^# 高级 I 导览'){ Write-Error 'STALE-I-GUIDE-AT-ROOT' }; $files=Get-ChildItem -LiteralPath 'docs/advanced' -Recurse -File -Filter '*.md'; foreach($f in $files){ $raw=[IO.File]::ReadAllText($f.FullName); if($raw -match "(?<!`r)`n"){ Write-Error "LF-ONLY $($f.Name)" }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error "H1-COUNT $($f.Name)" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path $f.DirectoryName $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error "DEAD-LINK $($f.Name) -> $($m.Groups[1].Value)" } }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]*/)\)')){ $t=Join-Path $f.DirectoryName $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error "DEAD-DIR $($f.Name) -> $($m.Groups[1].Value)" } } }; $iRaw=Get-ChildItem -LiteralPath 'docs/advanced/i' -File -Filter '*.md' | ForEach-Object { [IO.File]::ReadAllText($_.FullName) }; $iAll=$iRaw -join "`n"; if($iAll -match '\]\(\.\./intermediate/'){ Write-Error 'STALE-INTERMEDIATE-PATH' }; if($iAll -match '\]\(\./ii/'){ Write-Error 'STALE-II-PATH' }; $cfg=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/.vitepress/config.ts')); $links=[regex]::Matches($cfg,"link:\s*'(/advanced/[^']*)'") | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique; foreach($l in $links){ $rel=$l.TrimEnd('/'); $p1="docs/$rel.md"; $p2="docs/$rel/index.md"; if(-not(Test-Path -LiteralPath $p1) -and -not(Test-Path -LiteralPath $p2)){ Write-Error "CFG-DEAD $l" } }; $iLinks=[regex]::Matches($cfg,"link:\s*'(/advanced/i/[^']*)'") | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique; if($iLinks.Count -ne 9){ Write-Error ("I-LINK-COUNT " + $iLinks.Count) }; if($links -notcontains '/advanced/'){ Write-Error 'NO-ROOT-ENTRY' }; if(([regex]::Matches($cfg,"link:\s*'/advanced/ii/[^']*'")).Count -ne 10){ Write-Error 'II-LINKS-CHANGED' }; if($cfg -notmatch "text:\s*'高级 III · 原理与架构'"){ Write-Error 'NO-III-GROUP' }; $idx=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/advanced/index.md')); foreach($needle in @('# 高级教程总导览','./i/','./ii/','../intermediate/data-vs-render.md','边界与注意')){ if($idx -notmatch [regex]::Escape($needle)){ Write-Error "INDEX-MISSING $needle" } }; if($idx -match '\]\(\./iii'){ Write-Error 'INDEX-HAS-III-LINK' }; if($idx -match 'src/client'){ Write-Error 'INDEX-RENDERER' }; if($idx -match '\[需改\]|模板'){ Write-Error 'INDEX-TEMPLATE-MARK' }; Write-Output 'RESTRUCTURE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "STALE-ROOT", "LF-ONLY", "H1-COUNT", "DEAD-LINK", "DEAD-DIR", "STALE-INTERMEDIATE-PATH", "STALE-II-PATH", "CFG-DEAD", "I-LINK-COUNT", "NO-ROOT-ENTRY", "II-LINKS-CHANGED", "NO-III-GROUP", "INDEX-MISSING", "MISSING-ROOT-INDEX", "NO-I-GUIDE-TITLE", "STALE-I-GUIDE-AT-ROOT", "INDEX-HAS-III-LINK", "INDEX-RENDERER", or "INDEX-TEMPLATE-MARK"; or output lacks "RESTRUCTURE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>九个 I 页就位、根目录无旧文件;总导览成立且 III 无链接;config I 组 9 项 + 总导览项、II 组与 III 组未变;全部相对链接与目录链接可解析;CRLF 与单 H1 保持。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 格式检查、全量构建与 SUMMARY</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-33-SUMMARY.md</files>
|
||||
<read_first>docs/.vitepress/init.ts(确认 apiSidebar 生成入口)、.planning/phases/10-docs-restructure/10-30-SUMMARY.md(SUMMARY 写法)</read_first>
|
||||
<action>(1) 格式门禁:对 `docs/advanced/index.md`、`docs/advanced/i/*.md`、`docs/advanced/ii/index.md`、`docs/advanced/ii/custom-replay-command.md`、`docs/advanced/ii/static-vs-dynamic-tiles.md`、`docs/.vitepress/config.ts` 跑 `node node_modules/prettier/bin/prettier.cjs --check`,必须全通过;若 `config.ts` 因既有格式基线报错,先单独 `--write` 该文件再 `--check`,并在 SUMMARY 记录「仅格式化、无语义改动」。
|
||||
(2) 构建门禁:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,要求退出码 0、输出含 `build complete`、输出不含 `dead link`(VitePress 会打印 `[vitepress] Dead link found` 一类信息)。若因既有无关文件(工作区里 `packages-user/client-base` 的改动)导致失败,先判断是否与本计划修改无关,再决定是否停下向用户汇报——**不得**为了让构建过而改动本计划范围外的文件。
|
||||
(3) 人工点检:浏览器或产物目录确认 `/advanced/` 为总导览、`/advanced/i/` 为高级 I 导览、九个 I 页均可从侧边栏进入、`/advanced/ii/` 十篇仍可进入且返回导览指向 `i/index.md`。
|
||||
(4) 写 `.planning/phases/10-docs-restructure/10-33-SUMMARY.md`:移动清单(git mv 的 9 条,均为仅移动、内容不变)、链接修正的分类与数量(执行时实测,不写死条数)、总导览结构与它刻意不含的 III 链接、config 改动点(I 组 9 项 + 总导览项 + III 组保持为空)、prettier 与构建结论、侧边栏目标清单、遗留事项(III 注册与总导览 III 入口留给 10-39)。Commit:`docs(10-33): 目录重构收口与构建验证`。</action>
|
||||
<acceptance_criteria>prettier `--check` 对 six 个目标文件(组)全通过;构建退出码 0、含 `build complete`、无 `dead link`;SUMMARY 记录移动/链接/配置/格式/构建与遗留事项。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $sum='.planning/phases/10-docs-restructure/10-33-SUMMARY.md'; if(-not(Test-Path -LiteralPath $sum)){ Write-Error "MISSING $sum" }; $s=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $sum)); foreach($needle in @('git mv','总导览','config','构建','10-39')){ if($s -notmatch [regex]::Escape($needle)){ Write-Error "SUMMARY-MISSING $needle" } }; $ErrorActionPreference='Continue'; $pf = cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/index.md docs/advanced/i docs/advanced/ii/index.md docs/advanced/ii/custom-replay-command.md docs/advanced/ii/static-vs-dynamic-tiles.md docs/.vitepress/config.ts" 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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'BUILD-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "SUMMARY-MISSING", "PRETTIER-FAIL", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "BUILD-OK"</fails_when>
|
||||
</verify>
|
||||
<done>格式与构建双绿;SUMMARY 完整;无越界改动。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 配置文件 → 站点结构 | `sidebar` / `nav` 决定哪些页面可被浏览;写错即产生死链 |
|
||||
| 构建期 markdown → 静态站点 | 正文与相对链接在构建期解析,无运行时输入 |
|
||||
| 读者 → 站点 | 站内链接是唯一可点击指向面 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-97 | Tampering | `config.ts` 的 `/advanced/` 链接集合 | medium | mitigate | 静态断言每个 `/advanced/...` 目标能映射到实际文件或目录 index;I 组必须恰好 9 个 `/advanced/i/...` 目标 |
|
||||
| T-10-98 | Spoofing | 移动后的返回导览链接 | low | mitigate | 断言 I 内页 `./index.md` 仍指向 `i/index.md`;II 的返回导览改指 `../i/index.md` |
|
||||
| T-10-99 | Information Disclosure | 总导览与 SUMMARY | low | mitigate | 只引用仓库已公开路径与接口;不写密钥、账号 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 结构门禁:`docs/advanced/i/` 恰有 9 个 I 页,`docs/advanced/` 根目录无这 9 个旧文件。
|
||||
- 链接门禁:`docs/advanced/**` 内每个相对 `.md` 链接与目录链接目标存在;config 内每个 `/advanced/...` 目标存在。
|
||||
- 配置门禁:I 组 9 个 `/advanced/i/...` 目标 + 一条 `/advanced/` 总导览项;II 组 10 项逐字不变;III 组 `items: []`。
|
||||
- 内容门禁:总导览单 H1、三层表(I/II 有链接、III 无链接)、文末边界、无渲染端内容、无模板标记。
|
||||
- 格式门禁:prettier `--check` 通过。
|
||||
- 构建门禁:`build complete` 且无 `dead link`。
|
||||
- 越界门禁:`git status` 只含本计划 `files_modified` 内的改动与 SUMMARY。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 高级教程在站点上有了真正的总入口,高级 I 完整落在 `docs/advanced/i/`,高级 II 未受影响。
|
||||
- 任一计划边界处都没有死链,III 仍可在 10-39 一次性接入。
|
||||
- 构建与格式双绿,无越界改动。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-33-SUMMARY.md` when done
|
||||
</output>
|
||||
158
.planning/phases/10-docs-restructure/10-34-PLAN.md
Normal file
158
.planning/phases/10-docs-restructure/10-34-PLAN.md
Normal file
@ -0,0 +1,158 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 34
|
||||
type: execute
|
||||
wave: 6
|
||||
depends_on: [10-33]
|
||||
files_modified:
|
||||
- docs/advanced/iii/loading-system.md
|
||||
- .planning/phases/10-docs-restructure/10-34-SUMMARY.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 30000
|
||||
raw_tokens: 20000
|
||||
tasks: 3
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "`docs/advanced/iii/loading-system.md` 先讲系统模型(`LoadTask` → `LoadManager` → `MotaDataLoader` 三层,`ILoadTaskStarter` 作为环境适配点),再讲阶段顺序、配置与处理器、扩展点(D-82)"
|
||||
- "阶段顺序写对且完整:核心配置 → `onCoreConfigLoaded` → 额外配置 → `onExtraConfigLoaded` → 自定义任务 → 完成;并说明 `CoreState.load()` 把插件 `initialize` 放在 `loader.start()` 之前、`afterLoad` 放在之后"
|
||||
- "扩展点覆盖三类且各自完整:新增配置文件(`addCoreConfig` / `addExtraConfig`)、新增资源类型(`LoadTask` + processor + starter + `addCustomTask`)、替换环境(实现 `ILoadTaskStarter`);本地数据包只作为设计能力说明、不给实现代码"
|
||||
- "渲染端内容只出现在独立的接口级小节内(`RenderLoaderHooks` 追加配置、渲染端迭代同一个 `loader.start()` 读进度),不写渲染管线、场景与 `ClientCore` 生命周期;全文除该小节外不出现 `src/client`"
|
||||
- "文末有单一的「边界与注意」小节(D-61),覆盖配置 id 冲突、`addCustomTask` 调用时机、JSONC 前缀缺失、DEV 与构建后路径差异、自定义 starter 的返回契约、字节进度在总量未知时可能不准"
|
||||
- "不写 `getTotalByte` 的「总字节」语义、不承诺 `loading` / `dataLoaded` 守卫只加载一次、不写重复配置 id 稳定告警(D-72:这些疑点只在对话中报告);不写源码行号叙述(D-80);无「模板」「[需改]」标记(D-83)"
|
||||
- "page 仍是 kebab-case、CRLF、单 H1;相对链接全部可解析且至少一条指向 `../i/` 或 `../ii/`;prettier `--check` 通过;构建 green、`build complete`、无 `dead link`"
|
||||
artifacts:
|
||||
- "docs/advanced/iii/loading-system.md"
|
||||
- ".planning/phases/10-docs-restructure/10-34-SUMMARY.md"
|
||||
key_links:
|
||||
- "研究文件的 §1「加载系统」是事实源;执行前必须按当前源码复核接口名与行为,行号可能已漂移"
|
||||
- "本页是 III 唯一允许出现渲染端内容的页面(仅加载小节);这条边界由 10-39 的互链与 10-40 的读者审阅共同复核"
|
||||
- "加载三阶段与插件时机的关系必须链接到 `../i/plugin-system.md`,否则读者会把 `initialize` / `afterLoad` 与加载阶段混为一谈"
|
||||
---
|
||||
|
||||
<objective>
|
||||
写出高级 III 的第一篇数据端原理页《加载系统》:讲清「单任务 → 聚合器 → 阶段编排」的模型、四个阶段的顺序、配置文件与处理器的约定、三类扩展点,以及一段严格限定在接口层的渲染端消费说明,最后把边界集中到文末。
|
||||
|
||||
Purpose: 加载是所有数据端内容的入口。读者真正会做的扩展只有三件——加一份配置、加一种资源、换一个环境(例如本地数据包);不理解阶段顺序就会把「配置在加载前才可追加」这类约束踩成运行时怪象。
|
||||
Output: `docs/advanced/iii/loading-system.md` 与 `10-34-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-ADVANCED-III-RESEARCH.md
|
||||
@docs/advanced/index.md
|
||||
@docs/advanced/i/plugin-system.md
|
||||
@docs/.vitepress/config.ts
|
||||
|
||||
**写作规范(本批统一,逐条自检):**
|
||||
- 库文档风格(D-59),简洁优先(D-60);先模型后接口(D-82);使用 vs 自定义按内容性质分配重心(D-77)——本页两者各半,自定义集中在「加配置 / 加资源 / 换环境」。
|
||||
- 边界、特殊情况一律文末单列(D-61);纯内部实现章节要写「此章节讲解一下 XXX 的内部实现」并尽量后置(D-69)。
|
||||
- 代码块给完整类 / 函数上下文(D-62 / D-70),示例注释尽量充分(D-65);不写「模板」「[需改]」(D-83)。
|
||||
- 不写源码叙述与行号(D-80);不把用户提问或「为解决问题所以这样写」写进正文(D-81)。
|
||||
- 编辑器与 `.jsonc` 内容配置只按「需要编辑器」占位(D-73);序列化事件注册、地图事件 id 绑定不在本篇范围。
|
||||
- 引用其它文档的具体章节用 `./x.md#标题` 或 `../i/x.md#标题`(D-74)。
|
||||
- research 是权威事实源但行号会漂移:**执行前必须打开当前源码复核**每个接口名、方法与行为;发现注释与实现不一致按 D-72 以注释为准,并在汇报中列出,不写进正文。
|
||||
- 本页(且仅本页)允许出现 `src/client`,但只允许出现在「渲染端如何消费加载进度」一节内。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报本篇计划并等待「可以执行」</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-CONTEXT.md(D-12、D-53、D-60、D-61、D-77、D-80、D-82)、.planning/phases/10-docs-restructure/10-ADVANCED-III-RESEARCH.md(§1 加载系统、§2 页切分 loading-system 行、Pitfall 1/4)</read_first>
|
||||
<action>向用户汇报四段并等待批准(D-12):**要做什么**——新建 `docs/advanced/iii/loading-system.md`,按「模型 → 阶段顺序 → 配置与处理器 → 三类扩展点 → 进度 → 渲染端接口级小节 → 边界与注意」组织。**为什么**——加载是数据端入口,读者最容易踩的是「配置只能在加载前追加」「换环境要换 starter」这两条;同时它是本层唯一允许出现渲染端内容的页面,边界必须写死。**大致怎么做**——Task 1 先复核源码事实再落稿并跑 prettier;Task 2 跑全量构建与结构门禁、写 SUMMARY。**影响范围**——只新增这一篇与 SUMMARY;不动 config(III 组由 10-39 注册)、不动既有 I / II 页面、不动源码。汇报中明确:页面标题为《加载系统》、文件名为 `loading-system.md`(D-13 已由用户批准,若用户当场改名按用户意见执行);渲染端只写接口级消费、不写 `ClientCore` 生命周期;不写 `getTotalByte` 的总字节语义、不承诺只加载一次、不写重复配置 id 告警(research C1/C4/C5,只对话报告,D-72)。汇报中说明本计划的修改方式为**派发子代理执行**,范围严格限于本页与 SUMMARY。停下等待用户明确回复「可以执行」四个字;未收到前不得建文件、不得派发写作子代理。</action>
|
||||
<what-built>本计划内容:新建《加载系统》一页(含严格限定的渲染端接口小节)。</what-built>
|
||||
<how-to-verify>确认页面范围、渲染端小节深度,以及刻意不写的内容(总字节语义 / 只加载一次 / 重复 id 告警 / FSA 实现);如需调整请说明。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<acceptance_criteria>用户已回复「可以执行」;若对标题、渲染端小节深度或「本地数据包」写法提出意见,已并入后续任务。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已回复「可以执行」,且未否决页面标题 / 文件 / 渲染端小节范围。</human-check>
|
||||
</verify>
|
||||
<done>用户回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 复核加载事实并落稿 loading-system.md</name>
|
||||
<files>docs/advanced/iii/loading-system.md</files>
|
||||
<read_first>packages/loader/src/task.ts、packages/loader/src/manager.ts、packages/loader/src/types.ts、packages/loader/src/starter.ts、packages-user/data-state/src/loader/loader.ts、packages-user/data-state/src/loader/types.ts、packages-user/data-state/src/loader/jsoncProcessor.ts、packages-user/data-state/src/core.ts(load 管线)、src/data/loader/hook.ts、packages-user/client-modules/src/loader/hook.ts、src/client/ui/load.tsx(仅渲染端小节用)、docs/advanced/i/plugin-system.md(时机章节)、docs/intermediate/data-plugins.md</read_first>
|
||||
<action>(1) 建目录 `docs/advanced/iii/`(若不存在),用 Write 新建 `docs/advanced/iii/loading-system.md`(CRLF、单 H1)。
|
||||
(2) 结构(按此顺序,不写源码行号):
|
||||
- `# 加载系统` + 2–3 段引言:一次加载解决的问题、三层组成、本页只讲接口与扩展点。
|
||||
- `## 系统模型`:先讲三层职责——`LoadTask`(一个资源:取响应 → 处理 → 产出结果)、`LoadManager`(聚合任务、累计进度、以异步迭代方式推进)、`MotaDataLoader`(按阶段编排核心配置、额外配置与自定义任务);再讲 `ILoadTaskStarter` 是「怎么取响应」的环境适配点,网页端用 `fetch` 实现,换环境只需换 starter。给一段 `text` 数据流示意(阶段顺序),不要画渲染端。
|
||||
- `## 一次加载的阶段`:核心配置 → `onCoreConfigLoaded`(可继续追加)→ 额外配置 → `onExtraConfigLoaded` → 自定义任务 → 全部完成;补一句 `CoreState.load()` 的包裹关系:插件 `initialize` 在 `loader.start()` 之前、`afterLoad` 在之后(链接 `../i/plugin-system.md`)。
|
||||
- `## 配置文件与处理器`:配置按系统前缀切分后再解析为 JSON,前缀缺失或 JSON 解析失败会被报错丢弃;`addCoreConfig` / `addExtraConfig` 的差别(核心配置决定内容清单,额外配置在内容清单之后加载);额外配置的 `url` 相对于内容目录;DEV 与构建后路径前缀不同,由加载器统一处理。
|
||||
- `## 扩展点:加一份配置`:给出完整函数示例(含注释),说明在插件 `initialize` 或 `onCoreConfigLoaded` 里追加,且**必须在对应阶段之前**;配置文件本身由编辑器产出(D-73 占位)。
|
||||
- `## 扩展点:加一种资源`:完整示例:构造任务(标识、地址、数据类型:`ArrayBuffer` / `Uint8Array` / `Blob` / `Text` / `JSON` 五选一)、设置处理器、必要时设置 starter、`addCustomTask`;说明返回的 Promise 在该任务完成后兑现。代码块必须是完整函数/类上下文并带解释性注释。
|
||||
- `## 扩展点:换一个加载环境`:实现 starter 的 `start` 返回 `IResponseLike`,传给创建数据端 / 客户端主对象的入口;本地数据包(文件系统访问)只作为**设计能力**一句话说明,**不给实现代码**。
|
||||
- `## 进度`:`LoadManager` 是异步迭代器,进程内可读已加载任务数、已加入任务数、任务比例与字节比例;写清「读进度不参与业务逻辑」,并链接 `../../intermediate/data-vs-render.md` 说明数据端不依赖进度。
|
||||
- `## 渲染端如何消费加载进度`(**本节是全文唯一允许出现 `src/client` 的地方,控制在 1 屏内**):渲染端复用同一套配置与同一个加载器;它自己追加渲染侧配置(`RenderLoaderHooks`),并在加载场景里迭代同一个 `loader.start()`、读取聚合器的计数器画进度。只写接口消费,**不写**场景结构、UI 生命周期、`ClientCore` 初始化顺序。编辑器/渲染端未定稿内容不展开。
|
||||
- `## 边界与注意`(文末单列):配置 id 冲突时的处理(后到的同 id 配置不会被重复登记,加载器按 id 去重;**不要**写成「会稳定告警某个码」)、`addCustomTask` 只能在加载前或两个 hook 中调用、前缀缺失与解析失败的表现、DEV 与构建后的路径差异由加载器统一处理(自定义 starter 要自己处理路径)、starter 的返回必须符合 `IResponseLike`、总大小未知时字节进度可能不精确。
|
||||
(3) 全文自检:至少一条 `../i/` 或 `../ii/` 链接(建议 `../i/plugin-system.md`)与一条 `../../intermediate/` 链接;`src/client` 只出现在渲染端小节内;`src/client` 之外不出现渲染端叙述;无 `**` 紧贴中文标点的加粗(D-52);无「模板」/「[需改]」;无「源码第 X 行」。
|
||||
(4) 跑 `node node_modules/prettier/bin/prettier.cjs --write docs/advanced/iii/loading-system.md`,再确认文件仍是 CRLF。Commit:`docs(10-34): 新增加载系统原理页`。</action>
|
||||
<acceptance_criteria>文件存在、单 H1、CRLF;八个主体小节齐备且顺序为「模型 → 阶段 → 配置 → 三个扩展点 → 进度 → 渲染端 → 边界」;三类扩展点各有完整可解析代码块并带注释;`src/client` 仅出现在渲染端小节;扩展点覆盖配置/资源/starter 三类;至少一条 `../i/` 或 `../ii/` 链接;无模板标记、无源码行号叙事。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/iii/loading-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' }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error 'H1-COUNT' }; if($raw -notmatch '(?m)^# 加载系统\r?$'){ Write-Error 'NO-TITLE' }; foreach($h in @('## 系统模型','## 一次加载的阶段','## 配置文件与处理器','## 扩展点','## 进度','## 渲染端如何消费加载进度','## 边界与注意')){ if($raw -notmatch [regex]::Escape($h)){ Write-Error "NO-SECTION $h" } }; if($raw -notmatch [regex]::Escape('LoadTask')){ Write-Error 'NO-LOADTASK' }; if($raw -notmatch [regex]::Escape('LoadManager')){ Write-Error 'NO-LOADMANAGER' }; if($raw -notmatch [regex]::Escape('ILoadTaskStarter')){ Write-Error 'NO-STARTER' }; if($raw -notmatch [regex]::Escape('addCustomTask')){ Write-Error 'NO-CUSTOMTASK' }; if($raw -notmatch [regex]::Escape('onCoreConfigLoaded')){ Write-Error 'NO-COREHOOK' }; if($raw -notmatch [regex]::Escape('RenderLoaderHooks')){ Write-Error 'NO-RENDERHOOKS' }; $sec=$raw.IndexOf('渲染端如何消费加载进度'); $rend=$raw.IndexOf('src/client'); if($sec -lt 0){ Write-Error 'NO-RENDER-SECTION' }; if($rend -ge 0 -and $rend -lt $sec){ Write-Error 'RENDERER-OUTSIDE-SECTION' }; if($raw -match '\[需改\]'){ Write-Error 'TEMPLATE-MARK' }; if($raw -match '(?m)^#{1,3} .*模板'){ Write-Error 'TEMPLATE-HEADING' }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent (Resolve-Path -LiteralPath $p)) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '\.\./i/|\.\./ii/'){ Write-Error 'NO-I-II-LINK' }; if($raw -notmatch '\.\./\.\./intermediate/'){ Write-Error 'NO-INTERMEDIATE-LINK' }; if($raw -match '告警\s*68|码\s*68'){ Write-Error 'WARN68-CLAIM' }; Write-Output 'PAGE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "NO-TITLE", "NO-SECTION", "NO-LOADTASK", "NO-LOADMANAGER", "NO-STARTER", "NO-CUSTOMTASK", "NO-COREHOOK", "NO-RENDERHOOKS", "NO-RENDER-SECTION", "RENDERER-OUTSIDE-SECTION", "TEMPLATE-MARK", "TEMPLATE-HEADING", "DEAD-LINK", "NO-I-II-LINK", "NO-INTERMEDIATE-LINK", or "WARN68-CLAIM"; or output lacks "PAGE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>页面结构与事实要点齐备;渲染端内容限定在专用小节;链接可解析;CRLF、单 H1、无模板标记。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 格式与构建门禁、写 SUMMARY</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-34-SUMMARY.md</files>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-32-SUMMARY.md(SUMMARY 体例)</read_first>
|
||||
<action>(1) 格式:`node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/loading-system.md`,必须通过;未通过先 `--write` 再 `--check`。(2) 构建:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,退出码 0、含 `build complete`、无 `dead link`。此时 III 组仍为空、总导览也未链接本页,构建不应出现与本页相关的新死链。(3) 复核页面渲染结果:标题层级、代码块高亮正常、表格未错列。(4) 写 `.planning/phases/10-docs-restructure/10-34-SUMMARY.md`:页面结构清单、事实来源(复核了哪些文件、与 research 是否一致)、三类扩展点与代码块、刻意未写的内容(`getTotalByte` 语义 / 只加载一次 / 重复 id 告警 / FSA 实现 / UI 生命周期)、prettier 与构建结论、留给 10-39 的注册与互链事项。若发现新的注释/实现不一致,只写「已在对话报告」一句,不写细节(D-72)。Commit:`docs(10-34): 加载系统原理页收口`。</action>
|
||||
<acceptance_criteria>prettier `--check` 通过;构建退出码 0、含 `build complete`、无 `dead link`;SUMMARY 记录结构、事实复核、刻意未写内容与遗留事项。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $sum='.planning/phases/10-docs-restructure/10-34-SUMMARY.md'; if(-not(Test-Path -LiteralPath $sum)){ Write-Error "MISSING $sum" }; $s=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $sum)); foreach($needle in @('加载系统','扩展点','构建','10-39')){ if($s -notmatch [regex]::Escape($needle)){ Write-Error "SUMMARY-MISSING $needle" } }; $ErrorActionPreference='Continue'; $pf = cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/loading-system.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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'GATE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "SUMMARY-MISSING", "PRETTIER-FAIL", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "GATE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>格式与构建双绿;SUMMARY 完整。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 构建期 markdown → 静态站点 | 正文静态渲染,无运行时输入 |
|
||||
| 读者 → 站点 | 站内链接是唯一可点击指向面 |
|
||||
| 文档 → 读者对渲染端的预期 | 渲染端不稳定,多写一句都可能变成误导 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-100 | Tampering / XSS | `loading-system.md` 正文 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-101 | Information Disclosure | 示例代码 | low | mitigate | 示例只用占位路径与公开接口名,不写本地绝对路径、密钥 |
|
||||
| T-10-102 | Tampering | 渲染端小节 | medium | mitigate | 断言 `src/client` 只出现在专用小节之后;不写场景/UI 生命周期 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 结构门禁:单 H1、CRLF、文末「边界与注意」、八个主体小节齐备。
|
||||
- 内容门禁:三层模型、阶段顺序、三类扩展点、渲染端接口级小节;`src/client` 仅在渲染端小节内。
|
||||
- 措辞门禁:无 `getTotalByte` 总字节承诺、无「只加载一次」、无重复 id 稳定告警、无源码行号。
|
||||
- 链接门禁:相对 `.md` 目标全部存在;至少一条 `../i/` 或 `../ii/` 与一条 `../../intermediate/`。
|
||||
- 格式 / 构建门禁:prettier `--check` 通过;`build complete` 且无 `dead link`。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 读者能凭这一页判断「加载分几段、扩展点在哪、什么时候不能追加」。
|
||||
- 渲染端只得到接口级信息,不被引向不稳定的 UI 生命周期。
|
||||
- 页面可构建、可格式化、无死链。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-34-SUMMARY.md` when done
|
||||
</output>
|
||||
154
.planning/phases/10-docs-restructure/10-35-PLAN.md
Normal file
154
.planning/phases/10-docs-restructure/10-35-PLAN.md
Normal file
@ -0,0 +1,154 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 35
|
||||
type: execute
|
||||
wave: 7
|
||||
depends_on: [10-34]
|
||||
files_modified:
|
||||
- docs/advanced/iii/replay-internals.md
|
||||
- .planning/phases/10-docs-restructure/10-35-SUMMARY.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 32000
|
||||
raw_tokens: 21000
|
||||
tasks: 3
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "`docs/advanced/iii/replay-internals.md` 先讲模型(`ReplaySystem` = 二进制数组 + 指令注册表 + 可选沙箱),再讲录制、沙箱播放、存档,最后才进内部实现(D-82 / D-69)"
|
||||
- "录制与播放的接口写对:`record` 追加一步、指令对象分主动(`Active`)与被动(`Passive`)、被动步由外部取参数并在完成后回报、**指令切换或录像结束**时触发 `finalize`(同一指令连续多步之间不逐步触发)、沙箱提供播放/暂停/恢复/停止/单步"
|
||||
- "内部实现章节以「此章节讲解一下录像二进制格式的内部实现」开头,用「指令区 / 参数区 / 索引区」三块解释存储,给参数类型表(布尔、各宽度整数、浮点、大整数、字符串),不逐字节罗列、不写偏移算式"
|
||||
- "存档形状写清四个字段(长度、指令宽度、指令区、参数区),并说明返回的是内部缓冲区引用(不要就地修改)、读档会重建索引并使旧读流失效"
|
||||
- "旧引擎只在独立的「对比」小节出现,明确标注「旧引擎基线、不是当前接口」,给表格 + 3–5 条优势(紧凑、类型保真、可随机访问、可编辑、不依赖地图编号)"
|
||||
- "自定义指令不在本篇重讲,只链接 `../ii/custom-replay-command.md`;数组级编辑(插入 / 删除 / 改写)只作边界说明,不写成推荐用法"
|
||||
- "文末单一「边界与注意」小节(D-61),覆盖参数个数上限与截断、不支持的参数类型被丢弃、随机读取很慢、编辑是 O(n)、大整数上限、缩窄指令宽度的影响、存档不承诺旧格式可读回"
|
||||
- "page 仍是 kebab-case、CRLF、单 H1;相对链接全部可解析;prettier `--check` 通过;构建 green、`build complete`、无 `dead link`"
|
||||
artifacts:
|
||||
- "docs/advanced/iii/replay-internals.md"
|
||||
- ".planning/phases/10-docs-restructure/10-35-SUMMARY.md"
|
||||
key_links:
|
||||
- "研究文件 §2「录像系统底层」与「Old vs New Replay Storage」是事实源;执行前按当前源码复核类型码与接口名"
|
||||
- "与高级 II 的 `custom-replay-command.md` 是天然相邻页:本篇只讲格式与播放模型,自定义指令一律外链,避免重复"
|
||||
- "旧引擎内容放在对比小节并显式标注,是本页最大的误导风险点(Pitfall 3)"
|
||||
---
|
||||
|
||||
<objective>
|
||||
写出高级 III 的《录像系统底层》:讲清录像由「二进制数组 + 指令注册表 + 播放沙箱」组成,录制与回放如何配合,存档形状是什么,内部用哪三块缓冲区存一步操作,以及新格式相对旧引擎文本录像的优势。
|
||||
|
||||
Purpose: 高级 II 已经讲过「怎么新增一条录像指令」,但读者一旦要排查「录像存下来为什么对不上」「回放为什么读到了错误的参数」,就必须知道存储形状与读流语义。把这些讲清,才能让读者判断问题出在指令实现还是格式层。
|
||||
Output: `docs/advanced/iii/replay-internals.md` 与 `10-35-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-ADVANCED-III-RESEARCH.md
|
||||
@docs/advanced/ii/custom-replay-command.md
|
||||
@docs/advanced/i/replay-consistency.md
|
||||
|
||||
**写作规范(本批统一,逐条自检):**
|
||||
- 库文档风格(D-59),简洁优先(D-60);先模型后接口(D-82);内部实现章节后置并以「此章节讲解一下 XXX 的内部实现」开头(D-69)。
|
||||
- 边界单列文末(D-61);代码块完整、有注释(D-62 / D-65 / D-70);无「模板」「[需改]」(D-83)。
|
||||
- 不写源码叙述与行号(D-80);不写用户提问来源(D-81);旧引擎只作对比基线(Pitfall 3)。
|
||||
- 引用其他文档的具体章节用 `./x.md#标题` 或 `../ii/x.md#标题`(D-74)。
|
||||
- 本页与 10-34 之间**没有内容依赖**:依赖关系仅用于串行化构建产物目录——五个内容计划共用同一个 `outDir`(`public/_docs`),并发执行 `vitepress build` 会互相覆盖,因此按 10-34 → 10-35 → 10-36 → 10-37 → 10-38 依次执行;写作时不需要读上一页。
|
||||
- research 是权威事实源但行号会漂移:执行前打开当前源码复核类型码、接口名与方法行为;注释与实现不一致按 D-72 以注释为准,只在汇报中列出。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报本篇计划并等待「可以执行」</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-CONTEXT.md(D-12、D-60、D-61、D-69、D-77、D-80)、.planning/phases/10-docs-restructure/10-ADVANCED-III-RESEARCH.md(§2 录像系统底层、Old vs New Replay Storage、Pitfall 3)</read_first>
|
||||
<action>向用户汇报四段并等待批准(D-12):**要做什么**——新建 `docs/advanced/iii/replay-internals.md`,按「模型 → 录制 → 沙箱播放 → 存档 → 内部实现(指令区 / 参数区 / 索引区)→ 旧引擎对比 → 边界与注意」组织。**为什么**——高级 II 只讲「怎么加指令」,读者排查「回放读到错误参数」「存档对不上」时需要的是存储形状与读流语义,这正是本篇的落点。**大致怎么做**——Task 1 复核源码后落稿并格式化;Task 2 跑构建与结构门禁、写 SUMMARY。**影响范围**——只新增这一篇与 SUMMARY;不动 config、不动 II 的自定义指令页、不动源码。汇报中明确:重心是「使用为主、原理服务于理解格式」(D-77);旧引擎只作对比、明确标注不是当前接口;数组的插入/删除/改写只作边界说明;不写指令目录全量、不写 `compile.ts` 或 route folding。research 的 C6/C7(解释器 `save` 字段与 `dump()`)与本篇无关,若复核中发现新的注释/实现不一致,只对话报告(D-72)。汇报中说明本计划的修改方式为**派发子代理执行**,范围严格限于本页与 SUMMARY。停下等待用户明确回复「可以执行」四个字;未收到前不得建文件、不得派发写作子代理。</action>
|
||||
<what-built>本计划内容:新建《录像系统底层》一页(含旧引擎对比小节)。</what-built>
|
||||
<how-to-verify>确认内部实现章节的深度、旧引擎对比的篇幅,以及「数组编辑只作边界说明」的取舍。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<acceptance_criteria>用户已回复「可以执行」;若对旧引擎对比的篇幅或数组编辑的写法提出意见,已并入后续任务。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已回复「可以执行」,且未否决内部实现章节的深度与旧引擎对比的范围。</human-check>
|
||||
</verify>
|
||||
<done>用户回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 复核录像事实并落稿 replay-internals.md</name>
|
||||
<files>docs/advanced/iii/replay-internals.md</files>
|
||||
<read_first>packages-user/data-common/src/replay/types.ts、packages-user/data-common/src/replay/array.ts、packages-user/data-common/src/replay/system.ts、packages-user/data-common/src/replay/sandbox.ts、docs/advanced/ii/custom-replay-command.md、docs/advanced/i/replay-consistency.md、public/libs/control.js 与 public/libs/utils.js(只作旧引擎对比取证)</read_first>
|
||||
<action>(1) 用 Write 新建 `docs/advanced/iii/replay-internals.md`(CRLF、单 H1),结构如下:
|
||||
- `# 录像系统底层` + 引言:录像要解决「同一份操作序列在另一台机器上重放出同一局」,本篇讲存储格式、录制与播放的配合、存档形状;自定义指令见高级 II。
|
||||
- `## 系统模型`:`ReplaySystem` = 二进制数组(存指令与参数)+ 指令注册表(指令码 → 指令对象)+ 可选播放沙箱;录制时把一步追加进数组,播放时沙箱用读流顺序取步;存档只存数组与长度、宽度。
|
||||
- `## 录制一步`:`record(code, ...params)` 的行为;指令对象的两类——主动(自己执行并给出结果)与被动(只把参数交出去,由外部执行完再回报状态);结果三态(成功 / 失败 / 被忽略);**指令切换或录像结束时**会调用一次收尾方法——同一指令连续多步不会在每步之间调用(正文写法固定为「指令切换或录像结束时触发 `finalize`」,不得写成「连续步骤之间」)。给一段完整函数示例说明「玩家操作如何进入录像」,带注释。
|
||||
- `## 沙箱播放`:创建沙箱前先重置游戏状态;沙箱持有读流;主动步直接执行,被动步把「步骤 + 回报函数」交给外部,外部执行完调用回报函数后才继续;播放控制(播放 / 暂停 / 恢复 / 停止 / 单步 / 速度);已播放步数不含当前步。给完整类或函数示例(带注释)演示接管被动步。
|
||||
- `## 存档形状`:四个字段(长度、指令宽度、指令区、参数区);保存返回的是内部缓冲区引用,读档会重建索引并使旧读流全部失效;录像格式只增不改,不承诺旧文本格式可读回。
|
||||
- `## 内部实现:三块缓冲区`:以「此章节讲解一下录像二进制格式的内部实现」开头。讲指令区(每条指令固定宽度:参数个数 + 指令码;宽度可选 8 位或 16 位)、参数区(每个参数一个类型标记 + 载荷)、索引区(记录每条指令的参数起始位置)。给一张参数类型表:布尔、8/16/32 位整数、64 位整数、浮点、大整数、字符串,以及各自的载荷形态(**不要**写偏移算式与逐字节布局)。说明容量按需扩容、有上限。
|
||||
- `## 内部实现:索引与读流`:索引让「按步号直接定位参数」成为可能;读流带过期标记,任何写操作都会让已有读流失效;随机读取单步很慢、顺序流才是正常用法。
|
||||
- `## 旧引擎基线对比`:明确一句「以下为旧引擎的文本录像,仅作对比基线,不是当前接口」;给 5–6 行对比表(容器、单步表示、序列化、随机访问、参数类型、编辑、物品标识);随后 3–5 条优势(紧凑、类型保真、可随机访问、可编辑、不依赖地图编号/内容编号)。
|
||||
- `## 边界与注意`(文末单列):参数个数上限与截断、不支持的参数类型被丢弃、单步随机读取慢、插入/删除/改写是线性搬移且越界会告警、大整数幅值上限、把指令宽度从 16 位缩到 8 位会影响超范围指令、存档缓冲区不要就地修改、旧格式不保证可读回。
|
||||
(2) 全文自检:`ReplayArray` / `ReplaySystem` / `ReplaySandbox` 三个名字都要出现;`Active` / `Passive` 要出现;旧引擎的 `core.status.route` 一类写法**只能**出现在「旧引擎基线对比」小节之后;至少一条 `../ii/` 链接(`../ii/custom-replay-command.md`)与一条 `../i/` 链接(`../i/replay-consistency.md`);无 `**` 紧贴中文标点;无「模板」「[需改]」;无源码行号。
|
||||
(3) 跑 `node node_modules/prettier/bin/prettier.cjs --write docs/advanced/iii/replay-internals.md`,确认仍为 CRLF。Commit:`docs(10-35): 新增录像系统底层原理页`。</action>
|
||||
<acceptance_criteria>文件存在、单 H1、CRLF;小节顺序为「模型 → 录制 → 沙箱 → 存档 → 内部实现两块 → 旧引擎对比 → 边界」;内部实现章节有规定的开头句并给参数类型表;旧引擎标记清晰且只出现在对比之后;自定义指令只外链;无模板标记与源码行号。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/iii/replay-internals.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' }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error 'H1-COUNT' }; foreach($needle in @('ReplayArray','ReplaySystem','ReplaySandbox','Active','Passive','finalize','createReplaySandbox')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "NO-FACT $needle" } }; foreach($h in @('## 系统模型','## 录制一步','## 沙箱播放','## 存档形状','## 内部实现','## 旧引擎基线对比','## 边界与注意')){ if($raw -notmatch [regex]::Escape($h)){ Write-Error "NO-SECTION $h" } }; if($raw -notmatch '此章节讲解一下'){ Write-Error 'NO-INTERNAL-OPENER' }; $old=$raw.IndexOf('旧引擎基线对比'); if($old -lt 0){ Write-Error 'NO-OLD-SECTION' }; $legacy=$raw.IndexOf('core.status.route'); if($legacy -ge 0 -and $legacy -lt $old){ Write-Error 'LEGACY-BEFORE-COMPARE' }; if($raw -match '\[需改\]'){ Write-Error 'TEMPLATE-MARK' }; if($raw -match '(?m)^#{1,3} .*模板'){ Write-Error 'TEMPLATE-HEADING' }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent (Resolve-Path -LiteralPath $p)) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '\.\./ii/custom-replay-command\.md'){ Write-Error 'NO-II-LINK' }; if($raw -notmatch '\.\./i/replay-consistency\.md'){ Write-Error 'NO-I-LINK' }; if($raw -match '连续同指令|连续步骤之间会触发'){ Write-Error 'FINALIZE-WORDING' }; if($raw -notmatch '指令切换'){ Write-Error 'NO-FINALIZE-SWITCH' }; Write-Output 'PAGE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "NO-FACT", "NO-SECTION", "NO-INTERNAL-OPENER", "NO-OLD-SECTION", "LEGACY-BEFORE-COMPARE", "TEMPLATE-MARK", "TEMPLATE-HEADING", "DEAD-LINK", "NO-II-LINK", "NO-I-LINK", "FINALIZE-WORDING", or "NO-FINALIZE-SWITCH"; or output lacks "PAGE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>页面结构与事实要点齐备;内部实现后置并有规定开头句;旧引擎只在对比小节;链接可解析;CRLF、单 H1。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 格式与构建门禁、写 SUMMARY</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-35-SUMMARY.md</files>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-34-SUMMARY.md(SUMMARY 体例)</read_first>
|
||||
<action>(1) `node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/replay-internals.md`,通过。(2) `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,退出码 0、含 `build complete`、无 `dead link`。(3) 复核:参数类型表渲染正常;内部实现章节位于旧引擎对比之前、边界之前;没有把旧引擎写法写成当前用法。(4) 写 `.planning/phases/10-docs-restructure/10-35-SUMMARY.md`:结构清单、复核的源码与结论、参数类型表覆盖范围、旧引擎对比的标注方式、刻意未写内容(指令目录全量、route folding、`compile.ts`、数组编辑推荐用法)、格式与构建结论、留给 10-39 的注册与互链事项。Commit:`docs(10-35): 录像底层原理页收口`。</action>
|
||||
<acceptance_criteria>prettier `--check` 通过;构建退出码 0、含 `build complete`、无 `dead link`;SUMMARY 完整记录结构、事实复核、未写内容与遗留事项。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $sum='.planning/phases/10-docs-restructure/10-35-SUMMARY.md'; if(-not(Test-Path -LiteralPath $sum)){ Write-Error "MISSING $sum" }; $s=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $sum)); foreach($needle in @('录像','内部实现','旧引擎','构建','10-39')){ if($s -notmatch [regex]::Escape($needle)){ Write-Error "SUMMARY-MISSING $needle" } }; $ErrorActionPreference='Continue'; $pf = cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/replay-internals.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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'GATE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "SUMMARY-MISSING", "PRETTIER-FAIL", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "GATE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>格式与构建双绿;SUMMARY 完整。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 构建期 markdown → 静态站点 | 正文静态渲染,无运行时输入 |
|
||||
| 旧引擎代码 → 读者认知 | `public/libs/*` 仍在仓库里,容易被当成当前 API |
|
||||
| 读者 → 站点 | 站内链接是唯一可点击指向面 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-103 | Tampering | 旧引擎写法被误当当前接口 | medium | mitigate | 旧写法只允许出现在「旧引擎基线对比」小节之后,并在断言中检查其出现位置 |
|
||||
| T-10-104 | Tampering / XSS | 正文与代码块 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-105 | Information Disclosure | 示例代码 | low | mitigate | 示例只用占位路径与公开接口名 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 结构门禁:单 H1、CRLF、内部实现章节后置且以规定句式开头、文末「边界与注意」。
|
||||
- 内容门禁:模型、录制、沙箱、存档、三块缓冲区、索引与读流、新旧对比齐备;参数类型表覆盖到位。
|
||||
- 误导门禁:旧引擎写法只在对比小节;自定义指令只外链、不重讲。
|
||||
- 链接门禁:相对 `.md` 目标全部存在;含 `../ii/custom-replay-command.md` 与 `../i/replay-consistency.md`。
|
||||
- 格式 / 构建门禁:prettier `--check` 通过;`build complete` 且无 `dead link`。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 读者能解释一步录像在存储里长什么样、为什么能按步号读取、存档里存了什么。
|
||||
- 读者不会把旧文本录像当作当前接口。
|
||||
- 页面可构建、可格式化、无死链。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-35-SUMMARY.md` when done
|
||||
</output>
|
||||
180
.planning/phases/10-docs-restructure/10-36-PLAN.md
Normal file
180
.planning/phases/10-docs-restructure/10-36-PLAN.md
Normal file
@ -0,0 +1,180 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 36
|
||||
type: execute
|
||||
wave: 8
|
||||
depends_on: [10-35]
|
||||
files_modified:
|
||||
- docs/advanced/iii/event-system.md
|
||||
- docs/advanced/iii/anon-tokyo.md
|
||||
- .planning/phases/10-docs-restructure/10-36-SUMMARY.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 40000
|
||||
raw_tokens: 27000
|
||||
tasks: 4
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "`event-system.md` 先讲模型(事件仓库 → 事件对象 → 收集器 → 执行器 → 解释器),再讲触发器、收集顺序、执行与折叠、事件层,最后讲自定义内建函数(D-82 / D-69)"
|
||||
- "触发器的表述准确:完整枚举列出,但明确「当前数据端内置派发只覆盖进入 / 离开 / 触碰,其余值是枚举的一部分、由对应系统使用或预留」,绝不写成「全部已实现」"
|
||||
- "收集顺序与执行语义写对:点事件优先于图块事件、各自按优先级降序;触发器不匹配跳过;缺失事件告警并跳过;`CutIfFalsy` / `CutIfTruthy` 短路;`OrReduce` / `AndReduce` 对非布尔值告警"
|
||||
- "`anon-tokyo.md` 先讲模型(语言特性 → 解释器 → 编译产物 → 执行上下文与迭代器),再讲语句模型、与事件对象的衔接,内部实现(编译与执行循环)后置并以「此章节讲解一下 XXX 的内部实现」开头"
|
||||
- "解释器与事件对象的衔接写清:事件对象缓存编译结果、修改原始脚本会清空缓存、没有编译产物时可以退回直接执行;解释器本身不负责事件收集与触发过滤"
|
||||
- "构造内建函数是可执行路径:完整类(`name` + `func`)实现内建函数接口、参数接口单独声明、加入事件注册列表并随配置进入事件系统;通过环境对象取状态、图层、地图、触发器与图块"
|
||||
- "两页都明确不写序列化事件注册、地图事件 id 绑定与编辑器识别,也不写完整内建事件目录或事件脚本语法全量;渲染端与编辑器内容按占位处理(D-73)"
|
||||
- "两页都是 kebab-case、CRLF、单 H1;相对链接全部可解析;两页互相之间用真实相对链接(同一计划内创建,构建时都存在)且各至少一条指向 `../i/` 或 `../../intermediate/`;prettier `--check` 通过;构建 green、`build complete`、无 `dead link`"
|
||||
artifacts:
|
||||
- "docs/advanced/iii/event-system.md"
|
||||
- "docs/advanced/iii/anon-tokyo.md"
|
||||
- ".planning/phases/10-docs-restructure/10-36-SUMMARY.md"
|
||||
key_links:
|
||||
- "研究文件 §3「事件系统」与 §4「Anon-Tokyo 解释器模型」是事实源;执行前按当前源码复核触发器枚举、执行器折叠与解释器接口名"
|
||||
- "两页必须分工:事件页讲「谁收集、谁执行、内建函数怎么写」,解释器页讲「脚本怎么编译成程序、怎么迭代执行」;解释器页不重讲事件收集,事件页不重讲编译细节"
|
||||
- "触发器的「只有三个有派发点」是本篇最容易被写成过度承诺的地方,必须按当前源码核对后再落笔"
|
||||
---
|
||||
|
||||
<objective>
|
||||
写出高级 III 的事件两篇:《事件系统》讲事件从存储、收集到执行与折叠的完整链路,以及如何构造一个内建函数;《Anon-Tokyo 解释器》讲事件脚本的结构化表示、编译产物与执行循环。
|
||||
|
||||
Purpose: 事件是数据端唯一「用户脚本 → 引擎行为」的入口。读者会做的事是新增一个内建函数,而不是改解释器;因此事件页以自定义为主,解释器页以理解模型为主。两页合起来回答「我写的这条事件脚本,到底被谁、在什么时候、以什么顺序执行」。
|
||||
Output: `docs/advanced/iii/event-system.md`、`docs/advanced/iii/anon-tokyo.md`、`10-36-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-ADVANCED-III-RESEARCH.md
|
||||
@docs/advanced/i/data-stores.md
|
||||
@docs/advanced/ii/point-and-tile-events.md
|
||||
@docs/intermediate/events.md
|
||||
|
||||
**写作规范(本批统一,逐条自检):**
|
||||
- 库文档风格(D-59),简洁优先(D-60);先模型后接口(D-82);内部实现后置(D-69)。
|
||||
- 边界单列文末(D-61);代码块完整、带注释(D-62 / D-65 / D-70);无「模板」「[需改]」(D-83)。
|
||||
- 不写源码叙述与行号(D-80);不写用户提问来源(D-81)。
|
||||
- 编辑器 / 序列化事件注册 / 地图事件 id 绑定只按「需要编辑器」占位(D-73),不展开。
|
||||
- 引用其他文档的具体章节用 `./x.md#标题` 或 `../x/y.md#标题`(D-74)。
|
||||
- 本页与 10-35 之间**没有内容依赖**:依赖仅用于串行化构建产物目录(同一个 `outDir` 不能并发构建);写作时不需要读上一页。
|
||||
- research 是权威事实源但行号会漂移:执行前打开当前源码复核触发器枚举、收集顺序、执行器模式与解释器接口;注释与实现不一致按 D-72 以注释为准,只在汇报中列出(本批相关的 C6/C7 尤其不得写进正文)。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报事件两篇计划并等待「可以执行」</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-CONTEXT.md(D-12、D-60、D-61、D-69、D-73、D-77、D-82)、.planning/phases/10-docs-restructure/10-ADVANCED-III-RESEARCH.md(§3 事件系统、§4 Anon-Tokyo、Page Split 的 merge/split flags、Pitfall 4)</read_first>
|
||||
<action>向用户汇报四段并等待批准(D-12):**要做什么**——新建 `docs/advanced/iii/event-system.md` 与 `docs/advanced/iii/anon-tokyo.md`;事件页按「模型 → 触发器 → 收集 → 执行 → 事件层 → 自定义内建函数 → 边界」,解释器页按「模型 → 语句模型 → 与事件对象的衔接 → 内部实现(编译与执行)→ 自定义 → 边界」。**为什么**——两篇回答的是同一个读者问题「我的事件脚本什么时候、以什么顺序执行」,但答案分属两个系统:事件页负责收集与执行顺序,解释器页负责脚本怎么变成可执行程序;写成一篇会让两个系统互相纠缠。**大致怎么做**——Task 1 落事件页,Task 2 落解释器页并把两页互链收口,Task 3 跑格式与构建并写 SUMMARY。**影响范围**——只新增这两篇与 SUMMARY;不动 config、不动 II 的点事件页、不动源码。汇报中明确:触发器只写「当前内置派发覆盖进入 / 离开 / 触碰,其余为预留」,不写成全部已实现;不写序列化注册、地图事件 id 绑定、编辑器识别与完整内建目录;解释器页不写 `dump()` 可用、不给 `save` 字段赋予未证实的语义(research C6/C7,只对话报告,D-72)。research 建议「若解释器页过短可并入事件页」,但**页面清单已由用户锁定为独立成页**,本计划不做合并;若成稿明显偏短,只在汇报中提出供用户裁决,不得自行合并(D-13 / D-14)。汇报中说明本计划的修改方式为**派发子代理执行**,范围严格限于这两页与 SUMMARY。停下等待用户明确回复「可以执行」四个字;未收到前不得建文件、不得派发写作子代理。</action>
|
||||
<what-built>本计划内容:新建《事件系统》与《Anon-Tokyo 解释器》两页并互链。</what-built>
|
||||
<how-to-verify>确认两页分工、触发器表述口径(三个已派发 + 其余预留),以及解释器保持独立成页。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<acceptance_criteria>用户已回复「可以执行」;若用户要求合并两页或调整章节顺序,已按用户意见改写计划。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已回复「可以执行」,且未要求把两页合并或改变两页分工。</human-check>
|
||||
</verify>
|
||||
<done>用户回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 复核事件事实并落稿 event-system.md</name>
|
||||
<files>docs/advanced/iii/event-system.md</files>
|
||||
<read_first>packages-user/data-common/src/event/types.ts、packages-user/data-common/src/event/event.ts、packages-user/data-common/src/store/eventStore.ts、packages-user/data-common/src/store/types.ts、packages-user/data-system/src/event/system.ts、packages-user/data-system/src/event/executor.ts、packages-user/data-system/src/event/types.ts、packages-user/data-base/src/map/types.ts(事件层字段)、src/data/event/*(内建函数与注册)、src/data/hero/moverImpl.ts(派发点)、docs/advanced/i/data-stores.md、docs/advanced/ii/point-and-tile-events.md、docs/intermediate/events.md</read_first>
|
||||
<action>(1) 用 Write 新建 `docs/advanced/iii/event-system.md`(CRLF、单 H1),结构:
|
||||
- `# 事件系统` + 引言:事件把「脚本」和「什么时候跑」接在一起;由事件仓库、事件对象、收集器、执行器与解释器组成;本篇讲前四者与内建函数,脚本如何编译执行属同批的 Anon-Tokyo 解释器一篇(本轮先用纯文本提及,Task 2 再改成真实链接,避免指向尚未创建的文件)。
|
||||
- `## 系统模型`:一段数据流——事件仓库按 id 存事件对象;事件对象持有原始语句与(缓存的)编译产物;收集器按坐标与触发时机产出「这次该执行谁」的调用列表;执行器逐条过滤并执行;解释器负责把语句跑起来。用 `text` 示意即可,不必画渲染端。
|
||||
- `## 事件与触发器`:触发器枚举完整列出;明确「当前数据端内置派发只覆盖进入 / 离开 / 触碰,其余值是枚举的一部分,由对应系统使用或预留」;事件对象持有原始语句与触发器,编译结果会被缓存、改原始语句会清空缓存。
|
||||
- `## 收集:这一次该执行谁`(**标题逐字固定,不得改写**——10-39 的章节锚点依赖它):先判坐标是否在地图内;点事件来自坐标事件,图块事件来自静态图块与每个动态图块的事件;点事件整组优先于图块事件,两组内部各自按优先级降序;每个调用项带完整环境(状态、类型、触发器、系统、勇士位置与楼层、触发位置、图块、图层、地图)。给一个完整函数示例演示「取某格的事件调用列表并打印优先级与来源」,带注释。
|
||||
- `## 执行与折叠`:逐条查仓库、缺失则告警并跳过;触发器与调用环境不一致则跳过;执行结果为假/真时按执行模式短路;按折叠模式合并多条结果,非布尔值会告警;执行入口带有录像标记,所以回放时无需额外登记。给完整函数示例演示「用折叠模式执行一组事件并读取合并结果」。
|
||||
- `## 事件层`:事件层就是 `alias` 为事件别名的普通图层,具备图层的全部能力;地图上的事件层是当前楼层默认事件层,切层后由图层加载流程设置。
|
||||
- `## 自定义一个内建函数`(本篇重心,D-77):先说要做什么 → 给出完整类(`name` 与 `func(param, env)`,`implements` 内建函数接口,带解释性注释)→ 参数接口单独声明 → 说明加入事件注册列表、随配置进入事件系统 → 说明在 `func` 里怎么取状态 / 图层 / 地图 / 触发器 / 图块,需要默认图层时的取法 → 一句提示:新增触发器枚举值属于引擎级改动,文档不鼓励。示例给 1–2 个(设置标记 / 勇士回血),链接 `../../intermediate/events.md` 与 `../i/data-stores.md`。
|
||||
- `## 边界与注意`(文末单列):坐标越界返回空列表;点事件与图块事件互不替代;同一优先级可能互相覆盖;事件 id 必须已注册;触发器不匹配会被静默跳过;短路模式下后面的条目不再执行;折叠模式遇到非布尔值只告警;多条事件是异步顺序执行,不要假设并发。
|
||||
(2) 全文自检:不出现「源码第 X 行」;不写序列化注册、地图事件 id 绑定、编辑器识别规则;无 `**` 紧贴中文标点;无「模板」「[需改]」;至少一条 `../../intermediate/` 与一条 `../i/` 链接。
|
||||
(3) 跑 `node node_modules/prettier/bin/prettier.cjs --write docs/advanced/iii/event-system.md`,确认 CRLF。Commit:`docs(10-36): 新增事件系统原理页`。</action>
|
||||
<acceptance_criteria>文件存在、单 H1、CRLF;七个主体小节齐备且顺序正确;触发器表述为「三个已派发 + 其余预留」;收集顺序与执行语义准确;自定义内建函数给完整类与注册路径;边界覆盖九项;不写序列化注册与编辑器内容。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/iii/event-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' }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error 'H1-COUNT' }; foreach($needle in @('EventTrigger','EventExecutor','GameEventStore','collectEvent','BuiltInFunction','createEventRegistrations','CutIfFalsy','OrReduce')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "NO-FACT $needle" } }; foreach($h in @('## 系统模型','## 事件与触发器','## 收集','## 执行与折叠','## 事件层','## 自定义一个内建函数','## 边界与注意')){ if($raw -notmatch [regex]::Escape($h)){ Write-Error "NO-SECTION $h" } }; if($raw -notmatch 'OnTouch'){ Write-Error 'NO-ONTOUCH' }; if($raw -notmatch '预留|未派发|尚未派发'){ Write-Error 'NO-TRIGGER-CAVEAT' }; if($raw -match '全部(已)?实现'){ Write-Error 'OVERCLAIM-TRIGGERS' }; if($raw -match '\[需改\]'){ Write-Error 'TEMPLATE-MARK' }; if($raw -match '(?m)^#{1,3} .*模板'){ Write-Error 'TEMPLATE-HEADING' }; if($raw -match 'src/client'){ Write-Error 'RENDERER-CONTENT' }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent (Resolve-Path -LiteralPath $p)) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '\.\./\.\./intermediate/'){ Write-Error 'NO-INTERMEDIATE-LINK' }; if($raw -notmatch '\.\./i/'){ Write-Error 'NO-I-LINK' }; Write-Output 'PAGE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "NO-FACT", "NO-SECTION", "NO-ONTOUCH", "NO-TRIGGER-CAVEAT", "OVERCLAIM-TRIGGERS", "TEMPLATE-MARK", "TEMPLATE-HEADING", "RENDERER-CONTENT", "DEAD-LINK", "NO-INTERMEDIATE-LINK", or "NO-I-LINK"; or output lacks "PAGE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>事件页结构与事实要点齐备;触发器表述不过度承诺;自定义内建函数完整;边界齐全;CRLF、单 H1、链接可解析。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 落稿 anon-tokyo.md 并收口两页互链</name>
|
||||
<files>docs/advanced/iii/anon-tokyo.md, docs/advanced/iii/event-system.md</files>
|
||||
<read_first>packages/anon-tokyo/src/interpreter.ts、packages/anon-tokyo/src/type.ts、packages-user/data-common/src/event/event.ts、packages-user/data-system/src/event/system.ts、docs/advanced/iii/event-system.md、docs/intermediate/events.md</read_first>
|
||||
<action>(1) 用 Write 新建 `docs/advanced/iii/anon-tokyo.md`(CRLF、单 H1),结构:
|
||||
- `# Anon-Tokyo 解释器` + 引言:事件脚本在数据里不是字符串,而是结构化的语句数组;解释器把语句编译成可执行程序,再由迭代器逐步执行。本篇讲这个模型,不讲事件收集与触发过滤(见[事件系统](./event-system.md))。
|
||||
- `## 系统模型`:语言特性(内建函数 + 全局函数)→ 解释器(注册、编译、执行三件事)→ 编译产物(程序节点序列)→ 执行上下文(调用栈 + 环境)→ 迭代器(反复求值当前节点)。给 `text` 数据流示意。
|
||||
- `## 语句模型`:语句种类完整列出(表达式、调用、返回、条件、分支、循环、跳出、继续、块、退出、函数声明);表达式是「接收作用域返回值的函数」;值可以是字面量或表达式;块是语句数组;作用域分参数、局部、环境三层;执行节点返回四种操作(前进、调用、返回、退出)。这一段只讲形状,不讲解析器。
|
||||
- `## 与事件对象的衔接`:事件对象把原始语句交给解释器编译并缓存;修改原始语句会让缓存失效;没有编译产物时可以退回直接执行原始语句;解释器不判断触发器、不收集坐标,这两件事在事件系统里。
|
||||
- `## 内部实现:编译与执行`:以「此章节讲解一下 Anon-Tokyo 的执行循环的内部实现」开头。讲编译期按名字解析内建函数、找不到直接抛错;运行期创建上下文、调用全局函数时压栈、`Return` 记录返回值、`Exit` 结束;给一段完整的调用示例(带注释)演示「编译一次、多次执行」,不要贴解释器私有字段。
|
||||
- `## 自定义内建函数与全局函数`:内建函数的形状(名字 + 可选的执行函数)与注册入口;当前数据端的全局函数列表为空,需要时由使用方提供;内建函数的完整写法与注册路径见[事件系统](./event-system.md#自定义一个内建函数),本篇不重复示例。
|
||||
- `## 边界与注意`(文末单列):缺失内建或全局函数会在编译期直接抛错;语句是结构化对象、不是可解析的字符串;作用域三层不要混用;`Exit` 与 `Return` 语义不同;执行上下文没有可用的导出/转储能力;内建函数上的可选字段当前没有额外语义,不要据此设计行为。
|
||||
(2) 在 `event-system.md` 中把引言里「同批的 Anon-Tokyo 解释器一篇」这处纯文本改成真实链接 `[Anon-Tokyo 解释器](./anon-tokyo.md)`;在 `anon-tokyo.md` 中至少链接 `./event-system.md`(含一个章节锚点)与 `../../intermediate/events.md`。**只有当 Task 2 把两页都写完后才允许出现这两条链接**(Task 1 不能提前写,否则 Task 1 的链接门禁会因目标不存在而失败)。
|
||||
(3) 两页自检:无 `**` 紧贴中文标点;无「模板」「[需改]」;无 `src/client`;不出现 `dump(`;不写源码行号。
|
||||
(4) 跑 `node node_modules/prettier/bin/prettier.cjs --write docs/advanced/iii/anon-tokyo.md docs/advanced/iii/event-system.md`,确认两页仍为 CRLF。Commit:`docs(10-36): 新增 Anon-Tokyo 解释器页并互链`。</action>
|
||||
<acceptance_criteria>`anon-tokyo.md` 存在、单 H1、CRLF;六个主体小节齐备;内部实现章节有规定开头句且后置;语句种类与作用域写对;两页互链为真实相对链接且可解析;不写 `dump(`;无模板标记与渲染端内容。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/iii/anon-tokyo.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' }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error 'H1-COUNT' }; foreach($needle in @('AnonTokyoInterpreter','LanguageFeature','AnonTokyoExecutable','AnonTokyoExecutionContext','StatementType','BuiltInFunction')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "NO-FACT $needle" } }; foreach($h in @('## 系统模型','## 语句模型','## 与事件对象的衔接','## 内部实现','## 自定义内建函数与全局函数','## 边界与注意')){ if($raw -notmatch [regex]::Escape($h)){ Write-Error "NO-SECTION $h" } }; if($raw -notmatch '此章节讲解一下'){ Write-Error 'NO-INTERNAL-OPENER' }; if($raw -match 'dump\('){ Write-Error 'DOCUMENTS-DUMP' }; if($raw -match 'src/client'){ Write-Error 'RENDERER-CONTENT' }; if($raw -match '\[需改\]'){ Write-Error 'TEMPLATE-MARK' }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent (Resolve-Path -LiteralPath $p)) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '\./event-system\.md'){ Write-Error 'NO-EVENT-LINK' }; if($raw -notmatch '\.\./\.\./intermediate/'){ Write-Error 'NO-INTERMEDIATE-LINK' }; $ev=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/advanced/iii/event-system.md')); if($ev -notmatch '\./anon-tokyo\.md'){ Write-Error 'NO-ANON-LINK' }; Write-Output 'PAGE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "NO-FACT", "NO-SECTION", "NO-INTERNAL-OPENER", "DOCUMENTS-DUMP", "RENDERER-CONTENT", "TEMPLATE-MARK", "DEAD-LINK", "NO-EVENT-LINK", "NO-INTERMEDIATE-LINK", or "NO-ANON-LINK"; or output lacks "PAGE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>解释器页结构与事实要点齐备;内部实现后置;两页互链可解析;不写导出/转储能力;CRLF、单 H1。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: 两页格式与构建门禁、写 SUMMARY</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-36-SUMMARY.md</files>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-35-SUMMARY.md(SUMMARY 体例)</read_first>
|
||||
<action>(1) `node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/event-system.md docs/advanced/iii/anon-tokyo.md`,通过。(2) `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,退出码 0、含 `build complete`、无 `dead link`。(3) 交叉复核两页分工:事件页不解释迭代器循环、解释器页不解释坐标收集与触发器过滤;两页都没有重复高级 I / II 已讲内容(`data-stores` 的事件仓库用法、`point-and-tile-events` 的两类事件归属)。(4) 写 `.planning/phases/10-docs-restructure/10-36-SUMMARY.md`:两页结构清单、复核的源码与结论(尤其触发器派发点)、两页分工边界、刻意未写内容(序列化注册、地图事件 id 绑定、编辑器识别、完整内建目录、`dump()`、`save` 语义)、格式与构建结论、留给 10-39 的注册与互链事项;若认为解释器页偏短,只记录「已向用户提出是否合并,未合并」。Commit:`docs(10-36): 事件与解释器两页收口`。</action>
|
||||
<acceptance_criteria>prettier `--check` 对两页通过;构建退出码 0、含 `build complete`、无 `dead link`;SUMMARY 记录两页分工、事实复核、未写内容与遗留事项。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $sum='.planning/phases/10-docs-restructure/10-36-SUMMARY.md'; if(-not(Test-Path -LiteralPath $sum)){ Write-Error "MISSING $sum" }; $s=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $sum)); foreach($needle in @('事件','解释器','触发器','构建','10-39')){ if($s -notmatch [regex]::Escape($needle)){ Write-Error "SUMMARY-MISSING $needle" } }; $ErrorActionPreference='Continue'; $pf = cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/event-system.md docs/advanced/iii/anon-tokyo.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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'GATE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "SUMMARY-MISSING", "PRETTIER-FAIL", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "GATE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>两页格式与构建双绿;SUMMARY 完整。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 构建期 markdown → 静态站点 | 正文静态渲染,无运行时输入 |
|
||||
| 读者 → 站点 | 站内链接是唯一可点击指向面 |
|
||||
| 文档 → 读者对「触发器已实现」的预期 | 过度承诺会让读者写出永远不会触发的规则 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-106 | Tampering | 触发器表述 | medium | mitigate | 断言出现「预留 / 未派发」限定词,并禁止「全部已实现」式表述 |
|
||||
| T-10-107 | Tampering / XSS | 两页正文与代码块 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-108 | Information Disclosure | 示例代码 | low | mitigate | 示例只用占位路径与公开接口名 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 结构门禁:两页单 H1、CRLF;解释器页内部实现章节后置并以规定句式开头;两页文末均有「边界与注意」。
|
||||
- 事实门禁:事件页覆盖存储 / 收集顺序 / 执行折叠 / 事件层 / 内建函数;解释器页覆盖语言特性 / 语句模型 / 衔接 / 编译执行。
|
||||
- 边界门禁:不写序列化注册、地图事件 id 绑定、编辑器识别、完整内建目录、`dump()` 能力。
|
||||
- 链接门禁:相对 `.md` 目标全部存在;两页互链成立。
|
||||
- 格式 / 构建门禁:prettier `--check` 通过;`build complete` 且无 `dead link`。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 读者能回答「这条事件谁会执行、按什么顺序、结果怎么合并」,并能写一个可注册的内建函数。
|
||||
- 解释器页只讲「脚本怎么变成程序并执行」,不与事件页重复。
|
||||
- 两页可构建、可格式化、无死链。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-36-SUMMARY.md` when done
|
||||
</output>
|
||||
156
.planning/phases/10-docs-restructure/10-37-PLAN.md
Normal file
156
.planning/phases/10-docs-restructure/10-37-PLAN.md
Normal file
@ -0,0 +1,156 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 37
|
||||
type: execute
|
||||
wave: 9
|
||||
depends_on: [10-36]
|
||||
files_modified:
|
||||
- docs/advanced/iii/passability-and-pathfinding.md
|
||||
- .planning/phases/10-docs-restructure/10-37-SUMMARY.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 30000
|
||||
raw_tokens: 20000
|
||||
tasks: 3
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "`docs/advanced/iii/passability-and-pathfinding.md` 先讲模型:通行性谓词是「一步能不能走、撞不撞」的唯一语义契约,勇士移动与寻路构图都调它,因此选出的路和实际能走的路一致(D-82)"
|
||||
- "明确写出「同一契约、两个实例」:勇士顶层实现自建一个默认谓词,寻路在初始化时另建一个注入给求解器;两者行为一致但不是同一个对象,改一侧不会自动改另一侧"
|
||||
- "`canPass` / `shouldHit` 的语义写清(进入目标格是否允许、这次移动是否该产生撞击),以及默认实现的分层判断:事件层看通行位,其它图层只看标了只判事件的图块,撞击看目标格静态图块的事件通行标记(`eventPass`)"
|
||||
- "勇士移动的三种结果写对:不能走、撞停、正常一步;撞停与不能走分别触发不同的上层回调并停止,正常一步结算时先离开再进入"
|
||||
- "寻路流程写清「构图 → 搜索 → 交给移动器」:构图从起点出发只保留可达节点、把撞击格标为终端;搜索用最小损失、终端只能作终点;系统层负责绑定移动器并把步骤交出去"
|
||||
- "自定义谓词给出两条路径:给寻路注入实现;勇士侧要换顶层实现(或改默认实现)。损失函数与瞬移策略不在本篇,链接 `../ii/pathfinding.md` 并明确不重复"
|
||||
- "文末单一「边界与注意」小节(D-61),覆盖:两个实例不共享、图层必须属于同一数据端、未注入方向处理器时构图失败、起点越界、终端只能作终点、无路径是正常结果、目标在脚下不移动、换楼层要重绑、瞬移跳过途经格"
|
||||
- "page 仍是 kebab-case、CRLF、单 H1;相对链接全部可解析;prettier `--check` 通过;构建 green、`build complete`、无 `dead link`"
|
||||
artifacts:
|
||||
- "docs/advanced/iii/passability-and-pathfinding.md"
|
||||
- ".planning/phases/10-docs-restructure/10-37-SUMMARY.md"
|
||||
key_links:
|
||||
- "研究文件 §5「通行性谓词与寻路原理」与 §6 页切分表是事实源;执行前按当前源码复核谓词接口、构图与搜索状态"
|
||||
- "与 `docs/advanced/ii/pathfinding.md` 必须切分:II 讲「怎么用 / 换损失函数 / 换瞬移策略」,III 只讲契约与「构图 → 搜索 → 移动器」;一旦开始重复 `moveTo` / `teleportTo` 用法就删掉并外链"
|
||||
- "「同一契约、两个实例」是本页最容易讲错的一点:写成「共享同一个谓词对象」会让读者改一侧却发现另一侧没变"
|
||||
---
|
||||
|
||||
<objective>
|
||||
写出高级 III 的《通行性谓词与寻路原理》:讲清通行性谓词这一个契约如何同时服务勇士移动与寻路构图,默认实现在两层地图上怎么判断,勇士移动会得到哪三种结果,寻路如何构图、搜索并把步骤交给移动器,以及要换语义时该动哪里。
|
||||
|
||||
Purpose: 高级 II 已经讲过寻路系统的用法与可替换点,但读者一旦遇到「寻路画出的路勇士走不过去」「改了谓词寻路没变」,缺的是「谁在什么时候调这个契约、有几个实例」这层认识。
|
||||
Output: `docs/advanced/iii/passability-and-pathfinding.md` 与 `10-37-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-ADVANCED-III-RESEARCH.md
|
||||
@docs/advanced/ii/pathfinding.md
|
||||
@docs/advanced/i/mover.md
|
||||
@docs/advanced/i/face-handler.md
|
||||
|
||||
**写作规范(本批统一,逐条自检):**
|
||||
- 库文档风格(D-59),简洁优先(D-60);先模型后接口(D-82)。
|
||||
- 边界单列文末(D-61);代码块完整、带注释(D-62 / D-65 / D-70);无「模板」「[需改]」(D-83)。
|
||||
- 不写源码叙述与行号(D-80);不写 Dijkstra 证明、复杂度与性能结论;不写渲染端移动动画。
|
||||
- 引用其他文档的具体章节用 `./x.md#标题` 或 `../ii/x.md#标题`(D-74)。
|
||||
- 本页与 10-36 之间**没有内容依赖**:依赖仅用于串行化构建产物目录(同一个 `outDir` 不能并发构建);写作时不需要读上一页。
|
||||
- research 是权威事实源但行号会漂移:执行前打开当前源码复核谓词接口、默认实现的分支、构图与搜索的状态枚举;注释与实现不一致按 D-72 以注释为准,只在汇报中列出(瞬移钩子的时序疑点属 research C8,不得写进正文)。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报本篇计划并等待「可以执行」</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-CONTEXT.md(D-12、D-60、D-61、D-77、D-80)、.planning/phases/10-docs-restructure/10-ADVANCED-III-RESEARCH.md(§5 通行性谓词与寻路、Page Split 的 merge/split flags、Pitfall 2)</read_first>
|
||||
<action>向用户汇报四段并等待批准(D-12):**要做什么**——新建 `docs/advanced/iii/passability-and-pathfinding.md`,按「模型 → 谓词契约 → 默认实现 → 两个实例 → 勇士移动三种结果 → 构图 → 搜索 → 系统层 → 自定义谓词 → 边界与注意」组织。**为什么**——II 讲的是用法与可替换点;读者真正会卡住的是「谓词只有一个吗」「为什么改了勇士的规则寻路没变」,本页把契约与实例关系讲透。**大致怎么做**——Task 1 复核源码后落稿并格式化;Task 2 跑构建与结构门禁、写 SUMMARY。**影响范围**——只新增这一篇与 SUMMARY;不动 config、不动 II 的寻路页、不动源码。汇报中明确:本篇重心是自定义(D-77);不重复 `moveTo` / `teleportTo` 的完整用法与损失函数/瞬移策略示例,一律外链 `../ii/pathfinding.md`;不写算法证明与复杂度;不写渲染端移动表现;瞬移钩子的时序疑点(research C8)只对话报告(D-72)。汇报中说明本计划的修改方式为**派发子代理执行**,范围严格限于本页与 SUMMARY。停下等待用户明确回复「可以执行」四个字;未收到前不得建文件、不得派发写作子代理。</action>
|
||||
<what-built>本计划内容:新建《通行性谓词与寻路原理》一页。</what-built>
|
||||
<how-to-verify>确认与 `ii/pathfinding.md` 的切分、「同一契约、两个实例」的讲法,以及不写算法证明与渲染端动画的边界。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<acceptance_criteria>用户已回复「可以执行」;若对「两个实例」的讲法或与 II 的切分提出意见,已并入后续任务。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已回复「可以执行」,且未否决本页与 `ii/pathfinding.md` 的分工。</human-check>
|
||||
</verify>
|
||||
<done>用户回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 复核通行性与寻路事实并落稿</name>
|
||||
<files>docs/advanced/iii/passability-and-pathfinding.md</files>
|
||||
<read_first>packages-user/data-base/src/map/types.ts(通行性谓词与其检查处理器)、packages-user/data-base/src/hero/types.ts(勇士顶层实现)、packages-user/data-base/src/hero/mover.ts(单步三种结果与结算回调)、packages-user/data-system/src/path/graph.ts、packages-user/data-system/src/path/finder.ts、packages-user/data-system/src/path/system.ts、packages-user/data-system/src/path/types.ts、src/data/hero/predicate.ts、src/data/hero/moverImpl.ts、src/data/initialize.ts、docs/advanced/ii/pathfinding.md、docs/advanced/i/mover.md、docs/advanced/i/face-handler.md</read_first>
|
||||
<action>(1) 用 Write 新建 `docs/advanced/iii/passability-and-pathfinding.md`(CRLF、单 H1),结构:
|
||||
- `# 通行性谓词与寻路原理` + 引言:通行性谓词是「一步能不能走、撞不撞」的唯一语义契约;勇士移动与寻路构图都调它,所以「寻路选出的路」和「勇士实际能走的路」在默认实现下一致。本篇讲契约与流程,损失函数与瞬移策略见[自动寻路](../ii/pathfinding.md)。
|
||||
- `## 系统模型`:两个消费方——勇士移动器在一步开始时问「能不能进、要不要撞」,寻路构图在扩展相邻格时问同一组问题;寻路本身是「构图 → 搜索 → 交给移动器」。用 `text` 数据流示意。
|
||||
- `## 通行性谓词契约`:`canPass` 判断从当前格进入目标格是否允许,`shouldHit` 判断这次移动是否该产生撞击;两者接收同一个检查处理器(当前格、目标格、方向、地图)。一句话点出:`shouldHit` 不是「不能走」,而是「走得到但会撞」。
|
||||
- `## 默认实现怎么判断`:先在方向组里把方向降级到四向;事件层按当前格与目标格的通行位判断;其它图层只对「只判事件」的图块检查通行位;撞击看目标格静态图块的事件通行标记(`eventPass`)。给一段完整函数示例,演示「按默认规则判断某一步的结果」,带注释。
|
||||
- `## 同一契约、两个实例`:勇士顶层实现里自建一个默认谓词并直接用;数据端初始化时又为寻路求解器新建一个注入。行为一致、对象不同——改勇士侧不会自动改寻路侧,反之亦然。这一段是本页的核心,务必写清「要一致就两边都换」。
|
||||
- `## 勇士移动的三种结果`:不能走(调用上层的「无法进入」回调并停止)、撞停(调用「撞击」回调并停止)、正常一步(返回下一格);一次成功结算先触发离开、再触发进入。三种结果与触发器派发的对应关系一句话带过(细节见事件系统一篇,用文字提及即可,不要放链接——该页由另一计划创建)。
|
||||
- `## 构图:从起点只保留可达节点`:构图需要图层、谓词与方向处理器都已注入,起点必须在地图内;从起点广度扩展,跳过原地;每一步用谓词判通行,撞击格记为终端;只把从起点可达的节点放进图里。损失值非法时回退为 1。
|
||||
- `## 搜索:最小损失与终端约束`:在图上做最小损失搜索;起点等于终点直接返回「目标在脚下」;目标不在图里返回空;终端节点只能作为终点、不能作为中间点;四个结果状态(成功 / 输入不合法 / 无路径 / 目标在脚下)与各自含义。
|
||||
- `## 系统层做什么`:绑定移动器、绑定图层与方向处理器、绑定谓词;`getPath` 从移动器取起点;`moveTo` 恒逐步;`teleportTo` 交给回退策略决定是否逐步;忙碌时返回空;中断会等待底层停止。**只写这些职责,不抄 II 的完整示例**。
|
||||
- `## 换一套通行语义`(自定义重心):给完整类实现谓词接口(两个方法 + 检查处理器入参),带注释;寻路侧用求解器的注入方法换掉;勇士侧要么实现自己的勇士顶层实现并替换,要么改默认实现;说明「两边都换」才一致。链接 `../i/mover.md`(移动器)与 `../i/face-handler.md`(方向处理器)。
|
||||
- `## 边界与注意`(文末单列):两个实例不共享;图层必须属于同一数据端,否则告警并忽略绑定;方向处理器未注入时构图失败;起点越界会告警并返回失败;终端只能作终点;无路径是正常结果;目标在脚下不移动;换楼层要重新绑定图层;瞬移会跳过途经格。
|
||||
(2) 全文自检:`IPassPredicate` / `IPassCheckHandler` / `MapGraphBuilder` / `PathFinder` / `PathfindingSystem` / `DefaultPassPredicateImpl` 均出现;至少一条 `../ii/pathfinding.md` 与一条 `../i/` 链接;不出现 `src/client`;无 `**` 紧贴中文标点;无「模板」「[需改]」;无「源码第 X 行」。
|
||||
(3) 跑 `node node_modules/prettier/bin/prettier.cjs --write docs/advanced/iii/passability-and-pathfinding.md`,确认 CRLF。Commit:`docs(10-37): 新增通行性与寻路原理页`。</action>
|
||||
<acceptance_criteria>文件存在、单 H1、CRLF;十个主体小节齐备且顺序正确;「同一契约、两个实例」有独立小节且结论明确;三类勇士移动结果与四种寻路状态写对;自定义谓词给完整类与两条注入路径;边界九项齐全;不重复 II 的用法示例。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/iii/passability-and-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' }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error 'H1-COUNT' }; foreach($needle in @('IPassPredicate','IPassCheckHandler','MapGraphBuilder','PathFinder','PathfindingSystem','DefaultPassPredicateImpl','canPass','shouldHit')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "NO-FACT $needle" } }; foreach($h in @('## 系统模型','## 通行性谓词契约','## 默认实现怎么判断','## 同一契约、两个实例','## 勇士移动的三种结果','## 构图','## 搜索','## 系统层做什么','## 换一套通行语义','## 边界与注意')){ if($raw -notmatch [regex]::Escape($h)){ Write-Error "NO-SECTION $h" } }; if($raw -notmatch '两个实例|两个对象|不是同一个'){ Write-Error 'NO-TWO-INSTANCES' }; if($raw -match 'src/client'){ Write-Error 'RENDERER-CONTENT' }; if($raw -match '\[需改\]'){ Write-Error 'TEMPLATE-MARK' }; if($raw -match '(?m)^#{1,3} .*模板'){ Write-Error 'TEMPLATE-HEADING' }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent (Resolve-Path -LiteralPath $p)) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '\.\./ii/pathfinding\.md'){ Write-Error 'NO-II-LINK' }; if($raw -notmatch '\.\./i/'){ Write-Error 'NO-I-LINK' }; if($raw -notmatch 'eventPass'){ Write-Error 'NO-EVENTPASS' }; Write-Output 'PAGE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "NO-FACT", "NO-SECTION", "NO-TWO-INSTANCES", "RENDERER-CONTENT", "TEMPLATE-MARK", "TEMPLATE-HEADING", "DEAD-LINK", "NO-II-LINK", "NO-I-LINK", or "NO-EVENTPASS"; or output lacks "PAGE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>页面结构与事实要点齐备;两实例关系写清;自定义路径完整;边界齐全;链接可解析;CRLF、单 H1。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 格式与构建门禁、写 SUMMARY</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-37-SUMMARY.md</files>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-36-SUMMARY.md(SUMMARY 体例)</read_first>
|
||||
<action>(1) `node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/passability-and-pathfinding.md`,通过。(2) `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,退出码 0、含 `build complete`、无 `dead link`。(3) 交叉复核与 II 的切分:本篇不出现 `moveTo` / `teleportTo` 的完整示例、不出现损失函数与瞬移策略的实现;若发现重复,删掉并保留链接。(4) 写 `.planning/phases/10-docs-restructure/10-37-SUMMARY.md`:结构清单、复核的源码与结论(谓词分支、构图与搜索状态)、与 II 的切分点、刻意未写内容(算法证明、复杂度、渲染端动画、瞬移钩子时序)、格式与构建结论、留给 10-39 的注册与互链事项。Commit:`docs(10-37): 通行性与寻路原理页收口`。</action>
|
||||
<acceptance_criteria>prettier `--check` 通过;构建退出码 0、含 `build complete`、无 `dead link`;SUMMARY 记录切分点、事实复核、未写内容与遗留事项。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $sum='.planning/phases/10-docs-restructure/10-37-SUMMARY.md'; if(-not(Test-Path -LiteralPath $sum)){ Write-Error "MISSING $sum" }; $s=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $sum)); foreach($needle in @('谓词','寻路','两个实例','构建','10-39')){ if($s -notmatch [regex]::Escape($needle)){ Write-Error "SUMMARY-MISSING $needle" } }; $ErrorActionPreference='Continue'; $pf = cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/passability-and-pathfinding.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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'GATE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "SUMMARY-MISSING", "PRETTIER-FAIL", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "GATE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>格式与构建双绿;SUMMARY 完整。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 构建期 markdown → 静态站点 | 正文静态渲染,无运行时输入 |
|
||||
| 读者 → 站点 | 站内链接是唯一可点击指向面 |
|
||||
| 文档 → 读者对「同一谓词」的预期 | 写成共享对象会导致改一侧无效的误判 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-109 | Tampering | 「同一契约、两个实例」表述 | medium | mitigate | 断言出现「两个实例 / 不是同一个」限定,避免读者以为共享对象 |
|
||||
| T-10-110 | Tampering / XSS | 正文与代码块 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-111 | Information Disclosure | 示例代码 | low | mitigate | 示例只用占位路径与公开接口名 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 结构门禁:单 H1、CRLF、十个小节齐备、文末「边界与注意」。
|
||||
- 事实门禁:谓词契约、默认实现分支、三结果、构图/搜索约束、四状态、系统层职责。
|
||||
- 切分门禁:不重复 II 的用法与策略示例;损失函数与瞬移策略只外链。
|
||||
- 链接门禁:相对 `.md` 目标全部存在;含 `../ii/pathfinding.md` 与 `../i/`。
|
||||
- 格式 / 构建门禁:prettier `--check` 通过;`build complete` 且无 `dead link`。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 读者能解释「寻路和勇士为什么一致、为什么不共享一个对象」,并知道换语义要动两处。
|
||||
- 与高级 II 无重复,页面可构建、可格式化、无死链。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-37-SUMMARY.md` when done
|
||||
</output>
|
||||
180
.planning/phases/10-docs-restructure/10-38-PLAN.md
Normal file
180
.planning/phases/10-docs-restructure/10-38-PLAN.md
Normal file
@ -0,0 +1,180 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 38
|
||||
type: execute
|
||||
wave: 10
|
||||
depends_on: [10-37]
|
||||
files_modified:
|
||||
- docs/advanced/iii/core-state.md
|
||||
- docs/advanced/iii/plugin-internals.md
|
||||
- .planning/phases/10-docs-restructure/10-38-SUMMARY.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 40000
|
||||
raw_tokens: 27000
|
||||
tasks: 4
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "`core-state.md` 先讲模型(数据端组合根:按层装配子系统、注册并排序插件、串起加载与存档),再讲入口职责、加载管线、存档挂载与第二个实例,**最后**才进装配顺序这一内部实现小节(D-82 / D-69)"
|
||||
- "装配顺序按四层写清且粒度正确:L0 公共数据(朝向、面管理器、图块 / 道具 / 地图 / 事件 / 怪物仓库、录像)、L1 状态基座(标记、地图、勇士、怪物管理)、L2 系统层(怪物上下文、战斗、事件系统、寻路)、L3 顶层(加载管理器、加载器);不写私有字段名"
|
||||
- "加载管线写清:先插件 `initialize`、再加载器 `start`(可迭代推进)、再插件 `afterLoad`,最后检查插件状态并告警未完成者"
|
||||
- "入口职责写清:数据端入口只重新导出创建函数;配置入口组装加载 starter、核心配置地址、插件列表与事件注册;主插件负责朝向、寻路注入、勇士顶层实现、加载钩子、各系统初始化与五个可存档对象的注册;`afterLoad` 后再创建勇士属性与位置"
|
||||
- "存档部分写清注册与执行器两条线:可存档对象按 id 注册(重复 id 告警并保持唯一)、执行器可绑定 id 或对象(未注册对象告警)、读档时逐个对象短暂禁用录像、执行器在包裹之外运行"
|
||||
- "「游戏内第二个数据端实例」单独成节:构造器不依赖全局单例,传完整配置即可建独立实例,可用于回放 / 对比;必须提供完整配置字段,不能只给加载 starter 与核心配置地址"
|
||||
- "`plugin-internals.md` 先讲模型,再讲三阶段用法、取回与失败现象,**最后**才进依赖排序与状态机这两节内部实现;不重复 `../i/plugin-system.md` 的插件写法(D-69)"
|
||||
- "排序与失败写对:缺失依赖与环会阻断该插件及其下游;重复 id 在构造时告警并跳过;每个阶段只处理处于前一状态的插件;依赖在启动前固定、运行时不重算也不能追加"
|
||||
- "两页都是 kebab-case、CRLF、单 H1;内部实现章节后置并以「此章节讲解一下 XXX 的内部实现」开头;相对链接全部可解析;prettier `--check` 通过;构建 green、`build complete`、无 `dead link`"
|
||||
artifacts:
|
||||
- "docs/advanced/iii/core-state.md"
|
||||
- "docs/advanced/iii/plugin-internals.md"
|
||||
- ".planning/phases/10-docs-restructure/10-38-SUMMARY.md"
|
||||
key_links:
|
||||
- "研究文件 §6「CoreState 组装初始化」与 §7「插件内部机制」是事实源;执行前按当前源码复核层级归属、状态枚举与告警码"
|
||||
- "两页分工:`core-state` 讲「装配顺序与生命周期」,`plugin-internals` 讲「顺序怎么算出来、失败会怎样」;两页都不讲 L4/L5 渲染层与客户端主对象"
|
||||
- "`core-state` 的五个可存档 id 必须与 `src/data/initialize.ts` 当前登记一致;不一致时以源码为准并在 SUMMARY 记录"
|
||||
---
|
||||
|
||||
<objective>
|
||||
写出高级 III 的装配两篇:《CoreState 组装与初始化》讲数据端组合根按什么顺序装配、加载分几段、入口各自负责什么、存档挂在哪里;《插件内部机制》讲插件表如何变成确定顺序、状态如何推进、依赖写错会怎样。
|
||||
|
||||
Purpose: 读者新增插件、注册存档对象、或在游戏内再建一个数据端实例时,必须知道「构造期做了什么、加载期做了什么、顺序是谁定的」。这两页把「为什么我的插件拿不到另一个插件」「为什么读档后录像状态不对」这类问题的答案落到装配与排序上,而不是靠猜。
|
||||
Output: `docs/advanced/iii/core-state.md`、`docs/advanced/iii/plugin-internals.md`、`10-38-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-ADVANCED-III-RESEARCH.md
|
||||
@docs/advanced/i/plugin-system.md
|
||||
@docs/advanced/i/saveable-content.md
|
||||
|
||||
**写作规范(本批统一,逐条自检):**
|
||||
- 库文档风格(D-59),简洁优先(D-60);先模型后接口(D-82);内部实现后置并以「此章节讲解一下 XXX 的内部实现」开头(D-69)。
|
||||
- 边界单列文末(D-61);代码块完整、带注释(D-62 / D-65 / D-70);无「模板」「[需改]」(D-83)。
|
||||
- 不写源码叙述与行号(D-80);不写用户提问来源(D-81);不写 L4/L5 渲染层与 `ClientCore`。
|
||||
- 引用其他文档的具体章节用 `./x.md#标题` 或 `../i/x.md#标题`(D-74)。
|
||||
- 本页与 10-37 之间**没有内容依赖**:依赖仅用于串行化构建产物目录(同一个 `outDir` 不能并发构建);写作时不需要读上一页。
|
||||
- research 是权威事实源但行号会漂移:执行前打开当前源码复核构造顺序、状态枚举与告警码;注释与实现不一致按 D-72 以注释为准,只在汇报中列出(research C2 属渲染端插件生命周期、C3 属测试与构造契约,均不得写进正文;C3 只用于提醒「必须传完整配置」)。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报装配两篇计划并等待「可以执行」</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-CONTEXT.md(D-12、D-55、D-60、D-61、D-69、D-77、D-82)、.planning/phases/10-docs-restructure/10-ADVANCED-III-RESEARCH.md(§6 CoreState、§7 插件内部机制、Page Split 的 merge/split flags)</read_first>
|
||||
<action>向用户汇报四段并等待批准(D-12):**要做什么**——新建 `docs/advanced/iii/core-state.md`(模型 → 装配顺序 → 加载管线 → 入口职责 → 存档挂载 → 第二个实例 → 边界)与 `docs/advanced/iii/plugin-internals.md`(模型 → 依赖排序 → 状态机 → 三阶段 → 失败模式 → 取回 → 边界)。**为什么**——读者新增插件或注册存档对象时,最需要的是「构造期与加载期各做了什么、顺序谁定」,这两页把问题落到装配与排序上;同时必须与高级 I 的插件写法页分工,否则会重复入门内容。**大致怎么做**——Task 1 落 CoreState 页,Task 2 落插件内部页并互链,Task 3 跑格式与构建、写 SUMMARY。**影响范围**——只新增两页与 SUMMARY;不动 config、不动 I 的插件页、不动源码。汇报中明确:两页重心都是「使用为主、解释内部」(D-77);`orderPlugins` 只解释、不建议读者调用;不写 L4/L5 渲染层与 `ClientCore`;不提供重排构造顺序的做法;research 建议「若必须压缩可把插件内部并入 CoreState」,但**页面清单已由用户锁定为独立成页**,不合并,如成稿偏短只向用户提出(D-13)。汇报中说明本计划的修改方式为**派发子代理执行**,范围严格限于两页与 SUMMARY。停下等待用户明确回复「可以执行」四个字;未收到前不得建文件、不得派发写作子代理。</action>
|
||||
<what-built>本计划内容:新建《CoreState 组装与初始化》与《插件内部机制》两页并互链。</what-built>
|
||||
<how-to-verify>确认两页分工、不写 L4/L5 渲染层与 `ClientCore`,以及不提供重排构造顺序的做法。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<acceptance_criteria>用户已回复「可以执行」;若对两页分工或「第二个实例」一节的写法提出意见,已并入后续任务。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已回复「可以执行」,且未要求合并两页或改变分工。</human-check>
|
||||
</verify>
|
||||
<done>用户回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 复核装配事实并落稿 core-state.md</name>
|
||||
<files>docs/advanced/iii/core-state.md</files>
|
||||
<read_first>packages-user/data-state/src/core.ts、packages-user/data-state/src/types.ts、packages-user/data-state/src/plugin.ts、packages-user/data-state/src/loader/loader.ts、src/data.ts、src/data/entry.ts、src/data/initialize.ts、src/data/plugin/list.ts、docs/advanced/i/plugin-system.md、docs/advanced/i/saveable-content.md</read_first>
|
||||
<action>(1) 用 Write 新建 `docs/advanced/iii/core-state.md`(CRLF、单 H1),结构:
|
||||
- `# CoreState 组装与初始化` + 引言:数据端主对象是组合根,构造期把所有子系统按层装好、注册并排序插件,加载期按固定次序推进,存档与执行器挂在它身上。本篇只讲数据端;渲染层与客户端主对象不在范围内。
|
||||
- `## 系统模型`:为什么需要组合根(读者拿到的一切入口都从它出发、层与层之间由它注入、生命周期只有一套);四层的分工一句话概括。给 `text` 分层示意。
|
||||
- `## 入口的职责`:数据端入口只重新导出创建函数;配置入口组装加载 starter、核心配置地址、插件列表与事件注册;主插件做朝向、寻路注入、勇士顶层实现、加载钩子、各系统初始化与五个可存档对象注册;`afterLoad` 之后才创建勇士属性与位置。列出五个可存档 id(按源码当前值),并链接 `../i/saveable-content.md`。
|
||||
- `## 加载管线`:插件 `initialize` → 加载器 `start`(异步迭代推进)→ 插件 `afterLoad` → 检查插件状态并告警未完成者。说明为什么 `initialize` 在加载前(此时可追加配置)、`afterLoad` 在加载后(此时内容已就绪)。链接 `../i/plugin-system.md`。
|
||||
- `## 存档挂载`:可存档对象按 id 注册,重复 id 告警并保持唯一;执行器可绑 id 或对象,绑未注册对象会告警;读档时逐个对象短暂禁用录像、读完恢复,执行器在这层包裹之外运行。给完整函数示例演示「注册一个可存档对象并绑定执行器」,带注释。
|
||||
- `## 在游戏内另建一个数据端实例`:构造器不依赖全局单例;传完整配置即可得到独立实例,可与主实例并行用于回放或结果对比;必须提供完整配置字段(加载 starter、核心配置地址、插件列表、事件注册),缺项会导致构造期失败。给完整函数示例(带注释),不写测试文件路径。
|
||||
- `## 内部实现:装配顺序`(**放在全部使用侧小节之后、边界之前**):以「此章节讲解一下 CoreState 的装配顺序的内部实现」开头。按 L0 → L1 → L2 → L3 列出每层装了什么(用接口名与职责,不写私有字段名);点明「需要回指主对象的子系统在构造时拿到它」;点明装配完成后才注册插件并同步执行装配期回调。
|
||||
- `## 边界与注意`(文末单列):加载必须被迭代推进才会真正执行;配置四个字段都必填;重复可存档 id 告警且只保留一个;执行器绑未注册对象告警;读档缺键与多余键分别告警;插件未到完成态会告警;读档期间录像被临时禁用;不要重排装配顺序、也不要手动构造内部子系统。
|
||||
(2) 自检:不出现 `src/client`、`ClientCore`;不写「源码第 X 行」;无 `**` 紧贴中文标点;无「模板」「[需改]」;至少一条 `../i/` 链接(建议两条:`../i/plugin-system.md`、`../i/saveable-content.md`)。
|
||||
(3) 跑 `node node_modules/prettier/bin/prettier.cjs --write docs/advanced/iii/core-state.md`,确认 CRLF。Commit:`docs(10-38): 新增 CoreState 组装原理页`。</action>
|
||||
<acceptance_criteria>文件存在、单 H1、CRLF;七个主体小节齐备;装配顺序按四层且无私有字段名;加载管线四步准确;入口职责与五个可存档 id 与源码一致;第二个实例有完整示例;边界九项齐全;无渲染层内容。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/iii/core-state.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' }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error 'H1-COUNT' }; foreach($needle in @('CoreState','createCoreState','addSaveableContent','bindSaveableExecuter','initialize','afterLoad','LoadManager')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "NO-FACT $needle" } }; foreach($h in @('## 系统模型','## 内部实现:装配顺序','## 加载管线','## 入口的职责','## 存档挂载','## 在游戏内另建一个数据端实例','## 边界与注意')){ if($raw -notmatch [regex]::Escape($h)){ Write-Error "NO-SECTION $h" } }; if($raw -notmatch '此章节讲解一下'){ Write-Error 'NO-INTERNAL-OPENER' }; if($raw -match 'ClientCore|src/client'){ Write-Error 'RENDERER-CONTENT' }; if($raw -match '\[需改\]'){ Write-Error 'TEMPLATE-MARK' }; if($raw -match '(?m)^#{1,3} .*模板'){ Write-Error 'TEMPLATE-HEADING' }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent (Resolve-Path -LiteralPath $p)) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '\.\./i/plugin-system\.md'){ Write-Error 'NO-PLUGIN-LINK' }; if($raw -notmatch '\.\./i/saveable-content\.md'){ Write-Error 'NO-SAVEABLE-LINK' }; $internal=$raw.IndexOf('## 内部实现:装配顺序'); $usage=$raw.IndexOf('## 在游戏内另建一个数据端实例'); if($internal -lt 0){ Write-Error 'NO-INTERNAL-SECTION' }; if($usage -lt 0){ Write-Error 'NO-USAGE-SECTION' }; if($internal -lt $usage){ Write-Error 'INTERNAL-NOT-LATE' }; foreach($id in @('@system/hero','@system/flags','@system/maps','@system/enemy','@system/replay')){ if($raw -notmatch [regex]::Escape($id)){ Write-Error "NO-SAVEABLE-ID $id" } }; Write-Output 'PAGE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "NO-FACT", "NO-SECTION", "NO-INTERNAL-OPENER", "RENDERER-CONTENT", "TEMPLATE-MARK", "TEMPLATE-HEADING", "DEAD-LINK", "NO-PLUGIN-LINK", "NO-SAVEABLE-LINK", "NO-SAVEABLE-ID", "NO-INTERNAL-SECTION", "NO-USAGE-SECTION", or "INTERNAL-NOT-LATE"; or output lacks "PAGE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>CoreState 页结构与事实要点齐备;装配顺序四层清晰;五个可存档 id 与源码一致;第二个实例示例完整;边界齐全;CRLF、单 H1。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 落稿 plugin-internals.md 并收口两页互链</name>
|
||||
<files>docs/advanced/iii/plugin-internals.md, docs/advanced/iii/core-state.md</files>
|
||||
<read_first>packages-user/data-state/src/plugin.ts、packages-user/data-state/src/core.ts(awake / initialize / afterLoad / getDataPlugin)、packages-user/data-state/src/types.ts(插件状态与接口)、docs/advanced/i/plugin-system.md、docs/advanced/iii/core-state.md</read_first>
|
||||
<action>(1) 用 Write 新建 `docs/advanced/iii/plugin-internals.md`(CRLF、单 H1),结构:
|
||||
- `# 插件内部机制` + 引言:本篇讲插件表怎么变成确定顺序、状态怎么推进、依赖写错会怎样;怎么写一个插件见[插件系统](../i/plugin-system.md),本篇不重复。
|
||||
- `## 系统模型`:插件表(id → 插件)→ 依赖图(每个插件的依赖 id 列表)→ 拓扑序(外加环与缺失依赖的检测)→ 状态机 → 三阶段执行。给 `text` 示意。
|
||||
- `## 三个阶段怎么执行`:装配期回调同步执行并拿到依赖对象;初始化与加载后回调是异步的,按同一顺序等待;依赖对象是包装后的只读视图。说明每个阶段「适合做什么」(装配期建对象、初始化期追加加载配置、加载后读内容建状态)。
|
||||
- `## 取回一个插件`:按 id 取回得到只读包装或空值;取回前判断非空并检查状态,不要在装配期之前取。链接 `./core-state.md#加载管线`。
|
||||
- `## 失败会怎样`:环、缺失依赖、重复 id、状态不符、启动后仍未完成的告警语义;阻断会沿下游扩散,被阻断的插件不会执行任何阶段。**不罗列完整告警码表**,只写会看到什么现象(控制台提示 + 插件静默不执行)。
|
||||
- `## 内部实现:依赖排序`(**放在使用侧小节之后**):以「此章节讲解一下插件依赖排序的内部实现」开头。讲四步:先查缺失依赖(依赖 id 不在表里);再从每个插件沿依赖边搜索回到自身来查环;被环或缺失阻断的插件连同其下游一起标记为阻断;最后反复扫描,输出「依赖都已就绪」的插件,得到拓扑序。说明排序只在构造期算一次。
|
||||
- `## 状态机`(与排序同属内部机制,放在使用侧之后):四个状态(未装配 / 已装配 / 已初始化 / 已完成)与推进条件;每个阶段只处理处于前一状态的插件,跳过顺序不对的。
|
||||
- `## 边界与注意`(文末单列):依赖在启动前固定、运行时不重算也不支持追加插件;环或缺失会阻断下游;重复 id 构造期告警并跳过;状态不符的阶段会被跳过;数据端依赖与渲染端依赖是两套、不要混用;取回结果要判空并检查状态。
|
||||
(2) 在 `core-state.md` 的对应段落补一条指向 `./plugin-internals.md` 的真实链接(一句即可,不要重复本篇内容)。
|
||||
(3) 自检:不出现 `src/client`、`ClientCore`;不写「源码第 X 行」;不重复 `../i/plugin-system.md` 的插件写法(不贴插件类模板);无 `**` 紧贴中文标点;无「模板」「[需改]」。
|
||||
(4) 跑 `node node_modules/prettier/bin/prettier.cjs --write docs/advanced/iii/plugin-internals.md docs/advanced/iii/core-state.md`,确认两页 CRLF。Commit:`docs(10-38): 新增插件内部机制页并互链`。</action>
|
||||
<acceptance_criteria>`plugin-internals.md` 存在、单 H1、CRLF;七个小节齐备;排序四步与状态机准确;失败模式写现象而非码表;不重复插件写法;两页互链可解析;无渲染端内容。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $p='docs/advanced/iii/plugin-internals.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' }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error 'H1-COUNT' }; foreach($needle in @('orderPlugins','getDataPlugin','dataDeps','Awakened','Initialized','Loaded')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "NO-FACT $needle" } }; foreach($h in @('## 系统模型','## 内部实现:依赖排序','## 状态机','## 三个阶段怎么执行','## 失败会怎样','## 取回一个插件','## 边界与注意')){ if($raw -notmatch [regex]::Escape($h)){ Write-Error "NO-SECTION $h" } }; if($raw -notmatch '此章节讲解一下'){ Write-Error 'NO-INTERNAL-OPENER' }; if($raw -match 'ClientCore|src/client'){ Write-Error 'RENDERER-CONTENT' }; if($raw -match '\[需改\]'){ Write-Error 'TEMPLATE-MARK' }; if($raw -match '(?m)^#{1,3} .*模板'){ Write-Error 'TEMPLATE-HEADING' }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent (Resolve-Path -LiteralPath $p)) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error ("DEAD-LINK " + $m.Groups[1].Value) } }; if($raw -notmatch '\.\./i/plugin-system\.md'){ Write-Error 'NO-PLUGIN-LINK' }; if($raw -notmatch '\./core-state\.md'){ Write-Error 'NO-CORE-LINK' }; $c=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/advanced/iii/core-state.md')); if($c -notmatch '\./plugin-internals\.md'){ Write-Error 'NO-PLUGIN-INTERNALS-LINK' }; $internal=$raw.IndexOf('## 内部实现:依赖排序'); $usage=$raw.IndexOf('## 取回一个插件'); if($internal -lt 0){ Write-Error 'NO-INTERNAL-SECTION' }; if($usage -lt 0){ Write-Error 'NO-USAGE-SECTION' }; if($internal -lt $usage){ Write-Error 'INTERNAL-NOT-LATE' }; Write-Output 'PAGE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1-COUNT", "NO-FACT", "NO-SECTION", "NO-INTERNAL-OPENER", "RENDERER-CONTENT", "TEMPLATE-MARK", "TEMPLATE-HEADING", "DEAD-LINK", "NO-PLUGIN-LINK", "NO-CORE-LINK", "NO-PLUGIN-INTERNALS-LINK", "NO-INTERNAL-SECTION", "NO-USAGE-SECTION", or "INTERNAL-NOT-LATE"; or output lacks "PAGE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>插件内部页结构与事实要点齐备;排序与状态机准确;失败模式写现象;两页互链成立;CRLF、单 H1。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: 两页格式与构建门禁、写 SUMMARY</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-38-SUMMARY.md</files>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-37-SUMMARY.md(SUMMARY 体例)</read_first>
|
||||
<action>(1) `node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/core-state.md docs/advanced/iii/plugin-internals.md`,通过。(2) `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,退出码 0、含 `build complete`、无 `dead link`。(3) 交叉复核:`core-state` 不解释拓扑排序细节、`plugin-internals` 不重讲插件写法;两页都不出现 `ClientCore` / `src/client`。(4) 写 `.planning/phases/10-docs-restructure/10-38-SUMMARY.md`:两页结构清单、复核的源码与结论(层级归属、五个可存档 id、状态枚举、告警现象)、两页分工边界、刻意未写内容(告警码表、L4/L5 渲染层、重排构造建议)、格式与构建结论、留给 10-39 的注册与互链事项;如发现 research 与当前源码不一致,只写「已在对话报告」一句。Commit:`docs(10-38): CoreState 与插件内部两页收口`。</action>
|
||||
<acceptance_criteria>prettier `--check` 对两页通过;构建退出码 0、含 `build complete`、无 `dead link`;SUMMARY 记录两页分工、事实复核、未写内容与遗留事项。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $sum='.planning/phases/10-docs-restructure/10-38-SUMMARY.md'; if(-not(Test-Path -LiteralPath $sum)){ Write-Error "MISSING $sum" }; $s=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $sum)); foreach($needle in @('CoreState','插件','装配','构建','10-39')){ if($s -notmatch [regex]::Escape($needle)){ Write-Error "SUMMARY-MISSING $needle" } }; $ErrorActionPreference='Continue'; $pf = cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/core-state.md docs/advanced/iii/plugin-internals.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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'GATE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "SUMMARY-MISSING", "PRETTIER-FAIL", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "GATE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>两页格式与构建双绿;SUMMARY 完整。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 构建期 markdown → 静态站点 | 正文静态渲染,无运行时输入 |
|
||||
| 读者 → 站点 | 站内链接是唯一可点击指向面 |
|
||||
| 文档 → 读者对装配顺序的预期 | 引导读者重排构造会破坏存档与录像一致性 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-112 | Tampering | 「重排构造顺序」类建议 | medium | mitigate | 只解释顺序,不给重排方案;边界写明不要手动构造内部子系统 |
|
||||
| T-10-113 | Tampering / XSS | 两页正文与代码块 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-114 | Information Disclosure | 示例代码与 SUMMARY | low | mitigate | 示例只用占位路径与公开接口名 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 结构门禁:两页单 H1、CRLF;内部实现章节后置并以规定句式开头;文末均有「边界与注意」。
|
||||
- 事实门禁:四层装配、加载四步、入口职责、五个可存档 id、排序四步、四状态、三阶段、失败现象。
|
||||
- 分工门禁:`core-state` 不解释拓扑排序,`plugin-internals` 不重讲插件写法;两页均无渲染层内容。
|
||||
- 链接门禁:相对 `.md` 目标全部存在;两页互链成立。
|
||||
- 格式 / 构建门禁:prettier `--check` 通过;`build complete` 且无 `dead link`。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 读者能说清构造期与加载期各自的职责、插件顺序怎么来的、依赖写错会看到什么。
|
||||
- 两页与高级 I 的插件写法页无重复,且不引向不稳定的渲染层。
|
||||
- 两页可构建、可格式化、无死链。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-38-SUMMARY.md` when done
|
||||
</output>
|
||||
200
.planning/phases/10-docs-restructure/10-39-PLAN.md
Normal file
200
.planning/phases/10-docs-restructure/10-39-PLAN.md
Normal file
@ -0,0 +1,200 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 39
|
||||
type: execute
|
||||
wave: 11
|
||||
depends_on: [10-34, 10-35, 10-36, 10-37, 10-38]
|
||||
files_modified:
|
||||
- docs/advanced/iii/index.md
|
||||
- docs/advanced/iii/loading-system.md
|
||||
- docs/advanced/iii/replay-internals.md
|
||||
- docs/advanced/iii/event-system.md
|
||||
- docs/advanced/iii/anon-tokyo.md
|
||||
- docs/advanced/iii/passability-and-pathfinding.md
|
||||
- docs/advanced/iii/core-state.md
|
||||
- docs/advanced/iii/plugin-internals.md
|
||||
- docs/.vitepress/config.ts
|
||||
- docs/advanced/index.md
|
||||
- docs/advanced/i/index.md
|
||||
- docs/advanced/ii/index.md
|
||||
- .planning/phases/10-docs-restructure/10-39-SUMMARY.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 36000
|
||||
raw_tokens: 24000
|
||||
tasks: 4
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "`docs/advanced/iii/index.md` 存在且只讲「这层是给谁的、前置、八篇地图、阅读路径、与 I/II 的边界、怎么验证」;不复述各系统细节(D-60 / D-82)"
|
||||
- "七篇正文在「## 八篇地图」表里全部有真实相对链接(`./loading-system.md` 等),导览自身不自我链接;每篇给一句「它能让你做什么」;前置链接真实指向 `../i/` 与 `../ii/`"
|
||||
- "七个内容页末尾都有与 I / II 一致格式的返回导览:`返回[高级 III 导览](./index.md)。`;正文中此前以书名号纯文本提到的兄弟页,改为真实相对链接(引用具体章节时带 `#标题`,D-74)"
|
||||
- "`config.ts` 的「高级 III · 原理与架构」分组注册 8 个目标(导览 + 七篇正文),顺序与导览地图一致,`collapsed` 改为 `false`;I 组仍 9 项、II 组仍 10 项、`nav` 仍指向 `/advanced/`"
|
||||
- "三处占位全部替换为真实链接:总导览 `docs/advanced/index.md` 的 III 行 → `./iii/`;`docs/advanced/i/index.md` 的「暂不提供页面入口」段 → `../iii/`;`docs/advanced/ii/index.md` 的「高级 III 尚未编写」段 → `../iii/`"
|
||||
- "任一计划边界处无死链:config 内每个 `/advanced/...` 目标都能映射到实际文件或目录 index;`docs/advanced/**` 内每个相对 `.md` / 目录链接目标存在;构建输出无 `dead link`"
|
||||
- "八页仍是 kebab-case、CRLF、单 H1;prettier `--check` 通过;构建 green、`build complete`"
|
||||
- "不改 I / II 的正文语义(仅占位段与链接)、不改其它站点配置、不改源码;越界问题只记录(D-14)"
|
||||
artifacts:
|
||||
- "docs/advanced/iii/index.md"
|
||||
- "docs/.vitepress/config.ts(III 组 8 项)"
|
||||
- "docs/advanced/index.md(III 行链接)"
|
||||
- "docs/advanced/i/index.md(III 链接)"
|
||||
- "docs/advanced/ii/index.md(III 链接)"
|
||||
- ".planning/phases/10-docs-restructure/10-39-SUMMARY.md"
|
||||
key_links:
|
||||
- "本计划是「III 从草稿变成站点可见页面」的唯一入口:注册、导览、返回链接、兄弟页互链都在这里一次性收口"
|
||||
- "八篇内容由 10-34..10-38 产出(按构建目录串行,不是并行),彼时彼此之间只能写纯文本页名;互链必须在这里补齐,否则读者在页面之间无法跳转"
|
||||
- "注册顺序必须与导览地图一致:模型线建议 core-state → plugin-internals → event-system → anon-tokyo → passability-and-pathfinding → replay-internals → loading-system,导览与侧边栏使用同一顺序"
|
||||
---
|
||||
|
||||
<objective>
|
||||
把高级 III 从「七篇散落的草稿」变成站点上可浏览的一层:新建高级 III 导览,给七篇补返回导览与兄弟页互链,在 `config.ts` 注册「高级 III · 原理与架构」分组,替换总导览、高级 I 导览、高级 II 导览里的三处占位,最后跑通格式与全量构建。
|
||||
|
||||
Purpose: 10-34..10-38 为了让每个计划边界都不出现死链,刻意没有互相链接、也没有返回导览。这一步是把这些链接一次性补齐的唯一时机——页面齐备之后,任何一处遗漏都会变成读者卡在页面里出不去,或者侧边栏分组永远为空。
|
||||
Output: 高级 III 导览、八项侧边栏、三处占位替换、七篇互链与 `10-39-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-ADVANCED-III-RESEARCH.md
|
||||
@docs/advanced/index.md
|
||||
@docs/advanced/i/index.md
|
||||
@docs/advanced/ii/index.md
|
||||
@docs/advanced/iii/loading-system.md
|
||||
@docs/advanced/iii/replay-internals.md
|
||||
@docs/advanced/iii/event-system.md
|
||||
@docs/advanced/iii/anon-tokyo.md
|
||||
@docs/advanced/iii/passability-and-pathfinding.md
|
||||
@docs/advanced/iii/core-state.md
|
||||
@docs/advanced/iii/plugin-internals.md
|
||||
@docs/.vitepress/config.ts
|
||||
|
||||
**现状:**
|
||||
- `docs/advanced/iii/` 现有七篇正文,均由 10-34..10-38 产出;没有 `index.md`,没有返回导览,兄弟页之间只有纯文本页名。
|
||||
- `config.ts` 的 III 组为 `{ text: '高级 III · 原理与架构', collapsed: true, items: [] }`。
|
||||
- 总导览 `docs/advanced/index.md` 的 III 行只有层名与主题、没有链接;`i/index.md` 写「高级 III 仍用占位标记…暂不提供页面入口」;`ii/index.md` 写「高级 III 尚未编写…不会写成真实链接;等该层建立后再替换」。
|
||||
|
||||
**链接写法:**
|
||||
- 侧边栏:绝对路径、不带 `.md`、不带尾斜杠(`{ text: '加载系统', link: '/advanced/iii/loading-system' }`);目录入口用 `/advanced/iii/`。
|
||||
- 正文:相对路径带 `.md`;引用具体章节带 `#标题`(D-74)。
|
||||
- 加粗遵守 flanking(D-52);命令一律经 `cmd /c "..."` 执行。
|
||||
- 不改 I / II 的正文语义;只替换占位段与必要的链接文本。research 行号可能漂移,事实以当前文件为准。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报注册与收口计划并等待「可以执行」</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-CONTEXT.md(D-12、D-13、D-60、D-61、D-74)、.planning/phases/10-docs-restructure/10-ADVANCED-III-RESEARCH.md(Page Split、iii/index.md 事实、Pitfall 6)</read_first>
|
||||
<action>向用户汇报四段并等待批准(D-12):**要做什么**——新建 `docs/advanced/iii/index.md`(导览),给七篇补返回导览与兄弟页互链,在 `config.ts` 注册 III 组八项,替换总导览 / 高级 I 导览 / 高级 II 导览里的三处占位,最后跑格式与全量构建。**为什么**——前五个计划为保证「每一步都无死链」刻意没有互链与入口;页面齐备后必须一次性收口,否则侧边栏分组为空、读者在页面之间无法跳转。**大致怎么做**——Task 1 建导览并给七篇补返回与互链;Task 2 注册侧边栏并替换三处占位;Task 3 跑 prettier、全量构建并写 SUMMARY。**影响范围**——新增 1 个导览、修改 7 篇的链接与结尾、`config.ts` 的 III 组、三个导览页的占位段;不动 I / II 正文语义、不动源码。汇报中明确:侧边栏八项显示名与顺序(导览 → 加载系统 → 录像系统底层 → 事件系统 → Anon-Tokyo 解释器 → 通行性谓词与寻路原理 → CoreState 组装与初始化 → 插件内部机制)需用户确认(D-13);III 组 `collapsed` 由 `true` 改为 `false`;III 行/占位段的替换文案以「已可阅读 + 真实链接」为原则,不写「后续再补」式元叙述(D-16)。汇报中说明本计划的修改方式为**派发子代理执行**,范围严格限于 `files_modified` 与 SUMMARY。停下等待用户明确回复「可以执行」四个字;未收到前不得建文件、不得改配置、不得派发子代理。</action>
|
||||
<what-built>本计划内容:高级 III 导览、八项侧边栏注册、三处占位替换与八页互链收口。</what-built>
|
||||
<how-to-verify>确认八项显示名与顺序、III 组改为展开,以及三处占位的替换文案;如有替代名称或顺序请给出。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<acceptance_criteria>用户已回复「可以执行」;若对侧边栏显示名、顺序或 `collapsed` 取值提出替代,已按用户意见修正计划。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已回复「可以执行」,且未对八项显示名、顺序或展开状态提出替代。</human-check>
|
||||
</verify>
|
||||
<done>用户回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 新建高级 III 导览并给七篇补返回与互链</name>
|
||||
<files>docs/advanced/iii/index.md, docs/advanced/iii/loading-system.md, docs/advanced/iii/replay-internals.md, docs/advanced/iii/event-system.md, docs/advanced/iii/anon-tokyo.md, docs/advanced/iii/passability-and-pathfinding.md, docs/advanced/iii/core-state.md, docs/advanced/iii/plugin-internals.md</files>
|
||||
<read_first>docs/advanced/ii/index.md(导览体例:给谁、定位、前置、包含什么、阅读路径、不覆盖什么、验证)、docs/advanced/iii/*.md 的标题与小节(确认每篇讲什么、哪些地方是纯文本提到的兄弟页)、docs/advanced/i/index.md</read_first>
|
||||
<action>(1) 用 Write 新建 `docs/advanced/iii/index.md`(CRLF、单 H1),结构:
|
||||
- `# 高级 III 导览` + 引言:高级 III 是最高难度层,讲数据端系统的原理与架构;读者应是有高度定制需求、能独立读懂引擎大部分数据端系统的作者。
|
||||
- `## 高级 III 是给谁的`:3–4 条读者画像(能读懂类 / 接口 / 泛型 / 装饰器 / TypedArray、需要判断改动落在哪一层、需要排查存档与回放不一致)。
|
||||
- `## 与高级 I、高级 II 的区别`:I 讲扩展机制怎么用,II 讲具体系统怎么改到自己的需求,III 讲这些系统为什么这样设计、数据怎么流;三层是难度标签不是领域分类。
|
||||
- `## 前置`:链接 `../i/`(导览)与 `../ii/`(导览),并点名建议先读的几篇(用真实链接,按文件名:`../i/plugin-system.md`、`../i/saveable-content.md`、`../i/replay-consistency.md`、`../ii/pathfinding.md`)。
|
||||
- `## 八篇地图`:表格「序 / 篇目 / 它能让你做什么」,**七行正文全部用真实相对链接**:`./core-state.md`、`./plugin-internals.md`、`./event-system.md`、`./anon-tokyo.md`、`./passability-and-pathfinding.md`、`./replay-internals.md`、`./loading-system.md`;表格后补一句「本页是第八篇,负责导览」。地图中的顺序与侧边栏一致。
|
||||
- `## 阅读路径`:两条——原理线(装配 → 插件 → 事件 → 脚本执行 → 通行性与寻路 → 录像 → 加载)与按需线(先读 CoreState 装配,再跳目标系统);说明每篇都单独成立。
|
||||
- `## 本层不覆盖什么`:渲染端结构性内容、编辑器与内容配置、序列化事件注册与地图事件 id 绑定、API 签名清单、legacy 包。
|
||||
- `## 怎么验证你读懂了`:5–6 条自检(能说出装配顺序为什么固定、插件依赖写错会怎样、事件为什么按这个顺序执行、谓词为什么有两个实例、录像为什么能按步号读取、加载为什么分三段)。
|
||||
(2) 七篇末尾各补一行 `返回[高级 III 导览](./index.md)。`(若已有则不重复;格式与 I / II 一致,句末句号)。
|
||||
(3) 把七篇正文中**已经提到兄弟页**的地方改成真实链接:按内容判断,例如事件页提到的脚本执行 → `./anon-tokyo.md`;脚本页提到的收集与执行 → `./event-system.md#收集这一次该执行谁`(该页标题固定为 `## 收集:这一次该执行谁`,锚点按去标点后的标题书写;不要写成 `#收集`);寻路页提到的装配 → `./core-state.md`;装配页提到的插件排序 → `./plugin-internals.md`。**只把已有的纯文本提及改成链接,不新增论述**(D-60);引用具体章节时带 `#标题`(D-74)。若某页通篇没有提到兄弟页,只允许补**一句**最短衔接并链接最相关的一页:加载系统 → `./core-state.md`(加载由主对象的 `load` 管线驱动);录像系统底层 → `./core-state.md`(录像存档对象由主插件注册)。每篇至少一处兄弟页链接(`./index.md` 之外),全层合计不少于 5 处。
|
||||
(4) 自检:八页仍是单 H1、CRLF、kebab-case;导览内所有链接可解析;无 `src/client`;无「模板」「[需改]」;无「后续再补」式元叙述。
|
||||
(5) 跑 `node node_modules/prettier/bin/prettier.cjs --write docs/advanced/iii` 覆盖八页。Commit:`docs(10-39): 新增高级 III 导览并补齐互链`。</action>
|
||||
<acceptance_criteria>导览存在且六个小节齐备、八篇地图全部有真实链接;七篇都有返回导览;七篇至少有 5 处兄弟页链接总计且每篇至少一处(除返回导览);所有相对链接可解析;CRLF、单 H1。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $idx='docs/advanced/iii/index.md'; if(-not(Test-Path -LiteralPath $idx)){ Write-Error "MISSING $idx" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $idx)); if($raw -match "(?<!`r)`n"){ Write-Error 'LF-ONLY-INDEX' }; if(([regex]::Matches($raw,'(?m)^# ')).Count -ne 1){ Write-Error 'H1-INDEX' }; foreach($h in @('## 高级 III 是给谁的','## 与高级 I、高级 II 的区别','## 前置','## 八篇地图','## 阅读路径','## 本层不覆盖什么','## 怎么验证')){ if($raw -notmatch [regex]::Escape($h)){ Write-Error "NO-SECTION $h" } }; $pages=@('core-state','plugin-internals','event-system','anon-tokyo','passability-and-pathfinding','replay-internals','loading-system'); foreach($n in $pages){ if($raw -notmatch [regex]::Escape("./$n.md")){ Write-Error "INDEX-MISSING-LINK $n" } }; if($raw -notmatch '\.\./i/'){ Write-Error 'NO-I-PREREQ' }; if($raw -notmatch '\.\./ii/'){ Write-Error 'NO-II-PREREQ' }; $cross=0; foreach($n in $pages){ $p="docs/advanced/iii/$n.md"; $body=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $p)); if($body -match "(?<!`r)`n"){ Write-Error "LF-ONLY $n" }; if(([regex]::Matches($body,'(?m)^# ')).Count -ne 1){ Write-Error "H1 $n" }; if($body -notmatch [regex]::Escape('返回[高级 III 导览](./index.md)')){ Write-Error "NO-BACKLINK $n" }; $others=0; foreach($o in $pages){ if($o -ne $n -and $body -match [regex]::Escape("./$o.md")){ $others++ } }; if($others -lt 1){ Write-Error "NO-SIBLING-LINK $n" }; $cross=$cross+$others; foreach($m in [regex]::Matches($body,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent (Resolve-Path -LiteralPath $p)) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error "DEAD-LINK $n" } } if($body -match 'event-system\.md#收集\)'){ Write-Error "BAD-ANCHOR $n" } }; $ev=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/advanced/iii/event-system.md')); if($ev -notmatch '(?m)^## 收集:这一次该执行谁\r?$'){ Write-Error 'EVENT-HEADING-CHANGED' }; if($cross -lt 5){ Write-Error ("CROSS-LINK-TOTAL " + $cross) }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path 'docs/advanced/iii' $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error 'DEAD-LINK-INDEX' } }; Write-Output 'INDEX-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1", "NO-SECTION", "INDEX-MISSING-LINK", "NO-I-PREREQ", "NO-II-PREREQ", "NO-BACKLINK", "NO-SIBLING-LINK", "CROSS-LINK-TOTAL", "BAD-ANCHOR", "EVENT-HEADING-CHANGED", or "DEAD-LINK"; or output lacks "INDEX-OK"</fails_when>
|
||||
</verify>
|
||||
<done>导览成立且八篇地图全链;七篇都有返回导览与至少一处兄弟页链接;全部链接可解析。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 注册 III 侧边栏并替换三处占位</name>
|
||||
<files>docs/.vitepress/config.ts, docs/advanced/index.md, docs/advanced/i/index.md, docs/advanced/ii/index.md</files>
|
||||
<read_first>docs/.vitepress/config.ts:154-240、docs/advanced/index.md(三层表与边界段)、docs/advanced/i/index.md:17-27,73-77、docs/advanced/ii/index.md:17-29</read_first>
|
||||
<action>(1) 改 `config.ts` 的 III 组:把 `items: []` 换成八项,`collapsed` 改为 `false`。顺序与显示名:`高级 III 导览` → `/advanced/iii/`;`加载系统` → `/advanced/iii/loading-system`;`录像系统底层` → `/advanced/iii/replay-internals`;`事件系统` → `/advanced/iii/event-system`;`Anon-Tokyo 解释器` → `/advanced/iii/anon-tokyo`;`通行性谓词与寻路原理` → `/advanced/iii/passability-and-pathfinding`;`CoreState 组装与初始化` → `/advanced/iii/core-state`;`插件内部机制` → `/advanced/iii/plugin-internals`。保持现有缩进与对象风格;不改 I 组(9 项)、II 组(10 项)、`nav`、`outDir` / `base` / 搜索 / mermaid。
|
||||
(2) 改 `docs/advanced/index.md`:三层表里 III 行由纯文本改为 `[高级 III · 原理与架构](./iii/)`;若「本层不覆盖什么」或其它段落提到 III「暂不提供入口」,一并改成「已可阅读,入口见[高级 III 导览](./iii/)」;不新增论述。
|
||||
(3) 改 `docs/advanced/i/index.md`:把「高级 III 仍用 `高级 III · 专题名` 这样的占位标记指出归属,暂不提供页面入口」改为「高级 III 已经可阅读,入口见[高级 III 导览](../iii/)」;该页「本层不覆盖什么」里提到 III 的那条同步加上链接;其余文字不动。
|
||||
(4) 改 `docs/advanced/ii/index.md`:把「高级 III 尚未编写。本层正文只在需要时用…不会写成真实链接;等该层建立后再替换」改为「高级 III 已经可阅读,入口见[高级 III 导览](../iii/)」;其余文字不动。
|
||||
(5) 用 `git diff` 自查:`config.ts` 只应新增 III 组八项与 `collapsed` 取值变化;三个导览页只应改占位相关句子。
|
||||
(6) 跑 `node node_modules/prettier/bin/prettier.cjs --write docs/.vitepress/config.ts docs/advanced/index.md docs/advanced/i/index.md docs/advanced/ii/index.md`。Commit:`docs(10-39): 注册高级 III 侧边栏并替换占位`。</action>
|
||||
<acceptance_criteria>III 组恰有 8 个 `/advanced/iii/` 目标且顺序正确、`collapsed: false`;I 组 9 项与 II 组 10 项未变、nav 未变;三处占位全部替换为真实链接且不再出现「尚未编写」「暂不提供页面入口」。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $cfg=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/.vitepress/config.ts')); $iii=[regex]::Matches($cfg,"link:\s*'(/advanced/iii/[^']*)'") | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique; if($iii.Count -ne 8){ Write-Error ("III-LINK-COUNT " + $iii.Count) }; foreach($l in $iii){ $rel=$l.TrimEnd('/'); $p1=("docs/" + $rel + ".md"); $p2=("docs/" + $rel + "/index.md"); if(-not(Test-Path -LiteralPath $p1) -and -not(Test-Path -LiteralPath $p2)){ Write-Error "III-CFG-DEAD $l" } }; foreach($n in @('loading-system','replay-internals','event-system','anon-tokyo','passability-and-pathfinding','core-state','plugin-internals')){ if($iii -notcontains "/advanced/iii/$n"){ Write-Error "III-MISSING $n" } }; if($iii -notcontains '/advanced/iii/'){ Write-Error 'III-NO-INDEX' }; if(([regex]::Matches($cfg,"link:\s*'/advanced/i/[^']*'")).Count -ne 9){ Write-Error 'I-LINKS-CHANGED' }; if(([regex]::Matches($cfg,"link:\s*'/advanced/ii/[^']*'")).Count -ne 10){ Write-Error 'II-LINKS-CHANGED' }; if($cfg -notmatch "text:\s*'高级 III · 原理与架构'"){ Write-Error 'NO-III-GROUP' }; if($cfg -notmatch "link:\s*'/advanced/'"){ Write-Error 'NAV-OR-ROOT-CHANGED' }; $root=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/advanced/index.md')); if($root -notmatch '\]\(\./iii/\)'){ Write-Error 'ROOT-NO-III-LINK' }; $i=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/advanced/i/index.md')); if($i -notmatch '\]\(\.\./iii/\)'){ Write-Error 'I-NO-III-LINK' }; if($i -match '暂不提供页面入口'){ Write-Error 'I-STALE-PLACEHOLDER' }; $ii=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/advanced/ii/index.md')); if($ii -notmatch '\]\(\.\./iii/\)'){ Write-Error 'II-NO-III-LINK' }; if($ii -match '尚未编写'){ Write-Error 'II-STALE-PLACEHOLDER' }; Write-Output 'REGISTER-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "III-LINK-COUNT", "III-CFG-DEAD", "III-MISSING", "III-NO-INDEX", "I-LINKS-CHANGED", "II-LINKS-CHANGED", "NO-III-GROUP", "NAV-OR-ROOT-CHANGED", "ROOT-NO-III-LINK", "I-NO-III-LINK", "I-STALE-PLACEHOLDER", "II-NO-III-LINK", or "II-STALE-PLACEHOLDER"; or output lacks "REGISTER-OK"</fails_when>
|
||||
</verify>
|
||||
<done>III 组 8 项注册完成且目标存在;三处占位替换为真实链接;I / II 链接数与 nav 未变。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: 全量格式与构建门禁、写 SUMMARY</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-39-SUMMARY.md</files>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-38-SUMMARY.md(SUMMARY 体例)</read_first>
|
||||
<action>(1) 格式:`node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii docs/advanced/index.md docs/advanced/i/index.md docs/advanced/ii/index.md docs/.vitepress/config.ts`,通过。(2) 构建:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,退出码 0、含 `build complete`、无 `dead link`。(3) 人工点检:侧边栏「高级 III · 原理与架构」展开后八项齐全、顺序与导览地图一致;从高级 I 导览、高级 II 导览、总导览都能一键进入 III;八篇页末尾都能回到导览。(4) 写 `.planning/phases/10-docs-restructure/10-39-SUMMARY.md`:八项注册清单与顺序、导览结构、七篇补的返回链接与兄弟页链接清单(逐条列出,便于 10-40 审阅)、三处占位替换的具体文案、prettier 与构建结论、遗留事项(留给 10-40 的审阅范围)。Commit:`docs(10-39): 高级 III 注册与构建收口`。</action>
|
||||
<acceptance_criteria>prettier `--check` 通过;构建退出码 0、含 `build complete`、无 `dead link`;SUMMARY 逐条记录互链与占位替换,能直接支撑 10-40 的审阅。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $sum='.planning/phases/10-docs-restructure/10-39-SUMMARY.md'; if(-not(Test-Path -LiteralPath $sum)){ Write-Error "MISSING $sum" }; $s=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $sum)); foreach($needle in @('高级 III','八项','导览','占位','构建')){ if($s -notmatch [regex]::Escape($needle)){ Write-Error "SUMMARY-MISSING $needle" } }; $files=Get-ChildItem -LiteralPath 'docs/advanced' -Recurse -File -Filter '*.md'; foreach($f in $files){ $body=[IO.File]::ReadAllText($f.FullName); if($body -match "(?<!`r)`n"){ Write-Error "LF-ONLY $($f.Name)" }; if(([regex]::Matches($body,'(?m)^# ')).Count -ne 1){ Write-Error "H1 $($f.Name)" } }; $ErrorActionPreference='Continue'; $pf = cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii docs/advanced/index.md docs/advanced/i/index.md docs/advanced/ii/index.md docs/.vitepress/config.ts" 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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'GATE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "SUMMARY-MISSING", "LF-ONLY", "H1", "PRETTIER-FAIL", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "GATE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>全层格式与构建双绿;SUMMARY 完整列出注册、互链与占位替换。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 配置文件 → 站点结构 | III 组决定八篇能否被浏览 |
|
||||
| 构建期 markdown → 静态站点 | 相对链接在构建期解析 |
|
||||
| 读者 → 站点 | 站内链接是唯一可点击指向面 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-115 | Tampering | III 组链接集合 | medium | mitigate | 断言恰好 8 个 `/advanced/iii/...` 目标且逐个映射到实际文件 |
|
||||
| T-10-116 | Spoofing | 三处占位替换 | low | mitigate | 断言替换后不再出现「尚未编写」「暂不提供页面入口」,且三个导览都有真实链接 |
|
||||
| T-10-117 | Tampering / XSS | 导览与七篇正文 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 注册门禁:III 组 8 项、顺序与导览一致、`collapsed: false`;I 组 9 项、II 组 10 项、nav 未变。
|
||||
- 链接门禁:`docs/advanced/**` 每个相对 `.md` / 目录链接目标存在;config 内每个 `/advanced/...` 目标存在。
|
||||
- 导览门禁:六个小节齐备、八篇地图全链、每篇有返回导览与至少一处兄弟页链接。
|
||||
- 占位门禁:三处占位全部替换,无「尚未编写」「暂不提供页面入口」残留。
|
||||
- 格式 / 构建门禁:prettier `--check` 通过;`build complete` 且无 `dead link`。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 高级 III 在站点上成为可浏览的一层:侧边栏、导览、返回链接、兄弟页互链全部生效。
|
||||
- 三处旧占位不再误导读者。
|
||||
- 构建与格式双绿,无越界改动。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-39-SUMMARY.md` when done
|
||||
</output>
|
||||
181
.planning/phases/10-docs-restructure/10-40-PLAN.md
Normal file
181
.planning/phases/10-docs-restructure/10-40-PLAN.md
Normal file
@ -0,0 +1,181 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 40
|
||||
type: execute
|
||||
wave: 12
|
||||
depends_on: [10-39]
|
||||
files_modified:
|
||||
- .planning/phases/10-docs-restructure/10-40-READER-REVIEW.md
|
||||
- docs/advanced/iii/index.md
|
||||
- docs/advanced/iii/loading-system.md
|
||||
- docs/advanced/iii/replay-internals.md
|
||||
- docs/advanced/iii/event-system.md
|
||||
- docs/advanced/iii/anon-tokyo.md
|
||||
- docs/advanced/iii/passability-and-pathfinding.md
|
||||
- docs/advanced/iii/core-state.md
|
||||
- docs/advanced/iii/plugin-internals.md
|
||||
- .planning/phases/10-docs-restructure/10-40-SUMMARY.md
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 36000
|
||||
raw_tokens: 24000
|
||||
tasks: 4
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "读者人格与高级 III 受众匹配,并在计划中逐字固定:有高度定制需求、具备足够编码能力、能独立读懂引擎大部分数据端系统、但不是引擎编写者、也没通读过全部源码(相比高级 I / II 提高标准,D-57)"
|
||||
- "`10-40-READER-REVIEW.md` 逐字记录人格提示词、审阅范围(八页:导览 + 七篇正文)、首轮问题清单,并标注「未复用中级 / 高级 I / 高级 II 人格」"
|
||||
- "审阅必须逐章给出「被使用的概率」(高 / 中 / 低 + 理由,D-78),使修订者知道哪些章节重要、哪些可删或后置;这条只进审阅记录,**不得**写入文档正文(D-79)"
|
||||
- "阻断性问题被迭代修正(上限 3 轮);每轮后构建 green、`build complete`、dead link = 0;达到上限仍有阻断问题时停下交用户,不无限循环"
|
||||
- "迭代结束再派一个同人格独立子代理复审,逐条标注「已消除」或「仍存在」,并以「阻断性问题 = 0」或「阻断性问题余项:已交用户」结束"
|
||||
- "不修改用户的确认状态(本计划不产生也不改写任何「用户最终确认」标记);不收尾、不写「已完成」结论——那是 10-41 的职责"
|
||||
- "只改八页中确有阻断问题或明显重心错误的段落与审阅记录;不改 `config.ts`、不改 I / II 页面、不改源码;不因读者提问删掉已锁定要覆盖的内容(D-14)"
|
||||
- "结束时八页仍是 kebab-case、CRLF、单 H1;prettier `--check` 通过;构建 green、`build complete`、无 `dead link`"
|
||||
artifacts:
|
||||
- ".planning/phases/10-docs-restructure/10-40-READER-REVIEW.md"
|
||||
- "docs/advanced/iii/*.md(仅存在阻断问题的部分)"
|
||||
- ".planning/phases/10-docs-restructure/10-40-SUMMARY.md"
|
||||
key_links:
|
||||
- "D-46 第 1 阶段 + D-57 + D-78:本层读者标准高于 I / II,审阅必须给章节使用概率,否则无法判断该删什么"
|
||||
- "审阅对象是 10-39 已接入站点、互链完整的八页,而不是散落草稿"
|
||||
- "本计划不替用户收尾;10-41 才进入用户主导修订循环并要求明确确认"
|
||||
---
|
||||
|
||||
<objective>
|
||||
对高级 III 的八页做独立读者审阅:派一个与执行者不共享上下文的子代理,用「有能力读懂大部分数据端系统、但不是引擎编写者」的读者人格通读导览与七篇正文,逐章给出被使用的概率与理由,产出问题清单;据此修正阻断性问题,最后同人格复审直到阻断清零或把余项交给用户。
|
||||
|
||||
Purpose: 这一层最容易出现的不是「代码放哪」而是「模型讲错了一层」「两个实例被当成一个」「内部实现被写成源码导读」。这类问题作者自查看不出来,必须由目标读者视角发现;而「哪些章节其实没人会用」只有读者能判断,所以必须带使用概率。
|
||||
Output: `10-40-READER-REVIEW.md`(人格、概率表、问题、迭代、复审)、八页的阻断问题修正、`10-40-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-ADVANCED-III-RESEARCH.md
|
||||
@.planning/phases/10-docs-restructure/10-39-SUMMARY.md
|
||||
@.planning/phases/10-docs-restructure/10-31-READER-REVIEW.md
|
||||
@docs/advanced/iii/index.md
|
||||
@docs/advanced/iii/loading-system.md
|
||||
@docs/advanced/iii/replay-internals.md
|
||||
@docs/advanced/iii/event-system.md
|
||||
@docs/advanced/iii/anon-tokyo.md
|
||||
@docs/advanced/iii/passability-and-pathfinding.md
|
||||
@docs/advanced/iii/core-state.md
|
||||
@docs/advanced/iii/plugin-internals.md
|
||||
|
||||
**人格设定(D-57,高级 III 用更高标准,逐字使用、不得改写):**
|
||||
> 假设你是一位有高度定制需求的数据端作者,已经读完高级 I 与高级 II,具备足够的编码能力:能独立读懂引擎的大部分数据端系统(会读类、接口、泛型、装饰器与 TypedArray 操作),但你既不是引擎的编写者,也没有通读过全部源码。你正在阅读高级 III「原理与架构」,想判断这些原理能否帮你把现有系统改对。
|
||||
|
||||
**与高级 I / II 审阅的区别:**
|
||||
- 高级 I 人格见 `10-31-READER-REVIEW.md` 与 `10-19-READER-REVIEW.md`(了解基本语法、代码能力一般);高级 II 人格见 `10-26-READER-REVIEW.md`。本计划**禁止复用**其中任何一个人格提示词。
|
||||
- 本层读者能读装饰器与 TypedArray,因此问题应落在「模型是否讲对」「两个对象是不是同一个」「内部实现讲到哪一层才够」「这一段原理对改系统有什么用」,而不是「这段代码放在哪」。
|
||||
- 审阅对象是 10-39 接入站点后的八页(导览 + 七篇);不要求重审高级 I / II。
|
||||
|
||||
**流程约定:**
|
||||
- 迭代上限 **3 轮**;每轮后跑构建门禁;达到上限仍有阻断问题时停下向用户汇报。
|
||||
- 只改八页中确有阻断问题或重心明显错误的段落;不改 `config.ts`、不改 I / II、不改源码。
|
||||
- 命令一律经 `cmd /c "..."` 执行(本机 PowerShell 禁止 `pnpm.ps1`)。
|
||||
- 阻断性判定口径:读者据此**会把系统模型理解错**(例如把两个谓词实例当成一个、把加载阶段与插件时机混为一谈、把旧引擎写法当成当前接口),或**照着做会改错位置**。
|
||||
- 非阻断问题只记录,不修(D-14),留给 10-41 的用户循环。
|
||||
- 使用概率只进审阅记录,**不得**出现在文档正文(D-79)。
|
||||
- 审阅记录里的人格提示词可以写成引用块(每行带 `>`)或按行折行;门禁会先去掉 `>` 与所有空白再做逐字比对,因此只要文字逐字一致即可,不必强行单行。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报审阅计划并确认人格与范围</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-CONTEXT.md(D-12、D-46、D-57、D-77、D-78、D-79)、.planning/phases/10-docs-restructure/10-31-READER-REVIEW.md(高级 I 人格,用于说明差异)</read_first>
|
||||
<action>向用户汇报四段并等待批准(D-12):**要做什么**——派一个独立子代理,用「有高度定制需求、具备足够编码能力、能独立读懂引擎大部分数据端系统但不是引擎编写者」的人格通读八页(导览 + 七篇),逐章给使用概率(高 / 中 / 低 + 理由),产出阻断 / 非阻断问题清单;据此迭代修正阻断问题并同人格复审。**为什么**——D-46 第 1 阶段要求子代理自审,D-78 要求给出章节使用概率;本层最容易错的是系统模型与实例归属,作者视角看不出来。**大致怎么做**——Task 1 派子代理并落清单(不改文档);Task 2 按清单修阻断问题,最多 3 轮、每轮构建;Task 3 复审确认阻断清零并写 SUMMARY。**影响范围**——可能修改八页中确有阻断问题的段落 + 新增 `.planning` 下的审阅记录与 SUMMARY;不改配置、I / II 页面与源码。请用户确认:人格提示词原文(逐字照用,D-57 明确要求人格在计划中写明并经用户确认)、审阅范围仅八页、迭代上限 3 轮;如希望扩大到高级 I / II 或调整人格,请直接说明。汇报中明确:**本计划不产生任何「用户最终确认」标记、不收尾**,用户主导修订与最终确认在 10-41;使用概率只写审阅记录,不写进正文(D-79)。汇报中说明本计划的修改方式为**派发子代理执行**。停下等待用户明确回复「可以执行」四个字;未收到前不得派发子代理、不得改任何文件。</action>
|
||||
<what-built>本计划内容:高级 III 八页的独立读者审阅(含章节使用概率)→ 阻断问题迭代 → 同人格复审。</what-built>
|
||||
<how-to-verify>阅读人格提示词与流程约定,确认人格、范围仅八页、迭代上限 3 轮,且本计划不收尾;如希望调整人格或扩大范围请直接说明。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<acceptance_criteria>用户已确认人格提示词原文、范围与 3 轮上限,并回复「可以执行」;若用户要求修改人格或扩大范围,已按用户意见更新计划与逐字人格。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已确认人格与范围,并回复「可以执行」。</human-check>
|
||||
</verify>
|
||||
<done>用户确认人格与范围并回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 派发独立读者子代理并产出概率表与问题清单</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-40-READER-REVIEW.md</files>
|
||||
<action>派发一个**独立子代理**(不与执行者共享上下文),任务设定逐字为上文「人格设定」段落,并追加要求:「请按页、按小节阅读,输出:(1) 每页每节被使用的概率(高 / 中 / 低)与一句理由;(2) 哪些地方你会把系统模型理解错、把两个对象当成一个、把旧实现当成当前接口、或照着做会改错位置;(3) 哪些章节读完觉得没用、可以删或后置。」子代理可以打开被引用的源码核对,但**不假定它已通读引擎**;它**只提问、不修改文件**。把结果写入 `.planning/phases/10-docs-restructure/10-40-READER-REVIEW.md`:开头逐字记录人格提示词与八页范围,单独一行标注「未复用中级 / 高级 I / 高级 II 人格」;随后是逐页逐节的概率表(高 / 中 / 低 + 理由)、按**阻断性**与**非阻断**分组的问题清单(每条附文件与章节)。**本轮不改文档**。Commit:`docs(10-40): 记录高级 III 读者审阅清单`。</action>
|
||||
<acceptance_criteria>记录存在;逐字含人格提示词与八页范围;含「未复用」标记;含逐页概率(高/中/低)与问题清单;问题按阻断/非阻断分组并定位到文件章节。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $f='.planning/phases/10-docs-restructure/10-40-READER-REVIEW.md'; if(-not(Test-Path -LiteralPath $f)){ Write-Error "MISSING $f" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $f)); foreach($needle in @('有高度定制需求','具备足够的编码能力','能独立读懂','不是引擎的编写者','使用概率','高','中','低','阻断','非阻断','未复用')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING-CONTENT $needle" } }; $flat=($raw -replace '>','') -replace '\s',''; $persona='假设你是一位有高度定制需求的数据端作者,已经读完高级I与高级II,具备足够的编码能力:能独立读懂引擎的大部分数据端系统(会读类、接口、泛型、装饰器与TypedArray操作),但你既不是引擎的编写者,也没有通读过全部源码。你正在阅读高级III「原理与架构」,想判断这些原理能否帮你把现有系统改对。'; if(-not $flat.Contains($persona)){ Write-Error 'MISSING-PERSONA-PROMPT' }; foreach($n in @('index.md','loading-system.md','replay-internals.md','event-system.md','anon-tokyo.md','passability-and-pathfinding.md','core-state.md','plugin-internals.md')){ if($raw -notmatch [regex]::Escape("docs/advanced/iii/$n")){ Write-Error "MISSING-SCOPE $n" } }; Write-Output 'REVIEW-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "MISSING-CONTENT", "MISSING-PERSONA-PROMPT", or "MISSING-SCOPE"; or output lacks "REVIEW-OK"</fails_when>
|
||||
</verify>
|
||||
<done>审阅记录存在且含逐字人格、八页范围、未复用标记、逐页概率与分组问题清单。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 按清单迭代修正阻断问题(上限 3 轮)</name>
|
||||
<files>docs/advanced/iii/index.md, docs/advanced/iii/loading-system.md, docs/advanced/iii/replay-internals.md, docs/advanced/iii/event-system.md, docs/advanced/iii/anon-tokyo.md, docs/advanced/iii/passability-and-pathfinding.md, docs/advanced/iii/core-state.md, docs/advanced/iii/plugin-internals.md, .planning/phases/10-docs-restructure/10-40-READER-REVIEW.md</files>
|
||||
<action>逐个消除 `10-40-READER-REVIEW.md` 中的**阻断性问题**:典型修法包括——把「模型/组成/数据流」补在接口之前(D-82);把「同一契约、两个实例」写成读者能一眼分辨的对照;把内部实现章节的开头句与范围说清(D-69),删掉私有字段与源码叙述(D-80);把旧引擎写法明确标为基线;把读者「用不到但占了半页」的章节按概率后置或压缩(D-60,只压缩、不删除已锁定要覆盖的内容)。非阻断问题不修,只保留在清单(D-14)。**不得**把使用概率等元信息写进正文(D-79);**不得**改写或新增任何「用户最终确认」标记。每轮修正后:(a) 跑 `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,须 green、`build complete`、dead link = 0;(b) 在记录中追加本轮处理(修了哪些、依据、构建结论);(c) 回报本轮改动。迭代**最多 3 轮**;3 轮后仍有阻断问题时停下交用户。每轮 Commit:`docs(10-40): 迭代修正高级 III 阻断问题(第N轮)`。</action>
|
||||
<acceptance_criteria>阻断问题按最多 3 轮处理完毕;每轮记录与构建结论在案;正文无使用概率元信息、无用户确认标记;八页仍单 H1、CRLF、相对链接可解析。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $pages=@('index','loading-system','replay-internals','event-system','anon-tokyo','passability-and-pathfinding','core-state','plugin-internals'); foreach($n in $pages){ $p="docs/advanced/iii/$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 $p" }; if($raw -match '使用概率'){ Write-Error "META-IN-BODY $p" }; if($raw -match '用户最终确认'){ Write-Error "USER-CONFIRM-IN-BODY $p" }; foreach($m in [regex]::Matches($raw,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent (Resolve-Path -LiteralPath $p)) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error "DEAD-LINK $p" } } }; $f='.planning/phases/10-docs-restructure/10-40-READER-REVIEW.md'; $rev=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $f)); if($rev -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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'ITERATE-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "LF-ONLY", "H1", "META-IN-BODY", "USER-CONFIRM-IN-BODY", "DEAD-LINK", "NO-ROUND-LOG", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "ITERATE-OK"</fails_when>
|
||||
</verify>
|
||||
<done>阻断问题修正完毕(余项已交用户);每轮有记录;正文无元信息与确认标记;构建 green。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: 同人格复审确认阻断清零并写 SUMMARY</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-40-READER-REVIEW.md、八页当前内容、.planning/phases/10-docs-restructure/10-31-SUMMARY.md(复审与结束标记体例)</read_first>
|
||||
<files>.planning/phases/10-docs-restructure/10-40-READER-REVIEW.md, .planning/phases/10-docs-restructure/10-40-SUMMARY.md</files>
|
||||
<action>再次派发一个**独立子代理**(人格提示词逐字同 Task 1,不得换回高级 I / II 人格),重新通读八页并回答「现在还有哪些地方你会把系统模型理解错、把两个对象当成一个、把旧实现当成当前接口,或照着做会改错位置?另外,重读之后每节的使用概率是否要调整?」。把复审追加进记录:对照首轮清单逐条标注「已消除」或「仍存在」;若仍有阻断问题,回到 Task 2 再修(受 3 轮上限约束)。收敛时二选一写结构化结束标记:「阻断性问题 = 0,审阅通过」或「阻断性问题余项:已交用户」并列出余项与用户处置。最后跑一次构建门禁。写 `.planning/phases/10-docs-restructure/10-40-SUMMARY.md`:人格与范围、概率表摘要(哪些章节高、哪些低)、首轮问题数与分类、每轮修正摘要、复审逐条结论、结束标记、格式 / 构建 / 链接结论、非阻断残项(只记录,留给 10-41)。**明确写一句「本计划未做用户确认、未收尾」**。Commit:`docs(10-40): 高级 III 读者审阅收敛`。</action>
|
||||
<acceptance_criteria>复审逐条标注已消除/仍存在;结束标记为「阻断性问题 = 0,审阅通过」或「阻断性问题余项:已交用户」;SUMMARY 含人格、概率摘要、轮次、结论与「未收尾」声明。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $f='.planning/phases/10-docs-restructure/10-40-READER-REVIEW.md'; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $f)); foreach($needle in @('已消除','仍存在','未复用')){ if($raw -notmatch [regex]::Escape($needle)){ Write-Error "MISSING $needle" } }; if($raw -notmatch '阻断性问题 = 0' -and $raw -notmatch '阻断性问题余项:已交用户'){ Write-Error 'NO-TERMINAL-MARKER' }; $flat=($raw -replace '>','') -replace '\s',''; $persona='假设你是一位有高度定制需求的数据端作者,已经读完高级I与高级II,具备足够的编码能力:能独立读懂引擎的大部分数据端系统(会读类、接口、泛型、装饰器与TypedArray操作),但你既不是引擎的编写者,也没有通读过全部源码。你正在阅读高级III「原理与架构」,想判断这些原理能否帮你把现有系统改对。'; if(-not $flat.Contains($persona)){ Write-Error 'MISSING-PERSONA-PROMPT' }; $sum='.planning/phases/10-docs-restructure/10-40-SUMMARY.md'; if(-not(Test-Path -LiteralPath $sum)){ Write-Error "MISSING $sum" }; $s=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $sum)); foreach($needle in @('人格','概率','复审','构建','未')){ if($s -notmatch [regex]::Escape($needle)){ Write-Error "SUMMARY-MISSING $needle" } }; $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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'RECHECK-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "NO-TERMINAL-MARKER", "MISSING-PERSONA-PROMPT", "SUMMARY-MISSING", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "RECHECK-OK"</fails_when>
|
||||
</verify>
|
||||
<done>复审结论逐条在案;结束标记成立;SUMMARY 完整并声明未收尾;构建 green。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 执行者 → 子代理 | 子代理以独立上下文阅读八页并提问,不改文件 |
|
||||
| 构建期 markdown → 静态站点 | 正文静态渲染,无运行时输入 |
|
||||
| 审阅记录 → 文档正文 | 使用概率等元信息不得越界进入正文 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-118 | Spoofing | 人格与审阅记录 | medium | mitigate | 断言记录逐字含人格提示词(按空白归一化)与「未复用」标记 |
|
||||
| T-10-119 | Tampering | 正文混入元信息 / 确认标记 | low | mitigate | 断言八页不含「使用概率」「用户最终确认」字样 |
|
||||
| T-10-120 | Information Disclosure | 审阅记录 | low | mitigate | 只引用仓库已公开路径与接口 |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 人格门禁:记录逐字含高级 III 人格提示词;含「未复用中级 / 高级 I / 高级 II 人格」标记。
|
||||
- 概率门禁:逐页逐节给出高 / 中 / 低与理由;正文不含概率元信息。
|
||||
- 迭代门禁:上限 3 轮;结束标记为「阻断性问题 = 0,审阅通过」或「阻断性问题余项:已交用户」。
|
||||
- 结构门禁:八页单 H1、CRLF、相对链接可解析。
|
||||
- 构建门禁:每轮与收尾 `build complete` 且无 `dead link`。
|
||||
- 越界门禁:未改配置、I / II 页面与源码;未产生用户确认标记。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 八页按 D-46 第 1 阶段完成独立读者审阅:人格匹配本层、逐章给概率、问题清单、迭代、复审。
|
||||
- 阻断问题清零或余项明确交用户;没有替用户收尾。
|
||||
- 构建通过,页面结构未被审阅削掉锁定内容。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-40-SUMMARY.md` when done
|
||||
</output>
|
||||
203
.planning/phases/10-docs-restructure/10-41-PLAN.md
Normal file
203
.planning/phases/10-docs-restructure/10-41-PLAN.md
Normal file
@ -0,0 +1,203 @@
|
||||
---
|
||||
phase: 10-docs-restructure
|
||||
plan: 41
|
||||
type: execute
|
||||
wave: 13
|
||||
depends_on: [10-40]
|
||||
files_modified:
|
||||
- .planning/phases/10-docs-restructure/10-41-USER-REVIEW.md
|
||||
- docs/advanced/iii/index.md
|
||||
- docs/advanced/iii/loading-system.md
|
||||
- docs/advanced/iii/replay-internals.md
|
||||
- docs/advanced/iii/event-system.md
|
||||
- docs/advanced/iii/anon-tokyo.md
|
||||
- docs/advanced/iii/passability-and-pathfinding.md
|
||||
- docs/advanced/iii/core-state.md
|
||||
- docs/advanced/iii/plugin-internals.md
|
||||
- .planning/phases/10-docs-restructure/10-41-SUMMARY.md
|
||||
- .planning/ROADMAP.md
|
||||
- .planning/STATE.md
|
||||
- docs/.vitepress/config.ts
|
||||
autonomous: false
|
||||
requirements: []
|
||||
user_setup: []
|
||||
estimate:
|
||||
tokens: 32000
|
||||
raw_tokens: 21000
|
||||
tasks: 5
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "用户主导的修订循环被真正执行:用户以本领域视角逐条提出问题 → 派子代理修改 → 回报 → 用户继续提,直到用户明确确认满意(D-46 第 2 阶段 / D-57)"
|
||||
- "`10-41-USER-REVIEW.md` 记录每一轮用户意见、落点、处置与构建结论;最终确认写成**独占一行**的 `用户最终确认:通过` 或 `用户最终确认:满意`,用户原文放在**下一行**并以 `用户原文:` 开头(不追加在同一行)"
|
||||
- "收尾由用户确认触发,不由 AI 自行收尾;用户未确认前不得写 SUMMARY、不得改 ROADMAP / STATE、不得宣称完成"
|
||||
- "最终八页全部 kebab-case、CRLF、单 H1;高级 III 侧边栏仍 8 个目标;三处占位仍未回退成「尚未编写」;prettier `--check` 通过;构建 green、`build complete`、dead link = 0"
|
||||
- "`ROADMAP.md` 的 Phase 10 只做追加式更新:新增 10-33..10-41 九条勾选条目、把 Phase 10 的已完成 / 总数更新为 38/41、并在本批描述中补一句第五批内容;不删除、不重排既有条目,不用整文件重写"
|
||||
- "`STATE.md` 只做增量更新:只改「当前进度 / 最近活动 / Phase 10 的 Progress 行 / Roadmap Evolution」,**不改 frontmatter 的 `progress.total_plans` 与 `completed_plans`(它们是项目级累计值,不是本批口径)**;保留 10-06 / 10-11 / 10-12 仍待补的记录"
|
||||
- "不改 I / II 正文语义、不改源码;`config.ts` **只在用户明确要求显示名 / 顺序调整时**才动,其余情况不动;越界意见只记录、不实施(D-14)"
|
||||
artifacts:
|
||||
- ".planning/phases/10-docs-restructure/10-41-USER-REVIEW.md"
|
||||
- ".planning/phases/10-docs-restructure/10-41-SUMMARY.md"
|
||||
- ".planning/ROADMAP.md(追加第五批条目与批次说明)"
|
||||
- ".planning/STATE.md(增量记录第五批收尾;frontmatter 全局计数保持原值)"
|
||||
- "docs/.vitepress/config.ts(仅当用户要求调整 III 组显示名 / 顺序时)"
|
||||
key_links:
|
||||
- "执行 D-46 第 2 阶段 / D-57 的用户主导修订循环;这是本单元与整个高级 III 批次收尾的唯一合法出口"
|
||||
- "10-40 已消除独立读者人格的阻断问题;本计划处理用户以领域视角提出的问题"
|
||||
- "ROADMAP / STATE 的更新必须在用户确认之后,否则会把未验收的内容记成已完成(D-09 人工审阅)"
|
||||
---
|
||||
|
||||
<objective>
|
||||
执行高级 III 的用户主导修订循环并收尾:把接入站点后的八页交给用户,由用户以本领域视角逐条提出问题;AI 派子代理修改、回报;用户继续提,直到用户明确确认满意。只有确认后才做最终格式与构建收口、写 SUMMARY,并在 `ROADMAP.md` / `STATE.md` 里记录第五批收尾。
|
||||
|
||||
Purpose: D-46 明确要求子代理审阅之后必须再经用户主导的修订循环,且必须获用户确认才可收尾;D-09 的验收标准就是用户人工审阅。本计划是这条出口,也是把「高级 III 已上线」写回路线图与状态的唯一时机。
|
||||
Output: `10-41-USER-REVIEW.md`、最终八页、`10-41-SUMMARY.md`、ROADMAP / STATE 的第五批记录。
|
||||
</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-40-READER-REVIEW.md
|
||||
@.planning/phases/10-docs-restructure/10-32-USER-REVIEW.md
|
||||
@.planning/phases/10-docs-restructure/10-39-SUMMARY.md
|
||||
@docs/advanced/iii/index.md
|
||||
@docs/advanced/index.md
|
||||
@docs/.vitepress/config.ts
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
|
||||
**流程约定(D-46 第 2 阶段 / D-57):**
|
||||
- 用户主导:用户提问题,AI 不替用户判断「这不算问题」;用户说改就改,用户说不用改就不改。
|
||||
- 每轮:记录用户意见 → 派子代理逐条修改 → 回报 → 等用户继续提或确认。
|
||||
- 条目状态固定为 `状态:待处理` / `状态:已处理` / `状态:越界-不实施`(可带列表符号);最终确认写成**独占一行**的 `用户最终确认:通过` 或 `用户最终确认:满意`,用户原话放在**下一行**并以 `用户原文:` 开头(不要接在同一行)。收尾只检查以状态标记开头的条目行与这行最终标记,不把历史轮次里的字样当作未完成项。
|
||||
- **必须获用户明确确认(「通过」或「满意」)后才可收尾**;AI 不得自行收尾。
|
||||
- 用户指出的越界内容只记录、不实施(D-14)。
|
||||
- 修订不得删除已锁定要覆盖的内容;渲染端 / 编辑器 / 序列化事件注册 / 地图事件 id 绑定仍只用占位。
|
||||
- 命令一律经 `cmd /c "..."` 执行。
|
||||
|
||||
**研究开放问题处置:** Q1–Q6 的统一口径见 `10-33-PLAN.md` 的「研究开放问题处置」小节(目录/文件名已批准、I 同批移动、两页独立、渲染端只写接口级、C1–C8 只对话报告);本计划不重新讨论这些口径。
|
||||
|
||||
**交付方式:**
|
||||
- 本地预览:`cmd /c "pnpm docs:preview"`(预览 `public/_docs`),或直接浏览构建产物与源码。
|
||||
- 审阅对象:`docs/advanced/iii/` 的八页,以及它们在高级 III 导览、侧边栏、总导览 / I 导览 / II 导览中的入口;重点看系统模型是否讲对、两个实例 / 两个阶段的归属是否清楚、旧引擎是否被当成当前接口、示例能否照做、章节顺序是否符合「先用法后内部」、有没有死链或排版问题。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 0: 向用户汇报用户修订循环计划并等待「可以执行」</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-CONTEXT.md(D-09、D-12、D-46、D-57)、.planning/phases/10-docs-restructure/10-32-PLAN.md(同类收尾计划的流程与门禁)</read_first>
|
||||
<action>向用户汇报四段并等待批准(D-12):**要做什么**——执行八页的用户主导修订循环:用户逐条提问题 → AI 派子代理修改 → 回报 → 用户继续提,直到用户确认满意;确认后做最终格式与构建收口、写 SUMMARY,并在 `ROADMAP.md` / `STATE.md` 记录第五批收尾。**为什么**——D-46 第 2 阶段要求必须经用户循环且获确认才可收尾;D-09 的验收就是用户人工审阅。**大致怎么做**——Task 1 交付构建产物并收集意见;Task 2 逐条修改并记录;Task 3 回报并等待继续提或确认;Task 4 在用户确认后做最终 prettier / 构建 / 结构与 SUMMARY / ROADMAP / STATE 收口。**影响范围**——可能修改八页文档与 `.planning` 下的审阅记录、SUMMARY、ROADMAP、STATE;不改 I / II 正文语义、不改源码。汇报中明确:ROADMAP / STATE 的更新**只会在用户确认后**执行,且只做追加式编辑(不用整文件重写);本计划的修改方式为**派发子代理执行**(ROADMAP / STATE 的收尾编辑按 GSD 流程要求执行)。停下等待用户明确回复「可以执行」四个字;未收到前不得交付审阅、不得派发修改子代理、不得写任何文件。</action>
|
||||
<what-built>本计划内容:高级 III 的用户主导修订循环 + 用户确认后的收尾(含 ROADMAP / STATE 记账)。</what-built>
|
||||
<how-to-verify>确认交付方式(预览命令 / 审阅范围)、ROADMAP / STATE 只在用户确认后更新;如有要求请一并说明。</how-to-verify>
|
||||
<resume-signal>回复「可以执行」以继续;其它回复视为未批准,本计划停在此处。</resume-signal>
|
||||
<acceptance_criteria>用户已回复「可以执行」;若对交付方式(预览命令 / 审阅范围)或 ROADMAP / STATE 的更新范围提出要求,已并入后续任务。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已回复「可以执行」。</human-check>
|
||||
</verify>
|
||||
<done>用户回复「可以执行」。</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 1: 交付构建产物,收集用户意见</name>
|
||||
<read_first>docs/advanced/iii/index.md、docs/advanced/iii/*.md 的标题结构、docs/advanced/index.md 与 docs/advanced/i/index.md 与 docs/advanced/ii/index.md 的 III 入口</read_first>
|
||||
<action>先跑 `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` 确认 green、`build complete`、dead link = 0,然后向用户交付:八页路径与它们在导览 / 侧边栏中的位置、三处入口(总导览 / 高级 I 导览 / 高级 II 导览)、本地预览方式 `cmd /c "pnpm docs:preview"`,以及 10-40 的读者审阅结论摘要(阻断问题是否清零、非阻断残项、哪些章节使用概率高 / 低)。请用户以本领域视角逐条提出:系统模型是否正确、装配顺序与加载阶段是否讲反、谓词 / 事件 / 录像的实例归属是否清楚、旧引擎是否被当成当前接口、内部实现章节是否讲到合适深度、示例能否直接照做、章节顺序是否该调整、有没有死链或排版问题。**不要替用户判断某条算不算问题**;用户提出的每一条都记录到 `.planning/phases/10-docs-restructure/10-41-USER-REVIEW.md`(意见、定位、`状态:待处理`)。若用户直接回复「通过」或「满意」,就写独占一行的 `用户最终确认:通过` 或 `用户最终确认:满意`,并在**下一行**以 `用户原文:` 开头附原文(不要追加在同一行),然后直接进入 Task 4;否则带着意见清单进入 Task 2。Commit:`docs(10-41): 记录高级 III 用户修订意见(第1轮)`。</action>
|
||||
<what-built>构建后的八页与 10-40 审阅结论摘要,供用户逐条提问。</what-built>
|
||||
<how-to-verify>`cmd /c "pnpm docs:preview"` 预览或直接阅读八页;按「系统模型 / 实例归属 / 旧接口误用 / 章节顺序 / 示例可照做 / 链接与排版」逐条提问。</how-to-verify>
|
||||
<resume-signal>请逐条列出问题(可带文件名与章节);若已满意,请直接回复「通过」或「满意」。</resume-signal>
|
||||
<acceptance_criteria>用户已给出至少一条具体意见,或明确回复「通过」/「满意」;意见已记录并标注状态。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已给出至少一条具体意见,或明确回复「通过」/「满意」。</human-check>
|
||||
</verify>
|
||||
<done>用户意见已记录,或用户已明确确认通过。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 按用户意见逐条派子代理修改并回报</name>
|
||||
<files>docs/advanced/iii/index.md, docs/advanced/iii/loading-system.md, docs/advanced/iii/replay-internals.md, docs/advanced/iii/event-system.md, docs/advanced/iii/anon-tokyo.md, docs/advanced/iii/passability-and-pathfinding.md, docs/advanced/iii/core-state.md, docs/advanced/iii/plugin-internals.md, .planning/phases/10-docs-restructure/10-41-USER-REVIEW.md</files>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-41-USER-REVIEW.md(当前待处理条目)</read_first>
|
||||
<action>读取 `10-41-USER-REVIEW.md` 中 `状态:待处理` 的用户意见,逐条派子代理修改:每条意见都要落到具体文件与段落;用户说改哪里就改哪里,不擅自扩大范围;用户指出但属于渲染端、编辑器、序列化事件注册、地图事件 id 绑定、I / II 正文或生产代码的内容只记录、不实施(D-14),状态行写 `状态:越界-不实施`。每轮修改后:(a) 跑构建门禁 `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`,须 green、`build complete`、dead link = 0;(b) 把该条状态改为 `状态:已处理` 并写明处置与改动文件;(c) 向用户回报本轮改了哪些、怎么改的、构建结论。修订中守住 D-57..D-83(库文档风格、简洁、边界单列、代码块完整、内部实现后置、不写源码叙述、不写模板标记、不写使用概率元信息);不删除已锁定要覆盖的内容;不把占位改成死链。每轮 Commit:`docs(10-41): 按用户意见修订高级 III(第N轮)`。</action>
|
||||
<acceptance_criteria>本轮用户意见逐条落到具体段落;记录中状态与改动文件清楚;构建 green、`build complete`、dead link = 0;八页仍单 H1、CRLF、相对链接可解析;正文无使用概率元信息与模板标记。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $f='.planning/phases/10-docs-restructure/10-41-USER-REVIEW.md'; if(-not(Test-Path -LiteralPath $f)){ Write-Error "MISSING $f" }; $raw=[IO.File]::ReadAllText((Resolve-Path -LiteralPath $f)); if($raw -notmatch '状态:已处理|状态:越界-不实施|用户最终确认:通过|用户最终确认:满意'){ Write-Error 'NO-DISPOSITION' }; if($raw -notmatch 'docs/advanced/iii/'){ Write-Error 'NO-TARGET' }; $pages=@('index','loading-system','replay-internals','event-system','anon-tokyo','passability-and-pathfinding','core-state','plugin-internals'); foreach($n in $pages){ $p="docs/advanced/iii/$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 $p" }; if($body -match '使用概率'){ Write-Error "META-IN-BODY $p" }; if($body -match '\[需改\]'){ Write-Error "TEMPLATE-MARK $p" }; foreach($m in [regex]::Matches($body,'\]\((\.{1,2}/[^)#]+\.md)(#[^)]*)?\)')){ $t=Join-Path (Split-Path -Parent (Resolve-Path -LiteralPath $p)) $m.Groups[1].Value; if(-not(Test-Path -LiteralPath $t)){ Write-Error "DEAD-LINK $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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'FIX-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "NO-DISPOSITION", "NO-TARGET", "LF-ONLY", "H1", "META-IN-BODY", "TEMPLATE-MARK", "DEAD-LINK", "BUILD-FAIL", or "NO-BUILD-COMPLETE"; or output lacks "FIX-OK"</fails_when>
|
||||
</verify>
|
||||
<done>本轮意见逐条处置并有记录;构建 green、`build complete`、无 dead link;页面结构未被破坏。</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 3: 回报修改结果,等待用户继续提或确认满意</name>
|
||||
<read_first>.planning/phases/10-docs-restructure/10-41-USER-REVIEW.md(本轮处置)</read_first>
|
||||
<action>向用户回报本轮处置:逐条列出用户意见 → 改了哪个文件 / 段落 → 怎么改的 → 构建结论;附上更新后的预览方式。然后请用户继续审阅:如果还有问题,请继续逐条提出(执行者回到 Task 2 继续改,循环不限轮次,直到用户满意);如果已经满意,请明确回复「通过」或「满意」。**只有用户明确回复「通过」或「满意」才可进入 Task 4 收尾**;用户沉默、只回复「可以」、或回复其它内容(包括「不满意」「未通过」)都不算确认,不得自行收尾。确认后在记录中写独占一行的 `用户最终确认:通过` 或 `用户最终确认:满意`,并在**下一行**以 `用户原文:` 开头附用户原话(保持两行结构,不要把原文接在标记同一行)。Commit:`docs(10-41): 记录高级 III 用户确认结论`(仅在用户确认后提交)。</action>
|
||||
<what-built>按用户意见修订后的八页与逐条处置说明。</what-built>
|
||||
<how-to-verify>按上一轮的预览方式复查改动处;确认每条意见都已处理,且没有引入新的错误或死链。</how-to-verify>
|
||||
<resume-signal>若仍有问题,请逐条列出;若已满意,请回复「通过」或「满意」。</resume-signal>
|
||||
<acceptance_criteria>用户已明确回复「通过」或「满意」(记录原文),或提出了新的具体意见。</acceptance_criteria>
|
||||
<verify>
|
||||
<human-check>用户已明确回复「通过」或「满意」(不含「不满意」「未通过」),或提出了新的具体意见。</human-check>
|
||||
</verify>
|
||||
<done>用户确认满意(记录原文),或用户提出了新的具体意见(回到 Task 2)。</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 4: 用户确认后的最终格式、构建、SUMMARY 与 ROADMAP / STATE 收尾</name>
|
||||
<files>.planning/phases/10-docs-restructure/10-41-USER-REVIEW.md, .planning/phases/10-docs-restructure/10-41-SUMMARY.md, docs/advanced/iii/index.md, docs/advanced/iii/*.md, .planning/ROADMAP.md, .planning/STATE.md</files>
|
||||
<read_first>.planning/ROADMAP.md(Phase 10 段落:Goal、Plans 计数、Plans 列表末尾)、.planning/STATE.md(frontmatter progress、Current Position、Roadmap Evolution)、.planning/phases/10-docs-restructure/10-32-SUMMARY.md(收尾体例)</read_first>
|
||||
<action>**前置:`10-41-USER-REVIEW.md` 已有独占一行的 `用户最终确认:通过` 或 `用户最终确认:满意`;未确认则不得执行本任务。** (1) 最终格式:`node node_modules/prettier/bin/prettier.cjs --write docs/advanced/iii`,再 `--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;侧边栏 III 组 8 个目标;三处 III 入口链接仍在;八页都有返回导览;无 `src/client`(`loading-system` 的渲染端小节除外);无「模板」「[需改]」;正文无使用概率元信息。(4) 在 `10-41-USER-REVIEW.md` 记录最终结论:确认标记与用户原文、最终构建结论、非阻断残项(标注 `状态:越界-不实施` 或写成非阻断说明,不保留 `状态:待处理`)。(5) 写 `10-41-SUMMARY.md`:八页与导览 / 侧边栏 / 三处入口的最终清单、用户循环轮次与处置摘要、10-40 审阅结论、最终 prettier / 构建 / 链接 / 结构结论、未处理项(仅越界或非阻断)、「用户已确认收尾」的明确记录。
|
||||
(6) 更新 `.planning/ROADMAP.md`(**只用 Edit 做追加式修改,不得整文件 Write**):① 在 `<verification>` 之前不需要改动;在 Phase 10 的 `Plans:` 列表中,`10-32-PLAN.md` 之后追加九条已勾选条目 `- [x] 10-33-PLAN.md — 目录重构(高级 I 移入 i/)+ 高级教程总导览 + 侧边栏改指 + 全量构建`、`- [x] 10-34-PLAN.md — 加载系统(`loading-system.md`)`、`- [x] 10-35-PLAN.md — 录像系统底层(`replay-internals.md`)`、`- [x] 10-36-PLAN.md — 事件系统 + Anon-Tokyo 解释器(`event-system.md` / `anon-tokyo.md`)`、`- [x] 10-37-PLAN.md — 通行性谓词与寻路原理(`passability-and-pathfinding.md`)`、`- [x] 10-38-PLAN.md — CoreState 组装 + 插件内部机制(`core-state.md` / `plugin-internals.md`)`、`- [x] 10-39-PLAN.md — 高级 III 导览 + 侧边栏注册 + 互链收口 + 全量构建`、`- [x] 10-40-PLAN.md — 高级 III 独立读者审阅(含章节使用概率)`、`- [x] 10-41-PLAN.md — 高级 III 用户修订循环 → 用户确认 → 最终构建 + SUMMARY + ROADMAP/STATE 收尾`;② 把 `**Plans:**` 行改为反映实际:总数为 41,已完成为「已勾选条目数」(本计划执行前为 29,本批完成后应为 38),文案沿用原有「x/y completed + 各批次说明」的写法,并在批次说明末尾追加「**第五批 = 高级 III 数据端 8 篇 + 目录重构(高级 I 移入 `docs/advanced/i/`)+ 站点注册 + 读者/用户修订(D-53..D-83,2026-10-XX 收尾)**」;③ 在 Phase 10 的 `**Goal:**` 段落末尾追加一句第五批说明(若 Goal 行已很长,只追加一句,不改写原有句子)。**不要**改动其他 Phase、不要删除 10-06 / 10-11 / 10-12 的未勾选状态。
|
||||
(7) 更新 `.planning/STATE.md`(同样只用 Edit):① frontmatter:只更新 `last_updated`、`last_activity`、`last_activity_desc`(写「高级 III 8 篇 + 目录重构完成并经用户确认收尾」);**`progress.total_plans` 与 `progress.completed_plans` 是项目级累计值,保持原值不动**(不得改成 41 / 38 这类本批口径);② `Current Position` 的 `Plan:` 与 `Last activity:` 各补一句第五批收尾;③ `Progress:` 一行更新为「Phase 10 · 38/41 plans」并保留 10-06 / 10-11 / 10-12 待补说明(这是 Phase 10 的局部进度,不是 frontmatter 全局计数);④ `### Roadmap Evolution` 末尾追加一条第五批记录(内容与 ROADMAP 一致,说明第五批范围、修订轮次与用户确认)。**不要**改动其它阶段的历史决策条目、Deferred Items 与 Blockers。
|
||||
(8) 若用户在循环中要求调整侧边栏显示名或顺序,才编辑 `docs/.vitepress/config.ts` 的 III 组并重跑构建;未提出则不动该文件,并在 SUMMARY 注明「config 未改」。
|
||||
Commit:`docs(10-41): 高级 III 用户修订收尾并记录第五批`。</action>
|
||||
<acceptance_criteria>用户确认已记录且无待处理条目(标记独占一行、原文在下一行);八页结构、链接、侧边栏与三处入口全部正确;prettier 与构建双绿;SUMMARY 完整;ROADMAP 追加九条勾选条目并更新本阶段计数与批次说明;STATE 增量记录第五批、Phase 10 的 Progress 行为 38/41、frontmatter 的全局 `total_plans` / `completed_plans` 保持原值且保留既有待补项;两个文件均为追加式编辑、未删除既有内容。</acceptance_criteria>
|
||||
<verify>
|
||||
<automated>$ErrorActionPreference='Stop'; $rev='.planning/phases/10-docs-restructure/10-41-USER-REVIEW.md'; $sum='.planning/phases/10-docs-restructure/10-41-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 @('用户','确认','构建','高级 III')){ if($s -notmatch [regex]::Escape($needle)){ Write-Error "SUMMARY-MISSING $needle" } }; $pages=@('index','loading-system','replay-internals','event-system','anon-tokyo','passability-and-pathfinding','core-state','plugin-internals'); foreach($n in $pages){ $p="docs/advanced/iii/$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 $p" }; if($n -ne 'loading-system' -and $body -match 'src/client'){ Write-Error "RENDERER-CONTENT $p" }; if($n -ne 'index' -and $body -notmatch '\./index\.md'){ Write-Error "NO-BACKLINK $p" } }; $cfg=[IO.File]::ReadAllText((Resolve-Path -LiteralPath 'docs/.vitepress/config.ts')); if(([regex]::Matches($cfg,"link:\s*'/advanced/iii/[^']*'")).Count -ne 8){ Write-Error 'III-LINKS' }; $road=[IO.File]::ReadAllText((Resolve-Path -LiteralPath '.planning/ROADMAP.md')); foreach($n in 33,34,35,36,37,38,39,40,41){ if($road -notmatch ("10-" + $n + "-PLAN\.md")){ Write-Error "ROADMAP-MISSING 10-$n" } }; if($road -notmatch '第五批'){ Write-Error 'ROADMAP-NO-BATCH5' }; $state=[IO.File]::ReadAllText((Resolve-Path -LiteralPath '.planning/STATE.md')); if($state -notmatch '第五批'){ Write-Error 'STATE-NO-BATCH5' }; if($state -notmatch '10-06'){ Write-Error 'STATE-LOST-PENDING' }; $tp=[regex]::Match($state,'(?m)^\s*total_plans:\s*(\d+)'); $cp=[regex]::Match($state,'(?m)^\s*completed_plans:\s*(\d+)'); if(-not $tp.Success -or -not $cp.Success){ Write-Error 'STATE-COUNTERS-MISSING' }; $tpv=[int]$tp.Groups[1].Value; $cpv=[int]$cp.Groups[1].Value; if($tpv -eq 41 -or $cpv -eq 38){ Write-Error 'STATE-BATCH-COUNTERS' }; if($tpv -lt 134){ Write-Error 'STATE-TOTAL-PLANS-SHRANK' }; if($cpv -lt 126){ Write-Error 'STATE-COMPLETED-PLANS-SHRANK' }; if($state -notmatch 'Phase 10 · 38/41'){ Write-Error 'STATE-NO-PHASE-PROGRESS' }; $nextLine=[regex]::Match($r,'(?m)^\s*(?:[-*]\s*)?用户最终确认:(通过|满意)[ \t]*\r?\n[ \t]*(?:[-*]\s*)?用户原文:'); if(-not $nextLine.Success){ Write-Error 'NO-USER-QUOTE-NEXT-LINE' }; $ErrorActionPreference='Continue'; $pf = cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii" 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|dead link'){ Write-Error 'DEAD-LINK' }; Write-Output 'CLOSEOUT-OK'</automated>
|
||||
<fails_when>non-zero exit, or output contains "MISSING", "NO-USER-CONFIRMATION", "PENDING-USER-ITEMS", "SUMMARY-MISSING", "LF-ONLY", "H1", "RENDERER-CONTENT", "NO-BACKLINK", "III-LINKS", "ROADMAP-MISSING", "ROADMAP-NO-BATCH5", "STATE-NO-BATCH5", "STATE-LOST-PENDING", "STATE-COUNTERS-MISSING", "STATE-BATCH-COUNTERS", "STATE-TOTAL-PLANS-SHRANK", "STATE-COMPLETED-PLANS-SHRANK", "STATE-NO-PHASE-PROGRESS", "NO-USER-QUOTE-NEXT-LINE", "PRETTIER-FAIL", "BUILD-FAIL", "NO-BUILD-COMPLETE", or "DEAD-LINK"; or output lacks "CLOSEOUT-OK"</fails_when>
|
||||
</verify>
|
||||
<done>用户确认与最终结论在案;八页结构与入口正确;prettier 与构建双绿;SUMMARY 完整;ROADMAP / STATE 以追加方式记录第五批并保留既有待补项。</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| 用户 → 执行者 | 用户意见是修订输入;执行者不得替用户判断某条不算问题 |
|
||||
| 执行者 → 子代理 | 修改子代理按用户意见改文档,不扩大范围 |
|
||||
| 执行者 → 路线图 / 状态 | ROADMAP / STATE 是项目记账,误记会让未验收内容被当成已完成 |
|
||||
| 构建期 markdown → 静态站点 | 正文静态渲染,无运行时输入 |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-10-121 | Spoofing | 用户确认记录 | low | mitigate | 只把用户明确回复的「通过」/「满意」记为确认;沉默或其它回复不算 |
|
||||
| T-10-122 | Tampering | ROADMAP / STATE 记账 | medium | mitigate | 只做追加式 Edit;断言九条计划条目存在、第五批文本存在、10-06 待补记录未丢 |
|
||||
| T-10-123 | Tampering / XSS | 用户修订后的八页正文 | low | mitigate | 只写文本、代码块、表格;不引入原始 `<script>` 或内联 HTML |
|
||||
| T-10-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段不安装任何包,无供应链安装面 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- 机制门禁:`10-41-USER-REVIEW.md` 含每轮意见、定位、处置、构建结论与最终确认原文。
|
||||
- 确认门禁:记录含独占一行的 `用户最终确认:通过` 或 `用户最终确认:满意`;条目级 `状态:待处理` 为 0。
|
||||
- 构建门禁:每轮及收尾均 `build complete` 且无 `dead link`。
|
||||
- 格式门禁:prettier `--check` 对八页通过。
|
||||
- 结构门禁:八页 kebab-case、CRLF、单 H1;III 侧边栏 8 项;三处入口链接在。
|
||||
- 记账门禁:ROADMAP 追加九条勾选条目、计数与第五批说明;STATE 增量记录第五批并保留 10-06 待补。
|
||||
- 越界门禁:不改 I / II 正文语义与源码;越界意见只记录。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 用户主导修订循环真实执行,用户明确确认满意;AI 未自行收尾。
|
||||
- 高级 III 八页可构建、可预览、无死链,入口与互链完整。
|
||||
- 审阅记录、SUMMARY、ROADMAP、STATE 的记账一致且为追加式,能支撑 D-09 的人工验收。
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/10-docs-restructure/10-41-SUMMARY.md` when done
|
||||
</output>
|
||||
@ -0,0 +1,309 @@
|
||||
# Phase 10: Advanced III 文档批次 — Plan Check
|
||||
|
||||
**Phase:** 10-docs-restructure
|
||||
**Plans checked:** 10-33 .. 10-41 (9 plans)
|
||||
**Research input:** `10-ADVANCED-III-RESEARCH.md`
|
||||
**Decisions baseline:** `10-CONTEXT.md` D-09/D-12/D-13/D-46/D-53..D-83
|
||||
**Status:** ISSUES FOUND — 3 blockers, 6 warnings, 9 advisories
|
||||
**Re-check (post-revision):** PASS — 0 blockers, 0 warnings, 3 residual INFO; see **RE-CHECK (post-revision)** at the end.
|
||||
|
||||
## VERIFICATION SUMMARY
|
||||
|
||||
The batch covers the approved scope end-to-end: directory restructure + root guide (10-33), the seven III data-end pages (10-34..10-38), III index + sidebar registration + placeholder replacement (10-39), independent reader review (10-40), and user-led revision + closeout (10-41). Source facts in the research are assigned to the correct pages and the core models match the current source. However, two verification gates contradict their own required end state, and the STATE.md accounting instruction is factually wrong for the project-wide counters. These must be fixed before execution; otherwise the plan cannot pass its own gate or will corrupt project state.
|
||||
|
||||
## Plan Summary
|
||||
|
||||
| Plan | Scope | Tasks | Wave | Status |
|
||||
|------|-------|-------|------|--------|
|
||||
| 10-33 | git mv Advanced I → `docs/advanced/i/`; new root guide; config I group; cross-layer links | 3 (1 checkpoint + 2 auto) | 5 | Block (gate self-contradiction) |
|
||||
| 10-34 | `loading-system.md` | 3 (1 checkpoint + 2 auto) | 6 | Block (gate self-contradiction) |
|
||||
| 10-35 | `replay-internals.md` | 3 (1 checkpoint + 2 auto) | 7 | Warning (finalize semantics) |
|
||||
| 10-36 | `event-system.md` + `anon-tokyo.md` | 4 (1 checkpoint + 3 auto) | 8 | Pass |
|
||||
| 10-37 | `passability-and-pathfinding.md` | 3 (1 checkpoint + 2 auto) | 9 | Pass (minor imprecision) |
|
||||
| 10-38 | `core-state.md` + `plugin-internals.md` | 4 (1 checkpoint + 3 auto) | 10 | Warning (D-69 placement) |
|
||||
| 10-39 | III index; sidebar 8 items; 3 placeholder replacements; backlinks/cross-links | 4 (1 checkpoint + 3 auto) | 11 | Warning (anchor mismatch) |
|
||||
| 10-40 | Independent reader review + usage probabilities + ≤3 rounds | 4 (1 checkpoint + 3 auto) | 12 | Warning (persona gate fragility) |
|
||||
| 10-41 | User revision loop + confirmation + final gates + ROADMAP/STATE | 5 (3 checkpoints + 2 auto) | 13 | Block (STATE counters) |
|
||||
|
||||
Wave/dependency graph is valid and linear: 10-32(w4) → 10-33(w5) → 10-34(w6) → 10-35(w7) → 10-36(w8) → 10-37(w9) → 10-38(w10) → 10-39(w11) → 10-40(w12) → 10-41(w13). All referenced plans exist; no cycles; no same-wave parallel pairs, so no undeclared coupling. All plan structures are valid; only 10-33 emits a tool warning (fallible `git` heuristic).
|
||||
|
||||
## Coverage Summary
|
||||
|
||||
| Approved scope / decision | Plans | Status |
|
||||
|---------------------------|-------|--------|
|
||||
| Move Advanced I to `docs/advanced/i/`, rebuild root 高级教程总导览, update config I group | 10-33 | Covered |
|
||||
| loading-system (model, phases, extensions, renderer interface section, boundaries) | 10-34 | Covered |
|
||||
| replay-internals (ReplayArray/System/Sandbox, Active/Passive, save shape, old-vs-new baseline) | 10-35 | Covered (one fact misstated) |
|
||||
| event-system + anon-tokyo (store/collect/executor/triggers; interpreter model) | 10-36 | Covered |
|
||||
| passability-and-pathfinding (predicate contract, two instances, graph→search→mover) | 10-37 | Covered |
|
||||
| core-state + plugin-internals (L0–L3 assembly, load pipeline, saveable ids, orderPlugins) | 10-38 | Covered |
|
||||
| III index, sidebar 8 items, three placeholder replacements, backlinks | 10-39 | Covered |
|
||||
| D-46 stage 1 / D-57 higher persona / D-78 usage probabilities / ≤3 rounds | 10-40 | Covered |
|
||||
| D-46 stage 2 / user confirmation before closeout / appended ROADMAP+STATE | 10-41 | Block (STATE counters) |
|
||||
| D-09 (user review + links valid + build) | 10-40, 10-41 | Covered |
|
||||
| D-12/D-13 Task 0 「可以执行」 + naming confirmation | 10-33..10-41 | Covered |
|
||||
| D-77/D-80/D-82/D-83/D-60/D-61/D-62/D-70/D-65 content constraints | 10-34..10-39 | Covered |
|
||||
| Render pipeline excluded; editor/serialized registration excluded; legacy only as labeled baseline | 10-34..10-39 | Covered |
|
||||
|
||||
## ISSUES FOUND
|
||||
|
||||
### Blockers — these properties must hold ("must fix" is the property, never the example)
|
||||
|
||||
**1. [task_completeness / verification] The stale-root gate must not reject the intentionally recreated `docs/advanced/index.md`**
|
||||
- Plan: 10-33
|
||||
- Evidence: Task 1 creates the new root guide at `docs/advanced/index.md` (`10-33-PLAN.md:114` step 3: "用 Write 重写 `docs/advanced/index.md`(原文件已移走,这是新建)"). But the Task 1 verify iterates `$names` including `'index'` and errors if `docs/advanced/index.md` exists: `if(Test-Path -LiteralPath "docs/advanced/$n.md"){ Write-Error "STALE-ROOT $n.md" }` (`10-33-PLAN.md:122`). The `fails_when` explicitly fails on `STALE-ROOT` (`10-33-PLAN.md:123`). A correct execution therefore always fails the gate.
|
||||
- Example fix (non-binding — any mechanism reaching the property counts): exclude `index` from the stale-root check (check only the 8 non-index I files), or assert the old I-guide content is gone rather than the path being absent.
|
||||
|
||||
**2. [task_completeness / verification] The loading-system title check must accept a CRLF H1**
|
||||
- Plan: 10-34
|
||||
- Evidence: The page is required to be CRLF (`10-34-PLAN.md:101-102`: LF-ONLY check) and the exact title check is `if($raw -notmatch '(?m)^# 加载系统$'){ Write-Error 'NO-TITLE' }` (`10-34-PLAN.md:102`). In .NET multiline mode `$` does not match a line ending in `\r\n`; the actual H1 line is `# 加载系统\r\n`, so the check fails on every compliant file. `NO-TITLE` is in `fails_when` (`10-34-PLAN.md:103`). I confirmed the regex behavior locally: `[regex]::IsMatch("# 加载系统`r`n...", '(?m)^# 加载系统$')` = False.
|
||||
- Example fix (non-binding): use `'(?m)^# 加载系统\r?$'`, a substring check, or a BOM/line-normalized comparison.
|
||||
|
||||
**3. [context_compliance / accounting] STATE.md's project-wide `progress.*_plans` counters must not be overwritten with phase-only counts**
|
||||
- Plan: 10-41
|
||||
- Evidence: `10-41-PLAN.md:148` instructs: "`progress.total_plans` 改为 41、`progress.completed_plans` 改为实际完成数(预期 38)". But `.planning/STATE.md:14-15` currently hold project-wide aggregates `total_plans: 134` / `completed_plans: 126` (with `total_phases: 10` / `completed_phases: 3`). Setting them to 41/38 would silently corrupt the project ledger. It also contradicts the plan's own truth "STATE.md 只做增量更新" (`10-41-PLAN.md:35`) and acceptance "两个文件均为追加式编辑" (`10-41-PLAN.md:150`). The closeout verify only checks `第五批` and `10-06` (`10-41-PLAN.md:152`), so the corruption would pass unnoticed.
|
||||
- Example fix (non-binding): increment the global counters by the 9 newly completed plans (e.g. 134→143, 126→135) while keeping the phase-level `Progress: Phase 10 · 38/41 plans` line; or leave the frontmatter counters untouched and only update the phase-level progress line.
|
||||
|
||||
### Warnings — these properties should hold
|
||||
|
||||
**1. [verification_derivation / factual accuracy] The replay finalize semantics must match the source**
|
||||
- Plan: 10-35
|
||||
- Evidence: `10-35-PLAN.md:87` says "连续同指令步骤之间会调用一次收尾方法" ("finalize is called between consecutive same-command steps"). Source and research say the opposite: finalize runs when the active command changes (`sandbox.ts:196` `if (curr.command !== this.last)`) or at replay end (`sandbox.ts:162-164`), per research `10-ADVANCED-III-RESEARCH.md:264`. The must_haves wording "连续步骤之间会触发 finalize" (`10-35-PLAN.md:21`) is ambiguous; the action wording is wrong.
|
||||
- Example fix (non-binding): state "指令切换或录像结束时触发 finalize".
|
||||
|
||||
**2. [verification_derivation / D-69] Internal-implementation sections must be placed later, as the plan's own must_haves claims**
|
||||
- Plan: 10-38
|
||||
- Evidence: `10-38-PLAN.md:29` requires "内部实现章节后置". But core-state orders `## 系统模型` → `## 内部实现:装配顺序` → `## 加载管线` (`10-38-PLAN.md:90`), and plugin-internals orders `## 系统模型` → `## 内部实现:依赖排序` → usage sections (`10-38-PLAN.md:111`). Both put the internal section second, not later, conflicting with D-69 and the plan's stated truth.
|
||||
- Example fix (non-binding): move the internal sections after the usage/API sections, or justify that these pages are themselves internal-implementation topics.
|
||||
|
||||
**3. [context_compliance / D-74] Chapter anchor links must match the actual heading text**
|
||||
- Plan: 10-39
|
||||
- Evidence: 10-39 instructs linking `./event-system.md#收集` (`10-39-PLAN.md:121`), but the event page heading is `## 收集:这一次该执行谁` (`10-36-PLAN.md:92`). VitePress slugs the full heading, so `#收集` will not land on the section. The build does not validate anchors, so the link gate will not catch it.
|
||||
- Example fix (non-binding): use the full anchor (`#收集这一次该执行谁`) or point at the exact heading.
|
||||
|
||||
**4. [task_completeness / scope] Every file the plan may modify must be listed in scope**
|
||||
- Plan: 10-41
|
||||
- Evidence: `10-41-PLAN.md:36` allows "不改 `config.ts` 结构(除用户明确要求的显示名 / 顺序)", i.e. the user may request sidebar name/order changes, but `files_modified` (`10-41-PLAN.md:7-19`) omits `docs/.vitepress/config.ts`. If the user requests such a change, the executor would edit an undeclared file, violating the plan's own scope gate (`10-41-PLAN.md:187`).
|
||||
- Example fix (non-binding): add `docs/.vitepress/config.ts` to `files_modified` as a conditional target, or state that display-name/order changes are out of scope for 10-41.
|
||||
|
||||
**5. [verification_derivation] The reader-persona gate must accept the plan's own record format**
|
||||
- Plan: 10-40
|
||||
- Evidence: The persona is recorded as a blockquote in `10-40-PLAN.md:73`, but the verify computes `$flat=$raw -replace '\s',''` then `$flat.Contains($persona)` (`10-40-PLAN.md:111,136`). `-replace '\s',''` removes spaces/newlines but not the `>` prefix; if the long persona is line-wrapped in the review file (a legitimate rendering of the same text), the `>` characters break the substring and the gate fails.
|
||||
- Example fix (non-binding): also strip `>`/blockquote markers, or compare line-by-line after trimming the quote prefix.
|
||||
|
||||
**6. [research_resolution] The new research's Open Questions must be marked resolved or explicitly dispositioned**
|
||||
- Research: `10-ADVANCED-III-RESEARCH.md:810` has `## Open Questions` with six questions and no `(RESOLVED)` suffix or per-question RESOLVED markers. The plans do address them (Q1/Q2 via Task 0 gates; Q3/Q4 via fixed page list; Q5 via 10-34's renderer scope; Q6 via the C1–C8 dialogue-report rule), but the research artifact itself still reads as unresolved.
|
||||
- Example fix (non-binding): mark the section `## Open Questions (RESOLVED)` and annotate each question with its disposition, or add a pointer to the plans that answer it.
|
||||
|
||||
### Advisories (info)
|
||||
|
||||
**1. [requirement_coverage] Link-count annotations in 10-33 are stale; the "全部" replacement still works**
|
||||
- Plan: 10-33
|
||||
- Evidence: `10-33-PLAN.md:83` says 15 `../intermediate/` occurrences and 6 `./ii/` occurrences; the current tree has 16 `../intermediate/` (data-stores has 2, not 1) and 7 `./ii/` (index has 4, not 3). The action says "全部" replace (`10-33-PLAN.md:115`), and the verify checks no `](../intermediate/` / `](./ii/` remains, so the replacement is functionally correct; only the counts are wrong.
|
||||
- Example fix (non-binding): update the counts or drop the numbers.
|
||||
|
||||
**2. [scope_sanity] 10-33 lists 15 files, but 9 are `git mv` moves**
|
||||
- Plan: 10-33
|
||||
- Evidence: `10-33-PLAN.md:7-22` lists 15 paths; 9 are moved with unchanged content and 3 II files only change link paths. Estimate is 42k tokens (`10-33-PLAN.md:26-30`), well under the 100k budget, so the edit surface is not actually over budget. Advisory only.
|
||||
|
||||
**3. [scope_sanity] 10-41 has 5 tasks, but 3 are human checkpoints**
|
||||
- Plan: 10-41
|
||||
- Evidence: `10-41-PLAN.md:87-156` (Tasks 0/1/3 are `checkpoint:human-verify`; Tasks 2/4 are auto). Context cost is low (estimate 32k, `10-41-PLAN.md:23-27`). Advisory only.
|
||||
|
||||
**4. [task_completeness] 10-33 emits a `git` heuristic warning**
|
||||
- Plan: 10-33
|
||||
- Evidence: `gsd-tools query verify.plan-structure` warns "[plan-criteria R4] A fallible `git` in a non-final pipeline stage is swallowed". The plan only uses `git mv` (`10-33-PLAN.md:114`) and a `git status` self-check (`10-33-PLAN.md:172`); there is no pipeline whose last stage would mask a `git` failure. Likely a false positive; no action required.
|
||||
|
||||
**5. [verification_derivation] 10-37 does not link the event page from the "three move results" section**
|
||||
- Plan: 10-37
|
||||
- Evidence: `10-37-PLAN.md:91` says the trigger relationship is mentioned as text and "不要放链接——该页由另一计划创建"; however 10-36 is an earlier dependency, so `event-system.md` already exists. This is conservative and creates no dead link; 10-39 later adds the sibling link (`10-39-PLAN.md:121`). Advisory only.
|
||||
|
||||
**6. [factual_accuracy] 10-37's "撞击看目标格的通行位" is imprecise**
|
||||
- Plan: 10-37
|
||||
- Evidence: `10-37-PLAN.md:89` says `shouldHit` looks at "目标格静态图块的通行位"; source/research say it checks `!nextRaw.eventPass` (`src/data/hero/predicate.ts:79`, research `10-ADVANCED-III-RESEARCH.md:363`). The executor is told to re-read source (`10-37-PLAN.md:62`), so this is likely corrected in execution.
|
||||
- Example fix (non-binding): say "目标格的 eventPass 通行标记".
|
||||
|
||||
**7. [context_compliance] 10-34's "配置 id 冲突的后果" boundary could drift into the unreachable warning-68 claim**
|
||||
- Plan: 10-34
|
||||
- Evidence: `10-34-PLAN.md:97` asks the page to explain "配置 id 冲突的后果", while the plan's must_haves forbids documenting "重复配置 id 稳定告警" (`10-34-PLAN.md:25`) and research C5 says warning 68 is unreachable (`10-ADVANCED-III-RESEARCH.md:717`). Keep the boundary to "later duplicate is ignored / loader dedupes" and do not promise a stable warning.
|
||||
|
||||
**8. [verification_derivation] 10-41's confirmation-marker gate requires the marker on its own line**
|
||||
- Plan: 10-41
|
||||
- Evidence: `10-41-PLAN.md:152` matches `^\s*(?:[-*]\s*)?用户最终确认:(通过|满意)\s*$`. The plan says the marker is on its own line (`10-41-PLAN.md:31,74`), but also says "并附用户原文"; if the quote is appended on the same line, the gate fails on a legitimate record. Keep the quote on a following line or relax the regex.
|
||||
|
||||
**9. [context_compliance] 10-39's "八篇地图" wording vs seven linked pages**
|
||||
- Plan: 10-39
|
||||
- Evidence: `10-39-PLAN.md:31` and `:116` say the map has eight pages with real links, but the index itself is not (and should not be) a self-link; the verify correctly requires links to the seven content pages (`10-39-PLAN.md:126`). Wording only.
|
||||
|
||||
## Structured Issues
|
||||
|
||||
```yaml
|
||||
issues:
|
||||
- plan: "10-33"
|
||||
dimension: "task_completeness"
|
||||
severity: "blocker"
|
||||
required_property: "The stale-root gate does not reject the intentionally recreated docs/advanced/index.md"
|
||||
description: "Task 1 creates the new root guide at docs/advanced/index.md, but the Task 1 verify iterates $names including 'index' and errors if docs/advanced/index.md exists (STALE-ROOT index.md), so a correct execution always fails the gate."
|
||||
task: 1
|
||||
fix_hint: "Exclude 'index' from the stale-root check, or assert the old I-guide content is gone rather than the path being absent."
|
||||
- plan: "10-34"
|
||||
dimension: "task_completeness"
|
||||
severity: "blocker"
|
||||
required_property: "The loading-system title check accepts a CRLF H1"
|
||||
description: "The page is required to be CRLF, but the exact title check '(?m)^# 加载系统$' does not match '# 加载系统\\r\\n' in .NET multiline mode; NO-TITLE always fires."
|
||||
task: 1
|
||||
fix_hint: "Use '(?m)^# 加载系统\\r?$', a substring check, or a line-normalized comparison."
|
||||
- plan: "10-41"
|
||||
dimension: "context_compliance"
|
||||
severity: "blocker"
|
||||
required_property: "STATE.md's project-wide progress.*_plans counters remain consistent and are not overwritten with phase-only counts"
|
||||
description: "The plan sets progress.total_plans=41 and completed_plans=38, but STATE.md:14-15 currently hold project-wide aggregates 134/126; this corrupts the ledger and contradicts the plan's own 'incremental/append-only' truth."
|
||||
task: 4
|
||||
fix_hint: "Increment the global counters by the 9 completed plans (134→143, 126→135), or leave the frontmatter counters untouched and update only the phase-level Progress line."
|
||||
- plan: "10-35"
|
||||
dimension: "verification_derivation"
|
||||
severity: "warning"
|
||||
required_property: "The replay finalize semantics match the source"
|
||||
description: "The plan says finalize is called '连续同指令步骤之间', but the source triggers it when the active command changes or the replay ends."
|
||||
task: 1
|
||||
fix_hint: "State '指令切换或录像结束时触发 finalize'."
|
||||
- plan: "10-38"
|
||||
dimension: "verification_derivation"
|
||||
severity: "warning"
|
||||
required_property: "Internal-implementation sections are placed later per D-69 and the plan's own must_haves"
|
||||
description: "core-state and plugin-internals put '## 内部实现' sections second (immediately after the model), not later as the must_haves requires."
|
||||
task: 1
|
||||
fix_hint: "Move the internal sections after the usage/API sections, or justify that these pages are themselves internal-implementation topics."
|
||||
- plan: "10-39"
|
||||
dimension: "context_compliance"
|
||||
severity: "warning"
|
||||
required_property: "Chapter anchor links match the actual heading text (D-74)"
|
||||
description: "The plan links ./event-system.md#收集, but the heading is '## 收集:这一次该执行谁'; the anchor will not resolve."
|
||||
task: 1
|
||||
fix_hint: "Use the full anchor '#收集这一次该执行谁' or point at the exact heading."
|
||||
- plan: "10-41"
|
||||
dimension: "task_completeness"
|
||||
severity: "warning"
|
||||
required_property: "Every file the plan may modify is listed in scope"
|
||||
description: "The plan allows user-requested sidebar display/order changes but omits docs/.vitepress/config.ts from files_modified."
|
||||
task: 4
|
||||
fix_hint: "Add docs/.vitepress/config.ts as a conditional target, or state display-name/order changes are out of scope."
|
||||
- plan: "10-40"
|
||||
dimension: "verification_derivation"
|
||||
severity: "warning"
|
||||
required_property: "The reader-persona gate accepts the plan's own record format"
|
||||
description: "The verify strips whitespace but not blockquote '>' prefixes; a wrapped persona record breaks $flat.Contains($persona)."
|
||||
task: 1
|
||||
fix_hint: "Strip '>' prefixes or compare line-by-line after trimming the quote marker."
|
||||
- plan: null
|
||||
dimension: "research_resolution"
|
||||
severity: "warning"
|
||||
required_property: "The new research's open questions are marked resolved or explicitly dispositioned"
|
||||
description: "10-ADVANCED-III-RESEARCH.md:810 has '## Open Questions' without '(RESOLVED)' and no per-question resolution markers; the plans address them but the artifact reads as unresolved."
|
||||
fix_hint: "Mark the section '(RESOLVED)' and annotate each question with its disposition."
|
||||
- plan: "10-33"
|
||||
dimension: "requirement_coverage"
|
||||
severity: "info"
|
||||
required_property: "The plan's link-count facts match the repository"
|
||||
description: "The plan says 15 ../intermediate/ and 6 ./ii/ occurrences; the tree has 16 and 7. The '全部' replacement still covers them."
|
||||
task: 1
|
||||
fix_hint: "Update or drop the counts."
|
||||
- plan: "10-33"
|
||||
dimension: "scope_sanity"
|
||||
severity: "info"
|
||||
required_property: "Each plan stays within the per-plan context budget"
|
||||
description: "15 files listed, but 9 are git mv moves and the estimate is 42k/100k."
|
||||
task: 1
|
||||
fix_hint: "No change required; note the move-only files in the summary."
|
||||
- plan: "10-41"
|
||||
dimension: "scope_sanity"
|
||||
severity: "info"
|
||||
required_property: "Each plan stays within the per-plan context budget"
|
||||
description: "5 tasks, but 3 are human checkpoints and the estimate is 32k/100k."
|
||||
task: 0
|
||||
fix_hint: "No change required."
|
||||
- plan: "10-33"
|
||||
dimension: "task_completeness"
|
||||
severity: "info"
|
||||
required_property: "The plan's verify commands are runnable"
|
||||
description: "verify.plan-structure emits an R4 warning about fallible git, but the plan uses git mv only (no pipeline whose last stage masks it)."
|
||||
task: 1
|
||||
fix_hint: "No action; likely a false positive."
|
||||
- plan: "10-37"
|
||||
dimension: "verification_derivation"
|
||||
severity: "info"
|
||||
required_property: "Cross-page links are added when the target already exists"
|
||||
description: "The plan avoids linking event-system.md because it says another plan creates it, but 10-36 is an earlier dependency; 10-39 adds the link later."
|
||||
task: 1
|
||||
fix_hint: "Optional: link event-system.md now; no dead-link risk either way."
|
||||
- plan: "10-37"
|
||||
dimension: "verification_derivation"
|
||||
severity: "info"
|
||||
required_property: "Factual descriptions match the source"
|
||||
description: "'撞击看目标格的通行位' should be '目标格的 eventPass 标记'."
|
||||
task: 1
|
||||
fix_hint: "Use the precise eventPass wording."
|
||||
- plan: "10-34"
|
||||
dimension: "context_compliance"
|
||||
severity: "info"
|
||||
required_property: "Documentation does not promise unreachable warning behavior"
|
||||
description: "The boundary asks for '配置 id 冲突的后果' while the must_haves forbids documenting a stable warning-68; keep the boundary to dedupe/ignore semantics."
|
||||
task: 1
|
||||
fix_hint: "Do not promise a stable warning code."
|
||||
- plan: "10-41"
|
||||
dimension: "verification_derivation"
|
||||
severity: "info"
|
||||
required_property: "The confirmation gate accepts the plan's own record format"
|
||||
description: "The marker must be alone on its line; if the user quote is appended on the same line, the gate fails."
|
||||
task: 4
|
||||
fix_hint: "Put the quote on a following line or relax the regex."
|
||||
- plan: "10-39"
|
||||
dimension: "context_compliance"
|
||||
severity: "info"
|
||||
required_property: "Wording matches the actual link count"
|
||||
description: "'八篇地图全部有真实链接' is inaccurate because the index itself is not a self-link; the verify correctly requires the seven content pages."
|
||||
task: 1
|
||||
fix_hint: "Say '七篇正文 + 本页说明'."
|
||||
```
|
||||
|
||||
## Recommendation
|
||||
|
||||
3 blocker(s) and 6 warning(s) require revision. The batch's scope, page assignment, and content constraints are otherwise sound; source facts for loading, replay storage, Anon-Tokyo, predicate instances, CoreState assembly, and plugin ordering match the current source. Fix the two self-contradictory gates (10-33 stale-root, 10-34 CRLF H1), correct the STATE.md counter instruction (10-41), and tighten the replay finalize wording / D-69 placement / anchor / persona-normalization warnings. No plan files or docs/source were modified by this check.
|
||||
|
||||
---
|
||||
|
||||
# RE-CHECK (post-revision)
|
||||
|
||||
**Re-checked:** 2026-10-26
|
||||
**Revised plans:** 10-33 .. 10-41 (9 plans, all modified)
|
||||
**Status:** PASS — 0 blockers, 0 warnings, 3 residual advisories (INFO). No revision required.
|
||||
|
||||
## Re-check verdict
|
||||
|
||||
The planner revised all nine plans. Every BLOCK and FLAG from the first check is resolved, the fixes do not introduce a new blocker, and all nine plans remain structurally valid. D-12 Task 0 checkpoints and 「可以执行」 stop text are intact in every plan.
|
||||
|
||||
## Finding-by-finding re-verification
|
||||
|
||||
| # | Original finding | Result | Evidence |
|
||||
|---|------------------|--------|----------|
|
||||
| 1 | 10-33 stale-root gate rejected the intentionally recreated root guide | **PASS** | `10-33-PLAN.md:130` now uses `$moved=@('declarative-vs-imperative',...)` (8 pages, no `index`), asserts `docs/advanced/i/index.md` exists, asserts `docs/advanced/index.md` exists (`MISSING-ROOT-INDEX`), asserts the I guide title in `i/index.md`, and asserts the root guide does **not** contain `# 高级 I 导览` (`STALE-I-GUIDE-AT-ROOT`). `fails_when` includes the new tokens (`:131`). No self-contradiction. |
|
||||
| 2 | 10-34 CRLF H1 regex `(?m)^# 加载系统$` always failed on CRLF | **PASS** | `10-34-PLAN.md:102` now uses `(?m)^# 加载系统\r?$`; the LF-only check is still present. A scan of all 9 plans found no other `(?m)^# ...$` H1 regex; the remaining `^# ` checks are substring/count checks and are CRLF-safe. |
|
||||
| 3 | 10-41 overwrote global STATE counters with phase-only 41/38 | **PASS** | `10-41-PLAN.md:152` now says the frontmatter `progress.total_plans` / `completed_plans` are project-wide cumulative values and must stay unchanged (explicitly "不得改成 41 / 38"). `10-41-PLAN.md:36` encodes the same truth. The verify at `:157` extracts both counters, fails on `STATE-BATCH-COUNTERS` (`-eq 41` / `-eq 38`), `STATE-TOTAL-PLANS-SHRANK` (`< 134`), `STATE-COMPLETED-PLANS-SHRANK` (`< 126`), and `STATE-COUNTERS-MISSING`; `fails_when` lists them (`:158`). This actually catches a regression, not just a text match. |
|
||||
| 4 | 10-35 `finalize` semantics were reversed | **PASS** | `10-35-PLAN.md:87` now says finalize runs when "**指令切换或录像结束时**", explicitly forbids "连续步骤之间". This matches `sandbox.ts:196` (command change) and `sandbox.ts:162-164` (end) / research `:264`. The must_haves truth at `10-35-PLAN.md:21` matches. |
|
||||
| 5 | 10-38 internal sections were second, not late | **PASS** | Core-state order is now 系统模型 → 入口的职责 → 加载管线 → 存档挂载 → 在游戏内另建 → **内部实现:装配顺序** → 边界 (`10-38-PLAN.md:94`). Plugin-internals order is 系统模型 → 三个阶段 → 取回 → 失败 → **内部实现:依赖排序** → 状态机 → 边界 (`:116`). Both internal sections are second-to-last, satisfying D-69 and the must_haves. Verify section checks are substring-based, so reordering does not break them. |
|
||||
| 6 | 10-39 anchor `#收集` did not match the heading | **PASS** | `10-39-PLAN.md:121` now uses `./event-system.md#收集这一次该执行谁` and explicitly says the heading is frozen and the anchor must be written per the full heading, not `#收集`. `10-36-PLAN.md` freezes `## 收集:这一次该执行谁`. VitePress slug (colon removed) matches the new anchor. |
|
||||
| 7 | 10-41 omitted config.ts from `files_modified` | **PASS** | `10-41-PLAN.md:20` now lists `docs/.vitepress/config.ts` in `files_modified`; `:43` lists it in artifacts as conditional; `:153` action (8) authorizes the edit only if the user requests display-name/order changes and otherwise says leave it untouched and note "config 未改". |
|
||||
| 8 | 10-40 persona gate broke on blockquote/wrapped text | **PASS** | Both persona checks now normalize with `$flat=($raw -replace '>','') -replace '\s',''` (`10-40-PLAN.md:112` Task 1, `:137` Task 3), stripping blockquote markers and whitespace before `Contains($persona)`. Wrapped/blockquoted records no longer break the gate. |
|
||||
| 9 | Research Open Questions were not marked resolved | **PASS (by citation)** | `10-33-PLAN.md:93-99` adds a Q1–Q6 disposition block ("研究开放问题处置:Q1–Q6 统一口径记录在本段;research 文件本身不修改,以本表为准"), covering directory/file names, I move, anon-tokyo independence, plugin-internals independence, renderer-section depth, and C1–C8. This satisfies the accepted "citation in 10-33" route. The research file itself still shows `## Open Questions`, but the plan-side disposition is explicit. |
|
||||
| 10 | New blockers / structure / D-12 regressions | **PASS** | `verify.plan-structure` = valid for all 9 plans (10-33 keeps only the pre-existing R4 `git` heuristic warning). Waves/deps unchanged and valid (5→13). All plans have `autonomous: false`, `estimate`, and a Task 0 `checkpoint:human-verify` containing "停下等待用户明确回复「可以执行」四个字". SUMMARY references/outputs intact. Task counts unchanged (3/3/3/4/3/4/4/4/5). |
|
||||
|
||||
## Residual advisories (INFO only — do not block execution)
|
||||
|
||||
1. **10-35 verify only checks `finalize` presence, not the corrected semantics.** The action (`10-35-PLAN.md:87`) is correct, but the page gate does not assert "指令切换或录像结束". A regression in the page wording would not be caught by the automated gate. Optional: add a substring assertion for the corrected semantics.
|
||||
2. **10-41 Task 4 `<files>` omits `docs/.vitepress/config.ts`.** The plan-level `files_modified` includes it (`10-41-PLAN.md:20`) and action (8) authorizes the conditional edit (`:153`), but the task-level `<files>` line (`:148`) does not list it. Scope is still governed by `files_modified`; optional cleanup for consistency.
|
||||
3. **10-41 Task 4 step (4) says "确认标记与用户原文" without repeating "下一行".** The format is fixed by the must_haves (`:32`) and acceptance (`:155`), and the Task 1/3 actions (`:108`, `:135`) explicitly put the quote on the next line; the verify's `NO-USER-QUOTE-NEXT-LINE` (`:157`) enforces it. Optional wording cleanup.
|
||||
|
||||
## Re-check recommendation
|
||||
|
||||
**PASS.** All 3 blockers and 6 warnings are fixed; no new blockers were introduced. The three residual items are advisory only and do not require a revision before execution. No plan files, docs, or source were modified by this re-check.
|
||||
|
||||
877
.planning/phases/10-docs-restructure/10-ADVANCED-III-RESEARCH.md
Normal file
877
.planning/phases/10-docs-restructure/10-ADVANCED-III-RESEARCH.md
Normal file
@ -0,0 +1,877 @@
|
||||
# Phase 10: 高级 III「原理与架构」数据端文档 - Research
|
||||
|
||||
**Researched:** 2026-10-26
|
||||
**Domain:** 数据端加载、录像、事件、通行性/寻路、CoreState 组装、插件内部机制(纯文档产出)
|
||||
**Confidence:** HIGH(源码逐文件阅读;页面切分与渲染端稳定性判断为 MEDIUM)
|
||||
|
||||
<user_constraints>
|
||||
## User Constraints (from CONTEXT.md)
|
||||
|
||||
> 本批只涉及 D-53..D-83 中与高级 III 数据端文档相关的决策。D-01..D-52 仍然有效,但不在此重复;本文件所有写作建议均须同时满足它们(尤其 D-45 prettier、D-46 两阶段修订、D-59 库文档风格、D-60 简洁优先、D-61 边界单列、D-62/D-70 代码块完整、D-65 注释充分、D-69 内部实现章节后置、D-72 注释优先、D-73 假定编辑器已完成、D-74 章节锚点、D-80 不写源码叙述、D-81 不把用户问题写进文档)。
|
||||
|
||||
### Locked Decisions
|
||||
|
||||
- **D-53:** 高级教程分层。高级教程本身按**难度/深度**(而非领域)分为 **I / II / III** 三层(避免与顶层「初级/中级/高级教程」撞名):
|
||||
- **I(基础扩展)**:较低门槛,核心为插件系统与基本扩展机制;
|
||||
- **II(深入定制)**:中等门槛,需要理解并修改引擎的特定系统(主题不固定,可能落数据端也可能落渲染端);
|
||||
- **III(原理与架构)**:最高门槛,系统性讲解设计原理与架构。
|
||||
由于渲染端尚未完工,**当前只规划/编写数据端内容**。分层是**难度标签,不是领域分类**;具体主题可能在层间调整。
|
||||
- **D-54:** 高级教程定位。文档面向**使用者**(不是引擎编写者):在适度解析的同时,必须告诉读者**怎么用、怎么改、怎么自定义**;仍沿用 D-48(实操六步)、D-49(改内置优先/类示例)、D-50(通用模板单独成节)、D-51(落点/副作用/接口讲解方式)、D-52(加粗边界)等规范。
|
||||
- **D-55:** 优先插件。明确建议:**新增系统优先通过插件系统实现**;只有不得不修改内置系统时才去改内部。理由:版本迁移时 `src` 基本不会改,但内置包(`packages` / `packages-user`)有可能会改,改内置会增加升级时的维护成本。
|
||||
- **D-56:** 高级 I 内容,7 篇。
|
||||
1. **高级 I 导览**:I/II/III 分层、阅读路径与前置、D-55 原则;
|
||||
2. **声明式 vs 命令式接口**:旧样板命令式(写一行即生效)vs 新样板声明式(告诉系统要做什么,系统在合适时机执行);必须讲透,作为后续基础;
|
||||
3. **插件系统**:自定义新插件、插件间调用、`dependencies` 依赖与排序、生命周期进阶、插件 id 稳定性与版本迁移;
|
||||
4. **可存档内容(`ISaveableContent`)**:存档一致性、三级压缩、注册存档对象、存档顺序 / `afterLoad` / `bindSaveableExecuter`、跨对象引用、结构化约束(structured-cloneable:Object/Array/number/string/boolean/null/Map/Set/TypedArray/ArrayBuffer 等)、必须返回经 `structuredClone` 的对象;
|
||||
5. **录像一致性**:`@shouldReplay` / `@ignoreReplay` 该写在什么方法/函数上、能保证什么、不能保证什么;自定义录像行为 → II,深层原理 → III;
|
||||
6. **data-common 的 Store**:讲清各 Store 存了什么,明确是**内容层原始数据**、运行时一般**只读**,修改很可能绕过存档/录像/一致性而引发问题;
|
||||
7. **钩子深入**:多对象场景(地图与图层)、如何判断「触发者是不是我关心的 map/layer」(身份校验)、同一钩子被多个对象复用、注册/移除与异步钩子。
|
||||
每篇仍保留「怎么验证」。
|
||||
- **D-57:** 高级 I 读者修订循环。高级 I 完成后,**必须**再走用户主导的修订循环(D-46 第 2 阶段);且这次的读者人格**不能复用中级教程的「不懂代码的游戏作者」**——应面向**对代码有一定了解、了解基本语法(变量/函数/类/import 等),但代码能力一般、不熟悉引擎内部架构**的作者(既不是完全不懂代码,也不预设过高的代码水平;代码能力一般的作者同样需要高级定制内容)。**高级 I 用较低标准,高级 II / III 可适当提高标准。**具体人格在计划中明确,并经用户确认。
|
||||
- **D-58:** **D-49/D-50 的「改内置还是自己新增」取舍与「必须提供通用模板章节」只适用于中级教程。** 高级教程**不强制**提供模板章节:是否提供视文档内容而定(有些可以加,有些不用加)。高级教程的取舍原则仍是 D-55(优先插件、必要时才改内置);不要求逐字写「不懂代码就改内置」这类中级提示。
|
||||
- **D-59:** **高级教程写作风格(修订 D-48 在高级教程中的适用):** 高级教程按**一般库文档**的方式写(但读者门槛略低于一般库文档,需要多解释概念),**不再使用中级教程的「复制什么 / 改什么」六步实操**,也不要把读者当成什么都不懂。
|
||||
- 提供**示例代码 + 注释解释**即可:先说明要做什么,再给出完整示例(含解释性注释),最后说明如何在需要的地方加载/接入(注释标明写在哪里)。
|
||||
- 保留必要的概念解释与验证,但用叙述方式,不要步骤化说教。
|
||||
- 不写「如果你不怎么懂代码」这类提示(与 D-58 一致)。
|
||||
- 适用于高级 I / II / III。
|
||||
- **D-60(简洁优先,长期规范):** **文档不是讲得越多越好。** 所有文档的核心目的是让读者知道「怎么用接口」;理解原理也只是为了更好地用。判断标准:
|
||||
- 能删的就删,避免面面俱到;
|
||||
- 不重复其他教程(如中级已讲过的内容)已经讲清的内容;
|
||||
- 对「几乎不可能需要自定义」的内容(如 Store),只讲内置接口怎么用,不展开「自己写」与验证;
|
||||
- 高级教程尤其要克制:按库文档方式,只保留读者真正需要的说明与示例。
|
||||
适用于后续所有文档写作。
|
||||
- **D-61(主干与边界分离,长期规范):** 文档的**主干只讲最核心的内容**;**边界情况、特殊情况不要混入主干**,应在文末**单独开一节**(如「边界与注意」)集中说明。适用于后续所有文档写作。
|
||||
- **D-62(代码块结构完整,长期规范):** 文档中的代码块必须是**完整、结构正确的片段**(完整的类 / 函数 / 文件级片段),能让语法高亮正常解析;**不要单独写某个类成员或脱离上下文的片段**。必要时用注释省略无关部分,但仍需保留结构完整。适用于后续所有文档写作。
|
||||
- **D-63(模板的组织顺序,长期规范):** 模板**不要放在文档最开头**。正确顺序是:先讲**理念/概念**(要解决什么问题、可以做哪些扩展)→ 再给引导语(「如果想……,可参考下述模板」)→ 模板代码 → 解释模板做了什么、声明了什么类、实现了什么接口、需要自己改什么。适用于后续所有文档写作。
|
||||
- **D-64(模板 vs 纯接口,长期规范):** 只有**需要读者自己实现接口或注册内容**时,才提供完整模板;**纯接口使用**(如 Store)只给示例,不给模板。适用于后续所有文档写作。
|
||||
- **D-65(示例注释,长期规范):** 示例代码**必须加解释性注释**;注释**越多越好**,以降低读者的理解难度。适用于后续所有文档写作。
|
||||
- **D-66(杂类功能的示例分开写,长期规范):** 对内容较杂的功能(如多个 Store 并列),**示例必须按功能分开写**,一个功能一段示例,**不要合并成一大块**;示例应与它所讲的功能**直接对应**。适用于后续所有教程文档的编写。
|
||||
- **D-67(高级 II 内容,暂定):** 高级 II 定位为「**深入定制**」(难度层,非领域分类)。**本规划为暂定**,后续可能调整(例如发现高级 I 仍有遗漏内容时),当前先完成高级 II。高级 II 也需要一篇**导览**。拟含 8 个主题 + 导览:
|
||||
1. **高级 II 导览**:本层难度定位、前置(高级 I + 中级相关篇目)、阅读路径;
|
||||
2. **自定义范围对象**:`IRange` / 范围参数 / 绑定宿主;被光环、地图伤害等复用;
|
||||
3. **静态图块与动态图块**:两者区别与转换;**专门讲点事件与图块事件**;
|
||||
4. **勇士的装备系统**:装备实例仓、修饰器、排序器、`compareEquip`、装备事件;
|
||||
5. **自定义录像指令**:指令码、`IReplayCommand`、主动/被动、注册与沙盒播放;
|
||||
6. **自动寻路**:只讲流程(构建图 → Dijkstra 求路径 → 交给移动器),不讲算法细节;
|
||||
7. **新增勇士属性**:修改 `IHeroAttr`;示例用**攻速**(每回合造成 `atkSpeed%` 伤害)与**护甲**(战斗百分比减伤);接入修饰器/伤害计算器;编辑器新增项、状态栏显示等按 D-47 **留空**;
|
||||
8. **新增怪物属性**:修改 `IEnemyAttr`;同样用攻速/护甲举例;接入战斗/光环/查询;编辑器新增项、怪物手册(图鉴)修改按 D-47 **留空**;
|
||||
9. **怪物上下文与战斗深入**:buildup 管线、优先级、缓存/脏标记、伤害系统与光环/查询/最终的交互。
|
||||
推荐顺序:导览 → 范围对象 → 图块(含点事件/图块事件)→ 装备 → 录像指令 → 寻路 → 勇士属性 → 怪物属性 → 怪物上下文与战斗。仍以数据端为主;渲染端与编辑器内容按 D-47 占位。
|
||||
- **D-68(高级 II 读者水平):** 不要给高级 II 读者预设过高的代码水平。目标读者应设定为:**已经理解了绝大多数常用语法;认真阅读样板后,能把一部分简单系统和中等复杂度的系统读明白**。审阅人格与文档难度均按此标准,而不是按「资深引擎开发者」预设。
|
||||
- **D-69(纯内部实现章节,长期规范):** 讲解**纯内部实现**的章节,开头必须写明「此章节讲解一下 XXX 的内部实现」;这类章节应放在文档**靠后**位置,文档前面以「如何使用」为主。适用于后续所有文档写作。
|
||||
- **D-70(代码块完整类上下文,长期规范):** 代码块必须包裹**完整的类上下文**(尤其是类的方法),确保语法高亮正确;不要单独展示脱离类的方法体。适用于后续所有文档写作。
|
||||
- **D-71(高级 I 待补内容):** 高级 I 还有未讲内容,待后续补写:**移动 Mover**、**FaceHandler 系列接口**。当前先完成高级 II,之后再补。
|
||||
- **D-72(注释优先,长期规范):** 当引擎源码的**注释与实现不一致**时,文档**一律以方法注释为准**,并在对话中**向用户报告**该不一致。不得按实现去改写注释的语义。**该不一致只在对话中报告,不写进文档正文。** 适用于后续所有文档写作。
|
||||
- **D-73(假定编辑器已完成,长期规范):** 文档**假定编辑器已经完成**。涉及编辑器 / `.jsonc` 内容配置的地方,只写「需要编辑器」的占位标识;**不要讲「编辑器未就位时改文件、就位后用编辑器」**这类过渡做法。所有 `.jsonc` 均由编辑器负责。适用于后续所有文档写作。
|
||||
- **D-74(章节锚点链接,长期规范):** 引用其他文档的**具体章节**时,使用 `./xxx.md#标题` 直接定位到该标题(如 `./static-vs-dynamic-tiles.md#双向转换`)。适用于后续所有文档写作。**待办:** 高级 II 完成后,回头检查高级 I 与中级教程是否存在同类未定位的链接。
|
||||
- **D-75(高级 I 补写主题):** 高级 I 需补两篇数据端文档,接入 `docs/advanced/` 与高级 I 侧边栏:
|
||||
1. **移动 Mover**(`mover.md`):`IObjectMover` / `IObjectMovable` / `IMoveController`、移动计划与执行、异步移动、`step` / `forward` / `backward` / `tp` / `face` / `jump` / `speed` / `animDir`、`IObjectMoverHooks` 钩子;讲怎么用、怎么改/扩展。
|
||||
2. **FaceHandler 系列**(`face-handler.md`):`FaceDirection` / `FaceGroup`、`IFaceHandler` / `IFaceDescriptor`、`Dir4FaceHandler` / `Dir8FaceHandler`、`degrade` / `next` / `opposite` / `mapMovement`、`IRoleFaceBinder`;讲怎么用、怎么改/扩展。
|
||||
两篇按高级 I 读者水平(D-57:了解基本语法、代码能力一般)与库文档风格(D-59..D-74)编写;渲染端 / 编辑器内容按 D-47 占位。补写完成后同样走独立读者审阅 + 用户修订循环(D-46 / D-57)。
|
||||
- **D-76(面朝朝向 vs 移动朝向):** 文档必须明确区分**面朝朝向(`faceDirection`)**与**移动朝向(`moveDirection`)**。`IObjectMover.forward` 的「沿当前移动方向」指的是**面朝朝向**走(不是 `moveDirection`);`onSetMoveDir` 的「设置朝向」指设置**移动朝向**。源码实现正确,不得把此处注释当作 bug。
|
||||
- **D-77(文档重心由内容性质决定):** 文档重心**不是一律讲「怎么用」**,而由所讲内容的性质决定:
|
||||
- 样板**已提供、只需使用**的内容 → 重点讲**怎么用**(自定义概率极低,最多一两节带过);
|
||||
- 内容本身就是**靠自定义 / 扩展**实现的 → 重点讲**怎么自定义**。
|
||||
二者不冲突。写作后自问:**这篇文档每个章节被使用的概率有多大?** 通篇低概率说明写得不好(除非文档本身就是讲底层自定义)。**若无法判断重心,先问用户,不得自行决定。**
|
||||
适用于后续所有文档。
|
||||
- **D-78(审阅须给出章节使用概率):** 独立读者审阅时**必须为每个章节给出「被使用的概率」**(高/中/低,并简述理由),让修订者知道哪些重要、哪些可以删减或后置。适用于后续所有读者审阅。
|
||||
- **D-79(不写使用概率元信息):** **不要在文档正文写「本节使用概率很低」之类的元信息**。但若某章看起来很有用、实际根本用不到,可以适当提示。适用于后续所有文档写作。
|
||||
- **D-80(不写源码叙述):** **不要在文档里写「源码怎么怎么样」**。文档是给人看的,源码级叙述对读者无用。适用于后续所有文档写作。
|
||||
- **D-81(不把用户问题写进文档):** 用户提出的是**问题**;**不要把问题本身或「为了解决该问题所以这样写」写进文档**。文档面向读者,不是写给用户看的复盘。适用于后续所有文档写作。
|
||||
- **D-82(先讲系统模型):** 讲一个系统时,**先讲清它的模型是什么**(概念、组成、数据流),再讲接口与用法;不要一上来就讲怎么获取、怎么调用。适用于后续所有系统类文档。
|
||||
- **D-83(高级教程不写模板/需改标记):** 高级教程中**不再写「模板」「[需改]」等标记或模板章节名**;示例代码只需用**注释说明下一行在做什么**。注意区分:游戏数据领域的「怪物模板 / 原始模板 / 默认模板」等术语不受此限。适用于高级 I / II / III。
|
||||
|
||||
### the agent's Discretion
|
||||
|
||||
CONTEXT.md 未单独列出 Discretion。按 D-10/D-14,具体页面详略、示例组织、页面内小节顺序属于执行自由度;**页面清单、目录/文件名、公共命名**受 D-13 约束,必须先经用户批准。本文件给出的 8 页切分与文件名为**建议**,不是已批准事实。
|
||||
|
||||
### Deferred Ideas (OUT OF SCOPE)
|
||||
|
||||
- 初级文档、总览、Agent 参考、API 正文重建。
|
||||
- 渲染端结构性内容(渲染管线、场景、UI 内部架构)。
|
||||
- 编辑器 / `.jsonc` 内容配置、序列化事件注册、地图事件 id 绑定。
|
||||
- legacy 包与 `public/libs/*` 旧引擎作为**当前 API** 的讲解(旧录像只作为对比基线)。
|
||||
</user_constraints>
|
||||
|
||||
<phase_requirements>
|
||||
## Phase Requirements
|
||||
|
||||
Phase 10 没有专属需求 ID;本批以 `10-CONTEXT.md` D-53..D-83 为基线,验收锚在 D-09(人工审阅 + 链接有效 + 构建通过)。
|
||||
|
||||
| ID | Description | Research Support |
|
||||
|----|-------------|------------------|
|
||||
| D-53 | 高级 III 定位为「原理与架构」,只写数据端 | 本文件按数据端 L0–L3 组织 8 页;渲染端只留边界说明 |
|
||||
| D-61 | 边界与注意单列文末 | 每页 facts 均列「文末边界」候选 |
|
||||
| D-77 | 按内容性质决定讲「怎么用」还是「怎么自定义」 | 见「Use vs Customize」矩阵 |
|
||||
| D-82 | 先讲系统模型,再讲接口 | 见「System Models」各节 |
|
||||
| D-09 | 人工审阅 + 链接有效 + 构建通过 | 见「Build/verification commands」与「Validation Architecture」 |
|
||||
</phase_requirements>
|
||||
|
||||
## Summary
|
||||
|
||||
本批是**纯文档产出**:在 `docs/advanced/iii/` 下新增 1 篇导览 + 7 篇数据端原理文档,并把它们注册进 VitePress 的 `/advanced/` 侧边栏。写作对象是**数据端六个系统**:加载(`@motajs/loader` + `MotaDataLoader`)、录像底层(`ReplayArray` / `ReplaySystem` / `ReplaySandbox`)、事件(`GameEventSystem` / `EventExecutor` / `GameEventStore`)、Anon-Tokyo 解释器、通行性谓词与寻路、`CoreState` 组装与插件内部机制。所有事实均来自本会话对源码的直接阅读,未依赖训练记忆。
|
||||
|
||||
**Primary recommendation:** 8 页全部独立成篇,但每篇严格限定「模型 + 关键接口 + 一个可扩展点 + 文末边界」;`anon-tokyo` 与 `event-system`、`plugin-internals` 与 `core-state` 是两组天然相邻页,写作时必须用「本篇只讲 X,Y 见某页」划清边界,否则会重复高级 I/II 已讲内容。渲染端加载只写一个**接口级小节**(`src/client/ui/load.tsx` 如何消费同一个 loader),不写渲染管线;旧录像只作为**对比基线**,不写成当前 API。
|
||||
|
||||
### Stability classification(FINAL vs unstable)
|
||||
|
||||
| 范围 | 判定 | 依据 |
|
||||
|------|------|------|
|
||||
| `@motajs/loader`(LoadTask / LoadManager / starter / types) | FINAL,可写 | 被数据端与渲染端共用,源码完整 [VERIFIED: packages/loader/src/*] |
|
||||
| `MotaDataLoader` / `PrefixedJSONCProcessor` / `DefaultDataLoaderHook` | FINAL,可写 | 当前 `CoreState` 与 `src/data` 实际使用 [VERIFIED: packages-user/data-state/src/loader/*; src/data/loader/hook.ts] |
|
||||
| `ReplayArray` / `ReplaySystem` / `ReplaySandbox` | FINAL,可写 | 当前存档/播放实际使用;格式注释完整 [VERIFIED: packages-user/data-common/src/replay/*] |
|
||||
| `GameEventStore` / `GameEvent` / `GameEventSystem` / `EventExecutor` | FINAL,可写 | 当前事件派发实际使用 [VERIFIED: packages-user/data-common/src/{event,store}/*; packages-user/data-system/src/event/*] |
|
||||
| `AnonTokyoInterpreter` 模型 | FINAL,可写(模型级) | 当前事件执行实际使用 [VERIFIED: packages/anon-tokyo/src/interpreter.ts] |
|
||||
| `IPassPredicate` / `MapGraphBuilder` / `PathFinder` / `PathfindingSystem` | FINAL,可写 | 当前初始化与寻路实际使用 [VERIFIED: packages-user/data-base/src/map/types.ts; packages-user/data-system/src/path/*] |
|
||||
| `CoreState` 构造顺序 / `orderPlugins` | FINAL,可写 | 当前装配实际使用 [VERIFIED: packages-user/data-state/src/core.ts; plugin.ts] |
|
||||
| 渲染端加载 UI / `ClientCore` 生命周期 | UNSTABLE,只写接口级小节 | `dev.md:136` 明确渲染端底层架构仍需重构;`ClientCore.afterLoad` 有疑点(C2) |
|
||||
| 本地数据包 FSA starter | 设计能力,不写实现 | 仓库无实现;`ILoadTaskStarter` 是抽象点 [VERIFIED: packages-user/data-state/src/types.ts:73-77] |
|
||||
| 编辑器 / 序列化事件注册 / 地图事件 id 绑定 | 不在范围 | D-73 + 本批范围;只留占位/边界 |
|
||||
| `public/libs/*` 旧引擎 | 仅对比基线 | 旧录像用于解释新格式优势,不作为当前 API |
|
||||
|
||||
## Scope & Method
|
||||
|
||||
- 阅读了 `AGENTS.md`、`dev.md`、`STATE.md`、`ROADMAP.md` Phase 10、`10-CONTEXT.md`(D-53..D-83)、`10-RESEARCH.md` 的文档约定、`docs/advanced/index.md`、`docs/advanced/ii/index.md`、`docs/advanced/ii/pathfinding.md`、`docs/advanced/ii/point-and-tile-events.md`、`docs/advanced/plugin-system.md`、`docs/advanced/saveable-content.md`、`docs/advanced/replay-consistency.md`、`docs/advanced/data-stores.md`、`docs/advanced/ii/custom-replay-command.md`、`docs/.vitepress/config.ts`。
|
||||
- 逐文件阅读了 `packages/loader/src/*`、`packages-user/data-state/src/*`、`packages-user/data-common/src/{replay,event,store}/*`、`packages-user/data-system/src/{event,path}/*`、`packages-user/data-base/src/{hero,map}/*`、`packages/anon-tokyo/src/{interpreter,type}.ts`、`src/data/**`、`src/client/{entry,initialize,ui/load}.tsx`、`packages-user/client-modules/src/{client,loader/hook}.ts`、`script/test-data-node.ts`、`public/libs/{control,core,utils,items,events,actions}.js`(旧录像基线)。
|
||||
- 未修改任何 `docs/` 或源码文件;本文件只写研究结论。
|
||||
|
||||
## Architectural Responsibility Map
|
||||
|
||||
| Capability | Primary Tier | Secondary Tier | Rationale |
|
||||
|------------|-------------|----------------|-----------|
|
||||
| 加载任务与进度聚合 | 核心引擎 `@motajs/loader`(LoadTask / LoadManager) | — | 与游戏无关的通用加载设施,被数据端与渲染端共用 [VERIFIED: packages/loader/src/manager.ts:10-112] |
|
||||
| 数据端加载编排 | Layer 3 `@user/data-state`(MotaDataLoader / CoreState.load) | Layer 0 配置处理器 | 决定核心配置→额外配置→自定义任务的顺序 [VERIFIED: packages-user/data-state/src/loader/loader.ts:84-142] |
|
||||
| 录像二进制存储 | Layer 0 `@user/data-common`(ReplayArray / ReplaySystem) | — | 录像格式属于公共数据,不依赖地图/战斗 [VERIFIED: packages-user/data-common/src/replay/array.ts:57-99] |
|
||||
| 录像播放沙箱 | Layer 0 `@user/data-common`(ReplaySandbox) | Layer 3 状态重置器 | 沙箱只读录像数组;重置由外部 `IStateReseter` 提供 [VERIFIED: packages-user/data-common/src/replay/sandbox.ts:55-62] |
|
||||
| 事件存储与事件对象 | Layer 0 `@user/data-common`(GameEventStore / GameEvent) | — | 事件实例与触发器属于公共数据 [VERIFIED: packages-user/data-common/src/store/eventStore.ts:5-33] |
|
||||
| 事件收集与执行 | Layer 2 `@user/data-system`(GameEventSystem / EventExecutor) | Layer 0 解释器 | 事件派发依赖地图图层与勇士位置 [VERIFIED: packages-user/data-system/src/event/system.ts:44-111] |
|
||||
| 事件脚本解释 | 核心引擎 `@motajs/anon-tokyo` | — | 与游戏无关的通用解释器,数据端只注册内建函数 [VERIFIED: packages/anon-tokyo/src/interpreter.ts:143-219] |
|
||||
| 通行性判定 | Layer 1 契约 `@user/data-base`(IPassPredicate) | 实现 `src/data/hero/predicate.ts` | 契约属于数据层;默认实现属于游戏内容 [VERIFIED: packages-user/data-base/src/map/types.ts:867-890] |
|
||||
| 图构建与路径搜索 | Layer 2 `@user/data-system`(MapGraphBuilder / PathFinder) | Layer 1 图层数据 | 构图与搜索是系统层动作 [VERIFIED: packages-user/data-system/src/path/graph.ts:17-151] |
|
||||
| CoreState 组装 | Layer 3 `@user/data-state` | Layer 0/1/2 构造器 | 顶层只负责装配与生命周期 [VERIFIED: packages-user/data-state/src/core.ts:109-156] |
|
||||
| 插件依赖排序 | Layer 3 `@user/data-state`(orderPlugins) | — | 纯函数拓扑排序,构造期调用一次 [VERIFIED: packages-user/data-state/src/plugin.ts:20-105] |
|
||||
|
||||
## Standard Stack
|
||||
|
||||
本批不安装任何新依赖;只使用仓库既有 VitePress 文档工具链。
|
||||
|
||||
### Core
|
||||
|
||||
| Library | Version (package.json range) | Purpose | Why Standard |
|
||||
|---------|------------------------------|---------|--------------|
|
||||
| `vitepress` | `^1.6.4` | 站点生成 | 仓库既有站点载体 [VERIFIED: package.json:92-104] |
|
||||
| `vitepress-plugin-mermaid` | `^2.0.17` | mermaid 图 | 已启用 [VERIFIED: docs/.vitepress/config.ts:2,12-14] |
|
||||
| `mermaid` | `^11.12.3` | mermaid 运行时 | 插件依赖 [VERIFIED: package.json:92-104] |
|
||||
| `prettier` | `^3.8.1` | markdown 格式化 | D-45 要求 [VERIFIED: package.json:92-104] |
|
||||
|
||||
### Supporting
|
||||
|
||||
| Library | Version | Purpose | When to Use |
|
||||
|---------|---------|---------|-------------|
|
||||
| VitePress 容器 | 随 VitePress | `:::info` / `:::warning` | 提示与注意事项 |
|
||||
| `tsx` | `^4.21.0` | 运行 `docs/.vitepress/init.ts` | 构建前生成 `apiSidebar.ts` |
|
||||
|
||||
**Installation:** 无。工具链已在 `devDependencies` 中。
|
||||
|
||||
## Package Legitimacy Audit
|
||||
|
||||
本批**不安装外部包**,因此不触发安装前门禁;无需新增审计表。既有文档工具链的审计结论见 `10-RESEARCH.md` 的 Package Legitimacy Audit。
|
||||
|
||||
## System Models
|
||||
|
||||
### 1. 加载系统(含渲染端加载)
|
||||
|
||||
**模型(先讲这个):** 一次加载由三层组成:`LoadTask`(一个资源)→ `LoadManager`(聚合任务与字节/任务进度)→ `MotaDataLoader`(按阶段编排配置与自定义任务)。`ILoadTaskStarter` 是环境适配点:网页端用 `WebLoadStarter` 的 `fetch`,Node 端或其他环境可替换为自定义 starter。`MotaDataLoader.start()` 的顺序是:核心配置 → `onCoreConfigLoaded` → 额外配置 → `onExtraConfigLoaded` → 自定义任务 → 全部完成。`CoreState.load()` 把插件 `initialize` 插在 `loader.start()` 之前,把插件 `afterLoad` 插在之后。
|
||||
|
||||
**关键接口/类:**
|
||||
|
||||
| 名称 | 位置 | 职责 |
|
||||
|------|------|------|
|
||||
| `LoadTask<T,R>` | `packages/loader/src/task.ts:24-253` | 单任务:starter → Response → 流式/非流式 → processor → R |
|
||||
| `LoadManager` | `packages/loader/src/manager.ts:10-112` | 任务集合、总进度、`load()` 异步迭代 |
|
||||
| `ILoadTaskStarter` | `packages/loader/src/types.ts:186-192` | `start(request): Promise<IResponseLike>` |
|
||||
| `WebLoadStarter` | `packages/loader/src/starter.ts:3-10` | `fetch` 实现 |
|
||||
| `LoadDataType` | `packages/loader/src/types.ts:82-88` | `ArrayBuffer` / `Uint8Array` / `Blob` / `Text` / `JSON` |
|
||||
| `ILoadTaskProcessor` | `packages/loader/src/types.ts:121-128` | `process(response, task): Promise<R>` |
|
||||
| `MotaDataLoader` | `packages-user/data-state/src/loader/loader.ts:18-151` | 数据端加载编排 |
|
||||
| `IMotaDataLoader` | `packages-user/data-state/src/loader/types.ts:19-72` | `addCoreConfig` / `addExtraConfig` / `addCustomTask` / `getConfig` |
|
||||
| `PrefixedJSONCProcessor` | `packages-user/data-state/src/loader/jsoncProcessor.ts:4-28` | 按前缀切分后 `JSON.parse` |
|
||||
| `DefaultDataLoaderHook` | `src/data/loader/hook.ts:7-29` | 从 `core.jsonc` 添加敌人/道具/地图/图块配置 |
|
||||
| `RenderLoaderHooks` | `packages-user/client-modules/src/loader/hook.ts:5-25` | 添加贴图/音频/动画/字体配置 |
|
||||
|
||||
**数据流:**
|
||||
|
||||
```text
|
||||
CoreState.load()
|
||||
├─ plugin.initialize() (数据端插件,加载前)
|
||||
├─ loader.start()
|
||||
│ ├─ addCoreConfig 的任务(core.jsonc)
|
||||
│ ├─ manager.load() → onCoreConfigLoaded(loader)
|
||||
│ ├─ addExtraConfig 的任务(content/*.jsonc)
|
||||
│ ├─ manager.load() → onExtraConfigLoaded(loader)
|
||||
│ ├─ addCustomTask 的任务
|
||||
│ └─ manager.load() → loaded() 兑现
|
||||
└─ plugin.afterLoad() (数据端插件,加载后)
|
||||
```
|
||||
|
||||
[VERIFIED: packages-user/data-state/src/core.ts:274-283; packages-user/data-state/src/loader/loader.ts:84-142]
|
||||
|
||||
**路径解析:** `resolveURL` 在 `import.meta.env.DEV` 时前置 `src/`,构建后不前置 [VERIFIED: packages-user/data-state/src/loader/loader.ts:74-82]。
|
||||
|
||||
**自定义加载项/资源(recipe):**
|
||||
1. 新配置文件:在 `onCoreConfigLoaded` 或加载前调用 `loader.addExtraConfig(id, url)`;`url` 相对于 `src/content`,文件必须是带系统前缀的 JSONC [VERIFIED: packages-user/data-state/src/loader/types.ts:46-57]。
|
||||
2. 新资源类型:`new LoadTask<T,R>({ identifier, url, dataType })`,`setProcessor(processor)`,`setStarter(starter)`,再 `loader.addCustomTask(task)`;返回的 Promise 在任务完成后兑现 [VERIFIED: packages-user/data-state/src/loader/loader.ts:65-68; packages/loader/src/task.ts:50-77]。
|
||||
3. 新环境:实现 `ILoadTaskStarter.start(request)`,返回 `IResponseLike`;把它传给 `createCoreState(starter)` / `createClientCore(starter)` [VERIFIED: packages-user/data-state/src/types.ts:72-84; src/data/entry.ts:28-32]。
|
||||
4. 本地数据包(File System Access API):只作为**设计能力**说明——`ILoadTaskStarter` 把「取响应」抽象出来,换一个 starter 即可从本地目录/文件读取;仓库内没有实现,不写代码 [VERIFIED: packages-user/data-state/src/types.ts:73-77; packages/loader/src/starter.ts:3-10]。
|
||||
|
||||
**渲染端加载:** 包含一个**接口级小节**即可,不单独成页。`src/bootstrap.ts:12-16` 用 `WebLoadStarter` 创建 `ClientCore` 并打开 `LoadSceneUI`;`LoadScene` 直接迭代 `props.state.loader.start()`,用 `loader.manager.getLoadedTasks()` / `getAddedTasks()` / `getLoadedByte()` / `getTotalByte()` / `getByteRatio()` 画进度 [VERIFIED: src/client/ui/load.tsx:52-174]。`RenderLoaderHooks` 追加渲染端配置 [VERIFIED: packages-user/client-modules/src/loader/hook.ts:5-25]。渲染端底层架构仍在重构(`dev.md:136`),且 `ClientCore.afterLoad` 存在实现疑点(见「Source/comment conflicts」),所以**只写 loader 接口消费,不写场景/UI 生命周期**。
|
||||
|
||||
### 2. 录像系统底层
|
||||
|
||||
**模型:** `ReplaySystem` 持有一个 `ReplayArray`(二进制录像)与一个可选的 `ReplaySandbox`(播放器)。录制时 `record(code, ...params)` 把一步追加到数组;播放时沙箱用 `createReadStream()` 顺序读,主动步直接执行,被动步由外部 `getPassive()` 取参数并在完成后 `next(status)`。存档时 `saveState()` 返回 `{ length, commandWidth, commandArray, paramArray }`,读档时 `loadState()` 用 `setReplayArray()` 重建索引。
|
||||
|
||||
**关键接口/类:**
|
||||
|
||||
| 名称 | 位置 | 职责 |
|
||||
|------|------|------|
|
||||
| `ReplayArray` | `packages-user/data-common/src/replay/array.ts:57-900` | 二进制存储、索引、读写流、编辑 |
|
||||
| `ReplaySystem` | `packages-user/data-common/src/replay/system.ts:21-114` | 注册指令、记录、沙箱、禁用、存档 |
|
||||
| `ReplaySandbox` | `packages-user/data-common/src/replay/sandbox.ts:29-315` | 播放循环、主动/被动步、暂停/恢复/单步 |
|
||||
| `IReplayCommand` | `packages-user/data-common/src/replay/types.ts:62-78` | `type` + `execute(step)` + 可选 `finalize()` |
|
||||
| `ReplayCommandType` | `packages-user/data-common/src/replay/types.ts:24-29` | `Active` / `Passive` |
|
||||
| `ReplayCommandResult` | `packages-user/data-common/src/replay/types.ts:31-38` | `Success` / `Failed` / `Ignored` |
|
||||
| `ReplayParamValue` | `packages-user/data-common/src/replay/types.ts:40` | `number \| string \| boolean \| bigint` |
|
||||
| `IReplaySystemSave` | `packages-user/data-common/src/replay/types.ts:373-405` | 存档形状 |
|
||||
| `IReplaySandbox` | `packages-user/data-common/src/replay/types.ts:114-166` | 播放控制与 `getPassive()` |
|
||||
| `IStateReseter` | `packages-user/data-common/src/replay/types.ts:168-174` | 播放前重置游戏状态 |
|
||||
|
||||
**二进制布局(精确事实):**
|
||||
- 指令缓冲区:每条指令 `commandSize` 字节;`Uint8` 宽度为 2 字节(参数数量 1 字节 + 指令 id 1 字节),`Uint16` 宽度为 3 字节(参数数量 1 字节 + 指令 id 2 字节)[VERIFIED: packages-user/data-common/src/replay/array.ts:143-149,345-356; packages-user/data-common/src/replay/types.ts:379-383]。
|
||||
- 参数缓冲区:每个参数 = 1 字节类型 + 载荷;类型码与长度:`0 boolean` 2B、`1 int8` 2B、`2 int16` 3B、`3 int32` 5B、`4 非负 int64` 9B、`5 负 int64` 9B、`6 float` 9B、`7 非负 bigint` n+2B、`8 负 bigint` n+2B、`9 string` n+5B、`32..255` 短字符串 n+1B [VERIFIED: packages-user/data-common/src/replay/array.ts:11-34,227-309,363-409; packages-user/data-common/src/replay/types.ts:286-302]。
|
||||
- int64:按幅值拆高低 32 位,`high = floor(magnitude / 2147483648)`,`low = magnitude - high * 2147483648`;解码 `low + high * 2147483648`,负类型取负 [VERIFIED: packages-user/data-common/src/replay/array.ts:383-389,687-698]。
|
||||
- bigint:按幅值小端字节写入,长度 1 字节;幅值必须 `< 2^2040`,否则告警 152 并丢弃 [VERIFIED: packages-user/data-common/src/replay/array.ts:270-288,703-712]。
|
||||
- 索引数组:`Uint32Array`,每个指令记录参数起始字节;`add` 写 `indexArray[length] = paramUsed` [VERIFIED: packages-user/data-common/src/replay/array.ts:70-84,438,828-846]。
|
||||
- 初始容量:1000 条指令、10000 参数字节;扩容乘数 1.2;上限 1,000,000 条 / 10,000,000 字节 [VERIFIED: packages-user/data-common/src/replay/system.ts:34-42]。
|
||||
- 读取流:`createReadStream(startIndex)` 返回 `{ index, length, expired, read, destroy }`;任何写操作调用 `expireStreams()` 把旧流标记过期 [VERIFIED: packages-user/data-common/src/replay/array.ts:215-220,787-822]。
|
||||
- 编辑:`insert` / `delete` / `set` 会移动指令/参数缓冲区并平移索引,注释明确「耗时较长,不建议频繁调用」[VERIFIED: packages-user/data-common/src/replay/array.ts:445-584; packages-user/data-common/src/replay/types.ts:232-260]。
|
||||
- 存档:`saveState()` 直接返回 `commandArray` / `paramArray` 的内部 `ArrayBuffer` 引用,不复制 [VERIFIED: packages-user/data-common/src/replay/system.ts:97-104; packages-user/data-common/src/replay/array.ts:879-885]。读档 `setReplayArray()` 重建索引并过期所有读流 [VERIFIED: packages-user/data-common/src/replay/array.ts:848-875]。
|
||||
- 禁用:`disabled` 是嵌套计数;`disable()` +1,`revert()` -1,`clearDisableFlag()` 归零 [VERIFIED: packages-user/data-common/src/replay/array.ts:887-899; packages-user/data-common/src/replay/types.ts:323-339]。
|
||||
|
||||
**沙箱模型(精确事实):**
|
||||
- `createReplaySandbox(config)` 先 `config.reseter.reset(config.save)`,再 `new ReplaySandbox(config.route, this, config.startIndex ?? 0)`,并记录为当前沙箱 [VERIFIED: packages-user/data-common/src/replay/system.ts:70-82]。
|
||||
- 沙箱构造时创建 `route.createReadStream(startIndex)` [VERIFIED: packages-user/data-common/src/replay/sandbox.ts:55-62]。
|
||||
- 主动步:`command.execute(curr)`;被动步:`getPassive()` 返回 `{ step, next }`,外部调用 `next(status)` 后才读取下一步 [VERIFIED: packages-user/data-common/src/replay/sandbox.ts:81-110,146-224]。
|
||||
- 连续步骤的 `finalize()` 在上一步指令与下一步指令不同、或录像结束时触发 [VERIFIED: packages-user/data-common/src/replay/sandbox.ts:115-123,162-205]。
|
||||
- 控制:`play` / `pause` / `resume` / `stop` / `step` / `setSpeed`;`getReplayed()` 返回已播放步数(不含当前步)[VERIFIED: packages-user/data-common/src/replay/sandbox.ts:70-77,263-312; packages-user/data-common/src/replay/types.ts:136-165]。
|
||||
|
||||
**Use vs customize:** 使用侧重点是 `record` / `registerCommand` / `getCommand` / `createReplaySandbox` / `getPassive`;自定义侧重点是**新增指令**(`ReplayCode` + `IReplayCommand` + 注册),已在高级 II 的 `custom-replay-command.md` 讲透,本篇只链接,不重复 [CITED: docs/advanced/ii/custom-replay-command.md:18-245]。`ReplayArray` 的 insert/delete/set 属于底层编辑能力,除非做录像编辑器,否则不展开。
|
||||
|
||||
### 3. 事件系统
|
||||
|
||||
**模型:** 事件系统由「事件实例仓库」+「事件对象」+「收集器」+「执行器」+「解释器」组成。`GameEventStore` 以 id 存 `IReadonlyGameEvent`;`GameEvent` 持有 `rawEvent: Statement[]` 与 `compiled: AnonTokyoExecutable | null`;`GameEventSystem.collectEvent(layer, trigger, x, y)` 从事件层收集该坐标的点事件与图块事件,按「先点事件、后图块事件,各自优先级降序」生成 `IGameEventInvocation[]`;`EventExecutor.execute()` 逐条从 store 取事件、过滤触发器、执行并折叠返回值。事件层就是 `alias === 'event'` 的普通图层。
|
||||
|
||||
**关键接口/类:**
|
||||
|
||||
| 名称 | 位置 | 职责 |
|
||||
|------|------|------|
|
||||
| `EventTrigger` | `packages-user/data-common/src/event/types.ts:7-30` | 触发器枚举 |
|
||||
| `IReadonlyGameEvent` / `IGameEvent` | `packages-user/data-common/src/event/types.ts:32-74` | 事件对象契约 |
|
||||
| `GameEvent` | `packages-user/data-common/src/event/event.ts:8-54` | 编译/执行/设置原始数据与触发器 |
|
||||
| `GameEventStore` | `packages-user/data-common/src/store/eventStore.ts:5-33` | id → 事件实例 |
|
||||
| `IGameEventStore` | `packages-user/data-common/src/store/types.ts:291-311` | `addEvent` / `getEvent` |
|
||||
| `GameEventSystem` | `packages-user/data-system/src/event/system.ts:24-112` | 默认 store、解释器、`collectEvent` |
|
||||
| `EventExecutor` | `packages-user/data-system/src/event/executor.ts:20-114` | 执行、模式、折叠、钩子 |
|
||||
| `BlockEventType` | `packages-user/data-system/src/event/types.ts:16-23` | `CommonEvent` / `PointEvent` / `TileEvent` |
|
||||
| `IBlockEventEnv` | `packages-user/data-system/src/event/types.ts:30-49` | 事件环境字段 |
|
||||
| `IBlockEventParam` | `packages-user/data-system/src/event/types.ts:25-28` | `{ custom: Record<string, any> }` |
|
||||
| `EventExecuteMode` | `packages-user/data-system/src/event/types.ts:70-77` | `Normal` / `CutIfFalsy` / `CutIfTruthy` |
|
||||
| `EventReduceMode` | `packages-user/data-system/src/event/types.ts:79-92` | `NoReduce` / `OrReduce` / `AndReduce` |
|
||||
| `BuiltInFunction` | `packages/anon-tokyo/src/interpreter.ts:11-18` | `name` + `func(param, env)` |
|
||||
|
||||
**收集与执行(精确事实):**
|
||||
- `collectEvent` 先 `layer.inMap(x,y)`;点事件来自 `layer.getPointEvent(x,y)`,图块事件来自 `loc.static.tileEvent().get()` 与 `loc.dynamics` 的每个 `tile.tileEvent().get()` [VERIFIED: packages-user/data-system/src/event/system.ts:44-89]。
|
||||
- 点事件与图块事件各自按 `priority` 降序排序,再拼接 [VERIFIED: packages-user/data-system/src/event/system.ts:90-108]。
|
||||
- 每个 invocation 的 env 包含 `state` / `type` / `trigger` / `system` / `heroLocator` / `heroFloor` / `triggerLocator` / `tile` / `layer` / `map` [VERIFIED: packages-user/data-system/src/event/system.ts:95-107; packages-user/data-system/src/event/types.ts:30-49]。
|
||||
- `EventExecutor.execute()` 对每条 invocation:store 为空或事件不存在 → 告警 171 并跳过;`event.trigger !== invocation.env.trigger` → 跳过;否则 `await event.execute(param, env)` [VERIFIED: packages-user/data-system/src/event/executor.ts:81-99]。
|
||||
- `EventExecuteMode.CutIfFalsy` 在结果 falsy 时 break;`CutIfTruthy` 在 truthy 时 break [VERIFIED: packages-user/data-system/src/event/executor.ts:100-105]。
|
||||
- `EventReduceMode.OrReduce` / `AndReduce` 对非布尔值告警 172,并按短路规则折叠 [VERIFIED: packages-user/data-system/src/event/executor.ts:53-71]。
|
||||
- `EventExecutor.execute` 标有 `@shouldReplay('Executing event should be replayed.')` [VERIFIED: packages-user/data-system/src/event/executor.ts:73-77]。
|
||||
|
||||
**触发器(精确事实):** `EventTrigger` 的完整值是 `None` / `OnTouch` / `OnEnter` / `OnLeave` / `OnBeforeBattle` / `OnAfterBattle` / `OnBeforeOpenDoor` / `OnAfterOpenDoor` / `OnAfterGetItem` / `OnBeforeChangeFloor` / `OnAfterChangeFloor` [VERIFIED: packages-user/data-common/src/event/types.ts:7-30]。本仓库内**只有** `OnEnter` / `OnLeave` / `OnTouch` 有派发调用点,均由 `DefaultHeroMoveTopImpl` 的 `enter` / `leave` / `hit` 触发 [VERIFIED: src/data/hero/moverImpl.ts:72-82];`cannotEnter` 是空实现 [VERIFIED: src/data/hero/moverImpl.ts:87-90]。其余触发器值是枚举的一部分,但本仓库没有对应派发调用点——文档应写成「当前数据端内置派发覆盖进入/离开/触碰,其余由对应系统使用/预留」,不要写成「全部已实现」。
|
||||
|
||||
**事件层(精确事实):** `IGameMap.eventLayer` 是「当前楼层的默认事件层」,类型 `IMapLayer | null` [VERIFIED: packages-user/data-base/src/map/types.ts:668-669];`MapState.fromRaw` 在图层 `alias === 'event'` 时调用 `setEventLayer(layer)` [VERIFIED: packages-user/data-base/src/map/mapState.ts:89-91]。事件层是普通图层,具备 `getPointEvent` / `getLocationData` / `event` 等接口 [VERIFIED: packages-user/data-base/src/map/types.ts:478-600]。
|
||||
|
||||
**自定义事件内建函数(recipe):**
|
||||
1. 在 `src/data/event/*.ts` 写一个类,`implements BuiltInFunction<P, IBlockEventEnv>`,提供 `name: string` 与 `func(param, env)` [VERIFIED: src/data/event/map.ts:9-31; src/data/event/event.ts:9-21]。
|
||||
2. 参数接口写在 `src/data/event/types.ts`,例如 `ISetBlockEventParam` / `IMoveHeroEventParam` [VERIFIED: src/data/event/types.ts:1-66]。
|
||||
3. 把实例加入 `createEventRegistrations()` 返回数组;该数组最终进入 `ICoreStateConfig.events`,再由 `GameEventSystem` 构造 `AnonTokyoInterpreter` [VERIFIED: src/data/event/registrations.ts:9-24; src/data/entry.ts:15-20; packages-user/data-state/src/core.ts:130; packages-user/data-system/src/event/system.ts:33-36]。
|
||||
4. 在 `func` 中通过 `env.state` / `env.layer` / `env.map` / `env.trigger` / `env.tile` 取上下文;需要默认图层时用 `getPossibleLayer(env)` [VERIFIED: src/data/event/utils.ts:6-30]。
|
||||
5. **不要**在本篇讲序列化事件注册 / 地图事件 id 绑定——这是编辑器负责、当前未实现的部分 [CITED: 10-CONTEXT.md D-42; 10-STATE.md Phase 01 decisions]。
|
||||
|
||||
**Use vs customize:** 使用侧是 `collectEvent` + `executor.execute` + `eventStore.getEvent`;自定义侧是内建函数类 + 注册。`EventTrigger` 是 `const enum`,新增触发器值需要改 `packages-user` 并在派发点处理,属于引擎级改动,文档只说明边界,不鼓励。
|
||||
|
||||
### 4. Anon-Tokyo 解释器模型
|
||||
|
||||
**模型:** Anon-Tokyo 把「事件脚本」表示为 `Statement[]`。`AnonTokyoInterpreter` 接收 `LanguageFeature { builtInFunctions, globalFunctions }`;`compile(script)` 把语句编译成 `AnonTokyoExecutable`(内部是 `ExecutionNode[]` 程序);`exec(parameters, env)` 创建 `AnonTokyoExecutionContext`,由 `AnonTokyoIterator` 反复执行当前节点,处理 `Move` / `Call` / `Return` / `Exit` 四种操作。`GameEvent` 缓存编译结果,`setRaw` 时清空缓存。
|
||||
|
||||
**关键接口/类:**
|
||||
|
||||
| 名称 | 位置 | 职责 |
|
||||
|------|------|------|
|
||||
| `BuiltInFunction` | `packages/anon-tokyo/src/interpreter.ts:11-18` | `name` + 可选 `save` + `func(parameters, env)` |
|
||||
| `LanguageFeature` | `packages/anon-tokyo/src/interpreter.ts:20-23` | `builtInFunctions` + `globalFunctions` |
|
||||
| `AnonTokyoInterpreter` | `packages/anon-tokyo/src/interpreter.ts:143-219` | 注册内建/全局函数、`compile`、`exec` |
|
||||
| `AnonTokyoExecutable` | `packages/anon-tokyo/src/interpreter.ts:114-138` | 程序节点 + `exec` |
|
||||
| `AnonTokyoExecutionContext` | `packages/anon-tokyo/src/interpreter.ts:84-109` | 调用栈 + `env` |
|
||||
| `AnonTokyoIterator` | `packages/anon-tokyo/src/interpreter.ts:27-79` | 执行循环 |
|
||||
| `Statement` | `packages/anon-tokyo/src/type.ts:86-95` | 语句联合类型 |
|
||||
| `StatementType` | `packages/anon-tokyo/src/type.ts:1-17` | 语句种类 |
|
||||
| `Scope` | `packages/anon-tokyo/src/type.ts:210-214` | `args` / `local` / `env` |
|
||||
| `OPType` / `OP` | `packages/anon-tokyo/src/type.ts:197-208` | `Move` / `Call` / `Return` / `Exit` |
|
||||
| `ExecutionNode` | `packages/anon-tokyo/src/type.ts:216` | `(scope) => Promise<OP> \| OP` |
|
||||
|
||||
**精确事实:**
|
||||
- `StatementType` 的值是 `Expression` / `Call` / `Return` / `If` / `Switch` / `Loop` / `Break` / `Continue` / `Block` / `Exit` / `FunctionDeclare` [VERIFIED: packages/anon-tokyo/src/type.ts:1-17]。
|
||||
- `Expression = (env: Scope) => any`,`Value = Literal | Expression`,`Block = Statement[]` [VERIFIED: packages/anon-tokyo/src/type.ts:19-23]。
|
||||
- `compile(script)` 通过 `compile(script, name => this.getBuiltInFunction(name))` 生成程序;`getBuiltInFunction` / `getGlobalFunction` 找不到时直接 `throw` 字符串 [VERIFIED: packages/anon-tokyo/src/interpreter.ts:164-202]。
|
||||
- `AnonTokyoExecutionContext.callByName` 查全局函数;`call` 把 iterator 压栈、`run` 后弹栈 [VERIFIED: packages/anon-tokyo/src/interpreter.ts:92-106]。
|
||||
- `AnonTokyoIterator.next()` 根据 `OPType` 更新 `current`:`Move` 跳到下一节点,`Call` 调 `callByName`,`Return` 记返回值,`Exit` 置 -2 [VERIFIED: packages/anon-tokyo/src/interpreter.ts:38-67]。
|
||||
- `GameEvent.compile()` 调 `interpreter.compile(rawEvent)` 并缓存;`execute()` 优先用 `compiled`,否则编译,再不行直接 `interpreter.exec(rawEvent, param, env)`;`setRaw` 把 `compiled` 置 `null` [VERIFIED: packages-user/data-common/src/event/event.ts:24-49]。
|
||||
- `GameEventSystem` 构造时 `new AnonTokyoInterpreter({ builtInFunctions: [...builtins], globalFunctions: [] })` [VERIFIED: packages-user/data-system/src/event/system.ts:33-36]。
|
||||
|
||||
**Use vs customize:** 使用侧是「事件对象已经替你 compile/exec」;自定义侧是新增 `BuiltInFunction` 或提供 `globalFunctions`(当前数据端传空数组)。不要在本篇讲 `compile.ts` 的解析/优化细节;只讲「编译成可执行程序 → 迭代执行」的模型。
|
||||
|
||||
### 5. 通行性谓词与寻路原理
|
||||
|
||||
**模型:** 通行性谓词 `IPassPredicate` 是「一步能不能走、撞不撞」的唯一语义契约:`canPass(handler)` 判断能否从 `currLoc` 进入 `nextLoc`,`shouldHit(handler)` 判断这次移动是否应产生撞击。勇士移动器在 `onStepStart` 里用 `topImpl.predicate()` 调它;寻路图构建器在 `build()` 里用注入的谓词调它。**同一个契约**保证「寻路选出的路」和「勇士实际能走的路」一致;但注意它们是**两个实例**:`DefaultHeroMoveTopImpl` 自己 `new DefaultPassPredicateImpl(state)`,`MainDataPlugin.awake` 又为 finder `new DefaultPassPredicateImpl(state)`。寻路流程是「构图 → 搜索 → 交给移动器」:`MapGraphBuilder.build(start)` 从起点 BFS 只扩展可通行边,`shouldHit` 标记终端节点;`PathFinder.search()` 在图上做最小损失搜索,终端节点只能作为终点;`PathfindingSystem.moveTo/teleportTo` 把步骤交给绑定的 `IObjectMover`。
|
||||
|
||||
**关键接口/类:**
|
||||
|
||||
| 名称 | 位置 | 职责 |
|
||||
|------|------|------|
|
||||
| `IPassCheckHandler` | `packages-user/data-base/src/map/types.ts:867-876` | `currLoc` / `nextLoc` / `direction` / `map` |
|
||||
| `IPassPredicate` | `packages-user/data-base/src/map/types.ts:878-890` | `canPass` / `shouldHit` |
|
||||
| `DefaultPassPredicateImpl` | `src/data/hero/predicate.ts:6-86` | 默认实现:事件层 + 其它图层 `onlyEvents` |
|
||||
| `IHeroMoveTopImpl` | `packages-user/data-base/src/hero/types.ts:358-368` | `predicate()` 等勇士顶层行为 |
|
||||
| `MapGraphBuilder` | `packages-user/data-system/src/path/graph.ts:17-151` | 构图 |
|
||||
| `PathFinder` | `packages-user/data-system/src/path/finder.ts:96-214` | 搜索 |
|
||||
| `PathfindingSystem` | `packages-user/data-system/src/path/system.ts:21-130` | 绑定移动器、`getPath` / `moveTo` / `teleportTo` |
|
||||
| `PathfindingStatus` | `packages-user/data-system/src/path/types.ts:107-116` | `Success` / `InvalidInput` / `NoPath` / `TargetUnder` |
|
||||
|
||||
**精确事实:**
|
||||
- `DefaultPassPredicateImpl.canPass`:先取 `FaceGroup.Dir4` 处理器,`degrade(direction)`;检查事件层当前格 `outPass` 与目标格 `inPass`;再遍历 `map.layerList` 中非事件层,只对 `onlyEvents` 的图块检查通行位 [VERIFIED: src/data/hero/predicate.ts:30-74]。
|
||||
- `shouldHit`:目标格静态图块存在且 `!nextRaw.eventPass` 时返回 true [VERIFIED: src/data/hero/predicate.ts:76-84]。
|
||||
- 勇士移动:`onStepStart` 创建 `IPassCheckHandler`,`predicate.canPass` 为 false → `CannotMove`;为 true 且 `shouldHit` → `Hit`;否则 `Step` [VERIFIED: packages-user/data-base/src/hero/mover.ts:201-217]。
|
||||
- 移动结束:`CannotMove` 调 `topImpl.cannotEnter` 并停止;`Hit` 调 `topImpl.hit` 并停止;`Step` 返回 `nextLoc` [VERIFIED: packages-user/data-base/src/hero/mover.ts:256-282];`onStepSettled` 先 `leave` 再 `enter` [VERIFIED: packages-user/data-base/src/hero/mover.ts:285-301]。
|
||||
- 构图:`build(start)` 检查 layer / predicate / face 是否注入,起点是否在 map 内;BFS 用 `face.mapMovement()` 的每个方向,跳过原地,`layer.inMap` 后构造 handler,`predicate.canPass` 失败则跳过,`predicate.shouldHit` 则把 `nextIndex` 加入 terminals;只把从起点可达的节点放入 `nodes` [VERIFIED: packages-user/data-system/src/path/graph.ts:67-150]。
|
||||
- 搜索:`search()` 用距离堆 + `dist` / `prev` / `visited`;起点等于终点返回空;目标不在图中返回空;`node.terminal` 的节点不能作为中间点(`next.terminal && edge.to !== targetIndex` 跳过);回溯得到步骤 [VERIFIED: packages-user/data-system/src/path/finder.ts:135-197]。
|
||||
- `find()`:起点=终点 → `TargetUnder`;`build` 返回 null → `InvalidInput`;路径空 → `NoPath`;否则 `Success` [VERIFIED: packages-user/data-system/src/path/finder.ts:199-213]。
|
||||
- 初始化:`MainDataPlugin.awake` 创建 `DefaultPassPredicateImpl` 注入 finder,图层先 `null`,`useMover(state.hero.location.mover)`;再创建 `DefaultHeroMoveTopImpl` 并 `useTopImplementation` [VERIFIED: src/data/initialize.ts:34-43]。
|
||||
- 图层校验:`PathFinder.useMapLayer` 检查 `layer.state !== this.state`,不属于当前状态时告警 182 并忽略 [VERIFIED: packages-user/data-system/src/path/finder.ts:107-115]。
|
||||
- 移动器:`startMove` 若有未完成移动返回 null;逐步移动对每步 `mover.step(step.dir)`;瞬移 `mover.tp(last.to.x, last.to.y)`;`controller.onEnd` 后触发 `onAfterTeleportTo` [VERIFIED: packages-user/data-system/src/path/system.ts:72-103]。
|
||||
- `teleportTo`:没有策略或策略返回 true → 逐步;否则瞬移 [VERIFIED: packages-user/data-system/src/path/system.ts:112-121]。
|
||||
|
||||
**自定义谓词(recipe):**
|
||||
1. 实现 `IPassPredicate` 的 `canPass` / `shouldHit`,输入是 `IPassCheckHandler` [VERIFIED: packages-user/data-base/src/map/types.ts:867-890]。
|
||||
2. 寻路侧:`state.pathfinding.finder.usePassPredicate(new MyPredicate(state))` [VERIFIED: packages-user/data-system/src/path/types.ts:138-142]。
|
||||
3. 勇士侧:`DefaultHeroMoveTopImpl` 内部硬编码 `new DefaultPassPredicateImpl(this.state)` [VERIFIED: src/data/hero/moverImpl.ts:20-27];要换语义,需实现 `IHeroMoveTopImpl`(其 `predicate()` 返回你的谓词)并 `state.hero.location.mover.useTopImplementation(impl)`,或直接改 `DefaultHeroMoveTopImpl` [VERIFIED: packages-user/data-base/src/hero/types.ts:358-368; packages-user/data-base/src/hero/mover.ts:55-57]。
|
||||
4. 自定义损失/瞬移策略属于高级 II,链接 `../ii/pathfinding.md`,本篇不重复 [CITED: docs/advanced/ii/pathfinding.md:138-204]。
|
||||
|
||||
### 6. CoreState 组装初始化
|
||||
|
||||
**模型:** `CoreState` 是数据端的组合根:构造函数按 Layer 0 → Layer 1 → Layer 2 → Layer 3 的顺序 `new` 出所有子系统,并把 `this` 注入需要反向引用的对象;然后注册数据端插件、按 `dataDeps` 排序、同步 `awake()`。`load()` 依次执行插件 `initialize()`、`loader.start()`、插件 `afterLoad()`,最后检查所有插件是否 `Loaded`。存档注册与执行器也挂在 CoreState 上。
|
||||
|
||||
**构造顺序(精确事实):**
|
||||
- L0:`roleFace`(`RoleFaceBinder`)、`faceManager`(`FaceManager`)、`tileStore`(`TileStore`)、`itemStore`(`ItemStore`)、`mapStore`(`MapStore`)、`eventStore`(`GameEventStore`)、`enemyStore`(`EnemyStore`)、`replaySystem`(`ReplaySystem`)[VERIFIED: packages-user/data-state/src/core.ts:111-119]。
|
||||
- L1:`flags`(`FlagSystem`)、`maps`(`MapState(this)`)、`hero`(`HeroState(this)`)、`enemyManager`(`EnemyManager`)[VERIFIED: packages-user/data-state/src/core.ts:121-125]。
|
||||
- L2:`enemyContext`(`EnemyContext(this)`)、`combat`(`CombatFlow(this)`)、`eventSystem`(`GameEventSystem(this, config.events)`)、`pathfinding`(`PathfindingSystem(this)`)[VERIFIED: packages-user/data-state/src/core.ts:127-131]。
|
||||
- L3:`loadManager`(`LoadManager`)、`loader`(`MotaDataLoader(this.loadManager, config.loadStarter)`),然后 `loader.addCoreConfig('core', config.coreURL)` [VERIFIED: packages-user/data-state/src/core.ts:133-137]。
|
||||
- 插件注册:遍历 `config.dataPlugins`,重复 id 告警 78;构造 `deps`(`dataDeps`)并 `orderPlugins(deps)`;最后 `this.awake()` [VERIFIED: packages-user/data-state/src/core.ts:139-155]。
|
||||
- `load()`:`await this.initialize()` → `yield* this.loader.start()` → `await this.afterLoad()` → 检查所有插件状态,非 `Loaded` 告警 81 [VERIFIED: packages-user/data-state/src/core.ts:274-283]。
|
||||
- 存档:`saveables` / `addedSaveables` / `executors` 三个私有容器;`addSaveableContent` 重复 id 告警 112;`bindSaveableExecuter` 可传 id 或对象,未注册对象告警 113;`loadState` 先 `replaySystem.array.clearDisableFlag()`,每个对象 `disable()` → `loadState` → `revert()`,执行器在包裹之外 [VERIFIED: packages-user/data-state/src/core.ts:94-107,289-359]。
|
||||
|
||||
**入口职责(精确事实):**
|
||||
- `src/data.ts` 只有一行:`export { createCoreState } from './data/entry';` [VERIFIED: src/data.ts:1]。
|
||||
- `src/data/entry.ts` 的 `createCoreStateConfig(starter)` 组装 `{ loadStarter, coreURL: CORE_URL, dataPlugins: [new MainDataPlugin(HERO_FACE), ...dataPlugins], events: createEventRegistrations() }`;`createCoreState(starter)` 返回**未异步初始化**的 `CoreState`,需要手动 `state.load()` [VERIFIED: src/data/entry.ts:12-32]。
|
||||
- `src/data/initialize.ts` 的 `MainDataPlugin` 负责朝向组、寻路谓词/图层/移动器、勇士顶层实现、loader hook、敌人/录像/钩子初始化、5 个 `addSaveableContent`;`afterLoad` 读 `core` 配置并 `hero.createAttribute` / `createLocation` [VERIFIED: src/data/initialize.ts:17-84]。
|
||||
- `src/bootstrap.ts` 用 `createClientCore(new WebLoadStarter())` 创建渲染端主对象并打开 `LoadSceneUI` [VERIFIED: src/bootstrap.ts:12-16]。
|
||||
- `ClientCore extends CoreState`,构造时 `super(config)`,再挂渲染层 [VERIFIED: packages-user/client-modules/src/client.ts:48-125]。**本篇只讲数据端 CoreState,不讲 L4/L5 渲染层。**
|
||||
|
||||
**游戏内新建数据端实例(精确事实):** `CoreState` 的构造函数创建所有子系统,不依赖全局单例;`coreNode.test.ts` 创建两个实例并断言 `hero` / `attribute` / `eventStore` 不共享,且第一个实例的存档修改不影响第二个 [VERIFIED: packages-user/data-state/__test__/coreNode.test.ts:16-40]。因此可以在游戏内或 Node 中 `new CoreState(fullConfig)` 建一个独立数据端实例,用于回放/比对,而不必启动 CLI。注意必须传完整 `ICoreStateConfig`(`loadStarter` / `coreURL` / `dataPlugins` / `events`)[VERIFIED: packages-user/data-state/src/types.ts:72-84];测试里省略 `dataPlugins`/`events` 的写法与构造器实现不一致,见「Source/comment conflicts」。
|
||||
|
||||
**Use vs customize:** 使用侧是 `createCoreState` / `state.load()` / `state.hero` 等访问器;自定义侧是新增数据端插件(在 `awake` 里 `addSaveableContent`)、在 `src/data/plugin/list.ts` 注册插件、在 `src/data/entry.ts` 增加事件注册。不要建议读者重排构造顺序或手动 `new` 内部子系统。
|
||||
|
||||
### 7. 插件内部机制
|
||||
|
||||
**模型:** 插件是 `IDataPluginDefine`:`id` + `dataDeps` + `awake` / `initialize` / `afterLoad`。`CoreState` 构造时把所有插件放进 `dataPlugins: Map<string, IDataPlugin>`,用 `dataDeps` 建依赖图,`orderPlugins()` 返回拓扑序、环集合、缺失依赖表;随后按拓扑序执行 `awake`。`load()` 按同一顺序执行 `initialize` 与 `afterLoad`。每个阶段只处理处于前一状态的插件,状态推进 `NotAwakened → Awakened → Initialized → Loaded`。
|
||||
|
||||
**关键接口/类:**
|
||||
|
||||
| 名称 | 位置 | 职责 |
|
||||
|------|------|------|
|
||||
| `PluginStatus` | `packages-user/data-state/src/types.ts:10-19` | 四个状态 |
|
||||
| `IDataPluginDefine` | `packages-user/data-state/src/types.ts:39-68` | 三阶段生命周期 |
|
||||
| `IPluginBase` | `packages-user/data-state/src/types.ts:32-37` | `id` + `dataDeps` |
|
||||
| `IPluginOrderResult` | `packages-user/data-state/src/plugin.ts:3-10` | `order` / `cyclic` / `missing` |
|
||||
| `orderPlugins` | `packages-user/data-state/src/plugin.ts:20-105` | 拓扑排序 + 环/缺失检测 |
|
||||
| `CoreState.awake/initialize/afterLoad` | `packages-user/data-state/src/core.ts:208-262` | 按序执行 |
|
||||
| `CoreState.getDataPlugin` | `packages-user/data-state/src/core.ts:264-268` | 按 id 取包装对象 |
|
||||
|
||||
**精确事实:**
|
||||
- `PluginStatus` 的值是 `NotAwakened` / `Awakened` / `Initialized` / `Loaded` [VERIFIED: packages-user/data-state/src/types.ts:10-19]。
|
||||
- `orderPlugins` 先检测缺失依赖(`plugins.has(dependency)` 为 false),再从每个插件沿依赖边搜索回到自身来检测环;被环/缺失阻断的插件及其下游进入 `blocked`;最后反复扫描,输出依赖均已就绪的插件 [VERIFIED: packages-user/data-state/src/plugin.ts:26-104]。
|
||||
- `awake()`:先对 `cyclic` 告警 76、对 `missing` 逐条告警 77;按 `order` 取插件,检查状态为 `NotAwakened`,取依赖包装对象,`plugin.awake(this, dependencies)`,状态置 `Awakened` [VERIFIED: packages-user/data-state/src/core.ts:208-228]。
|
||||
- `initialize()`:按 `order`,检查状态为 `Awakened`,`await plugin.initialize(...)`,状态置 `Initialized` [VERIFIED: packages-user/data-state/src/core.ts:233-245]。
|
||||
- `afterLoad()`:按 `order`,检查状态为 `Initialized`,`await plugin.afterLoad(...)`,状态置 `Loaded` [VERIFIED: packages-user/data-state/src/core.ts:250-262]。
|
||||
- 依赖在启动前定好,之后不重算;没有运行时追加插件的接口 [CITED: docs/advanced/plugin-system.md:255-257]。
|
||||
- 重复插件 id 在构造时告警 78 并跳过 [VERIFIED: packages-user/data-state/src/core.ts:140-149]。
|
||||
- `getDataPlugin` 返回 `Readonly<IDataPlugin<T>> | null` [VERIFIED: packages-user/data-state/src/core.ts:264-268; packages-user/data-state/src/types.ts:106-108]。
|
||||
|
||||
**Use vs customize:** 使用侧是声明 `dataDeps`、在三个时机里做事、用 `getDataPlugin` 取回;自定义侧是新增插件类并注册到 `src/data/plugin/list.ts` [VERIFIED: src/data/plugin/list.ts:4]。`orderPlugins` 是内部机制,读者不需要自己调用;本篇只解释「为什么顺序固定、依赖写错会怎样」。
|
||||
|
||||
## Old vs New Replay Storage(exact facts)
|
||||
|
||||
**旧表示(逐字证据):**
|
||||
|
||||
```js
|
||||
// public/libs/core.js:185 —— 旧录像就是字符串数组
|
||||
route: [],
|
||||
|
||||
// public/libs/control.js:604 —— 一步转向写成字符串
|
||||
core.status.route.push('turn:' + direction);
|
||||
|
||||
// public/libs/items.js:88 —— 使用道具写成 'item:<id>'
|
||||
core.status.route.push('item:' + itemId);
|
||||
|
||||
// public/libs/utils.js:681 —— 整条 route 压缩成一个 base64 字符串
|
||||
return LZString.compressToBase64(ans);
|
||||
|
||||
// public/libs/utils.js:685-688 —— 物品 id 先映射成地图块号
|
||||
utils.prototype._encodeRoute_id2number = function (id) {
|
||||
var number = core.maps.getNumberById(id);
|
||||
return number === 0 ? id : number;
|
||||
};
|
||||
```
|
||||
|
||||
**新表示(逐字证据):**
|
||||
|
||||
```ts
|
||||
// packages-user/data-common/src/replay/array.ts:80-84 —— 三块 TypedArray
|
||||
private commandArray: Uint8Array;
|
||||
private paramArray: Uint8Array;
|
||||
private indexArray: Uint32Array;
|
||||
|
||||
// packages-user/data-common/src/replay/system.ts:97-104 —— 存档就是四个字段
|
||||
saveState(): IReplaySystemSave {
|
||||
return {
|
||||
length: this.array.length,
|
||||
commandWidth: this.array.commandWidth,
|
||||
commandArray: this.array.getCommandArray(),
|
||||
paramArray: this.array.getParamArray()
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
| 维度 | 旧实现(`public/libs/*`) | 新实现(`@user/data-common`) | 证据 |
|
||||
|------|---------------------------|-------------------------------|------|
|
||||
| 容器 | `core.status.route: string[]`,初始化 `route: []` | `ReplayArray`:`commandBuffer` / `paramBuffer` / `indexBuffer` + `Uint8Array` / `Uint32Array` | [VERIFIED: public/libs/core.js:185; packages-user/data-common/src/replay/array.ts:66-84] |
|
||||
| 单步表示 | 字符串,如 `'up'` / `'turn:D'` / `'item:123'` / `'move:3:4'` | 2/3 字节指令记录:参数数量 + 指令 id;参数为类型 token + 载荷 | [VERIFIED: public/libs/control.js:525,604; public/libs/items.js:88; packages-user/data-common/src/replay/array.ts:143-149,345-356] |
|
||||
| 序列化 | `encodeRoute()` 生成文本,`LZString.compressToBase64(ans)`;`compress=false` 时前缀 `'!!'` | `saveState()` 返回 `{ length, commandWidth, commandArray, paramArray }`,直接是 `ArrayBuffer` | [VERIFIED: public/libs/utils.js:645-682; packages-user/data-common/src/replay/system.ts:97-104] |
|
||||
| 解码 | `decodeRoute()` 先 `LZString.decompressFromBase64`,再逐字符解析 | `createReadStream()` / `get(index)` 按索引与类型表解码 | [VERIFIED: public/libs/utils.js:715-736; packages-user/data-common/src/replay/array.ts:776-822] |
|
||||
| 移动压缩 | 连续同方向合并成 `U3` / `D2` 等 run-length | 每步一条 `ReplayCode.Up/Right/Down/Left`,不做文本 run-length | [VERIFIED: public/libs/utils.js:650-682; packages-user/data-common/src/replay/types.ts:5-22] |
|
||||
| 参数类型 | 文本 + `parseInt`;类型信息丢失 | `boolean` / `int8` / `int16` / `int32` / `int64` / `float64` / `bigint` / `string` | [VERIFIED: public/libs/utils.js:738-763; packages-user/data-common/src/replay/array.ts:227-309] |
|
||||
| 大整数/浮点 | 字符串解析,精度/类型有限 | int64 高低 32 位;float64 原样;bigint 幅值字节(<2^2040) | [VERIFIED: packages-user/data-common/src/replay/array.ts:270-288,383-389,687-712] |
|
||||
| 随机访问 | 从文本开头逐字符解析 | `indexArray` 存每条指令的参数起点;`get(index)` 直接定位 | [VERIFIED: packages-user/data-common/src/replay/array.ts:70-84,776-785] |
|
||||
| 编辑 | 数组 `splice` 后整体重新 `encodeRoute` | `insert` / `delete` / `set` 原地移动缓冲区并平移索引 | [VERIFIED: public/libs/control.js:2414; packages-user/data-common/src/replay/array.ts:445-584] |
|
||||
| 物品 id | `getNumberById` 把 id 映射为地图块号;解码用 `blocksInfo` 反查 | 指令码 + 原始参数,不依赖地图块号 | [VERIFIED: public/libs/utils.js:685-688,765-771; packages-user/data-common/src/replay/types.ts:5-22] |
|
||||
| 读取失效 | 数组被修改后没有读流概念 | 任何写操作 `expireStreams()`;读流 `expired` | [VERIFIED: packages-user/data-common/src/replay/array.ts:215-220,787-822] |
|
||||
| 指令宽度 | 单字母/自定义 `(...)` 文本语法 | `Uint8`(0-255)或 `Uint16`(0-65535),可 `setCommandWidth` | [VERIFIED: public/libs/utils.js:690-711; packages-user/data-common/src/replay/types.ts:176-181; packages-user/data-common/src/replay/array.ts:586-628] |
|
||||
| 兼容性 | 文本语法 + LZString | 类型码只增不改;注释明确不承诺旧格式可读回 | [VERIFIED: packages-user/data-common/src/replay/types.ts:400-402] |
|
||||
| 禁用 | 旧引擎用播放状态/全局判断 | `disabled` 嵌套计数;`disable` / `revert` / `clearDisableFlag` | [VERIFIED: packages-user/data-common/src/replay/array.ts:887-899] |
|
||||
|
||||
**具体优势(写文档时可直接用):**
|
||||
1. **紧凑**:每步固定 2/3 字节 + 类型化载荷,不需要 base64 膨胀,也不需要 LZString 解压;旧文本虽然 run-length 压缩移动,但整体仍要压缩/解压。
|
||||
2. **类型保真**:新格式区分 int8/int16/int32/int64/float64/bigint/string;旧格式所有数字都靠文本解析,浮点与大整数容易失真。
|
||||
3. **可随机访问**:`indexArray` 让 `get(index)` 直接定位参数区;旧格式必须从头解析。
|
||||
4. **可编辑**:`insert` / `delete` / `set` 是数组级操作;旧格式改一步要重编码整条 route。
|
||||
5. **不依赖地图编号**:新录像不把物品 id 转成地图块号;旧格式的 `getNumberById` / `blocksInfo` 映射会随地图内容变化而失效。
|
||||
6. **读取安全**:新数组写操作会让旧读流 `expired`;旧数组没有这个概念。
|
||||
7. **格式稳定**:类型码只增不改,指令宽度可配置;旧格式是文本语法 + LZString,扩展靠新增字母/括号语法。
|
||||
|
||||
## Page Split(8 pages)
|
||||
|
||||
**建议目录:** `docs/advanced/iii/`。目录名与文件名须按 D-13 先经用户批准;下表是建议,不是已批准事实。
|
||||
|
||||
| # | 文件 | 标题 | 核心内容 | 不写什么 |
|
||||
|---|------|------|----------|----------|
|
||||
| 1 | `index.md` | 高级 III 导览 | 难度定位、前置(高级 I/II + 中级相关篇)、阅读路径、8 页地图、与 I/II 的边界 | 不复述各系统细节;不写「渲染端以后讲」的元叙述 |
|
||||
| 2 | `loading-system.md` | 加载系统 | LoadTask → LoadManager → MotaDataLoader 模型;三阶段;starter 抽象;配置文件/自定义任务;渲染端接口级小节 | 不写 FSA 实现;不写渲染场景/UI;不写 `getTotalByte` 语义 |
|
||||
| 3 | `replay-internals.md` | 录像系统底层 | ReplayArray 二进制布局、ReplaySystem、Sandbox、主动/被动、存档形状、旧 vs 新对比 | 不写旧引擎当前 API;不写指令目录;不写 route folding |
|
||||
| 4 | `event-system.md` | 事件系统 | eventStore / GameEvent / collectEvent / EventExecutor / 触发器 / 事件层 / 自定义内建函数 | 不写序列化注册、地图事件 id 绑定、编辑器识别 |
|
||||
| 5 | `anon-tokyo.md` | Anon-Tokyo 解释器模型 | LanguageFeature / Interpreter / Executable / Context / Iterator / Statement / Scope / OP | 不写 compile.ts 解析器内部;不写性能 |
|
||||
| 6 | `passability-and-pathfinding.md` | 通行性谓词与寻路原理 | IPassPredicate 契约、同一语义服务勇士/寻路、构图→搜索→移动器、自定义谓词 | 不写 Dijkstra 细节;不重复 `ii/pathfinding.md` 的使用教程 |
|
||||
| 7 | `core-state.md` | CoreState 组装与初始化 | 构造顺序 L0–L3、load 管线、存档注册、入口职责、游戏内第二实例 | 不写 L4/L5 渲染层;不写 ClientCore |
|
||||
| 8 | `plugin-internals.md` | 插件内部机制 | orderPlugins 算法、注册表/状态、依赖失败、固定顺序、getDataPlugin | 不重复 `plugin-system.md` 的插件写法;不写渲染端插件 |
|
||||
|
||||
**Merge/split flags:**
|
||||
- `anon-tokyo` 与 `event-system`:天然相邻,但**不要合并**——解释器是独立子系统,合并会让 event-system 变成「事件 + 语言」两大块;若成稿不足约 150 行,可合并,但需用户批准。
|
||||
- `plugin-internals` 与 `core-state`:天然相邻,但**不要合并**——core-state 讲装配顺序,plugin-internals 讲拓扑排序与失败模式;合并会超出单页合理长度。若必须压缩,优先保留 core-state,把 plugin-internals 缩成 core-state 的一节。
|
||||
- `passability-and-pathfinding` 与 `ii/pathfinding.md`:**不要合并**,但必须切分:II 讲「怎么用/怎么换损失函数/瞬移策略」,III 只讲「谓词契约 + 构图→搜索→移动器 + 自定义谓词」。若 III 稿开始重复 II 的 `moveTo`/`teleportTo` 用法,删掉并链接。
|
||||
- `loading-system` 的渲染端内容:**不要单独成页**;作为 `### 渲染端如何消费加载进度` 小节,控制在 1 屏内。
|
||||
- `replay-internals` 的旧 vs 新对比:**不要单独成页**;作为该页的一节,表格 + 3-5 条优势即可。
|
||||
|
||||
## Documentation facts per page
|
||||
|
||||
> 每页先写「系统模型」,再写「关键接口」,最后写「怎么用/怎么自定义」;边界统一放文末「边界与注意」。下列 file:line 均为本会话直接阅读。
|
||||
|
||||
### 1. `iii/index.md` — 高级 III 导览
|
||||
|
||||
- **定位**:高级 III 是「原理与架构」难度层,只写数据端;不是领域分类 [CITED: 10-CONTEXT.md D-53]。
|
||||
- **前置**:高级 I(插件系统、可存档、录像一致性、Store、钩子、Mover、FaceHandler)+ 高级 II(范围、图块、装备、录像指令、寻路、属性、战斗上下文);中级相关篇目按需链接 [CITED: docs/advanced/index.md:17-27; docs/advanced/ii/index.md:31-48]。
|
||||
- **8 页地图**:本文件 Page Split 表;每页一句话「它能让你做什么」。
|
||||
- **阅读路径建议**:原理线(core-state → plugin-internals → event-system → anon-tokyo → passability-and-pathfinding → replay-internals → loading-system);按需线(先读 core-state,再跳目标系统)。
|
||||
- **当前占位**:`docs/advanced/index.md:27` 仍写「高级 III 暂不提供页面入口」;`docs/advanced/ii/index.md:29` 仍写「高级 III 尚未编写」;`docs/.vitepress/config.ts:234-238` 的「高级 III · 原理与架构」分组 `items: []`。本批必须替换这些占位并注册侧边栏。
|
||||
- **导航风险**:`config.ts:22` 的 `nav` 仍指向 `/advanced/`(当前是高级 I 导览)。若高级 I 被移动到 `/advanced/i/`,`nav` 与 `advanced/index.md` 的归属要同步调整;如果 `advanced/index.md` 也被移走,必须保留或新建一个 `/advanced/index.md` 作为高级教程总入口,否则 `nav` 的 `/advanced/` 会 dead link。否则 `/advanced/` 会指向旧位置。
|
||||
- **文末边界**:不承诺渲染端/编辑器内容;不把 III 写成 II 的重复;页面文件名需 D-13 批准。
|
||||
|
||||
### 2. `iii/loading-system.md` — 加载系统
|
||||
|
||||
- **模型**:`LoadTask`(单资源)→ `LoadManager`(聚合)→ `MotaDataLoader`(阶段编排);`ILoadTaskStarter` 是环境适配点 [VERIFIED: packages/loader/src/task.ts:24-253; packages/loader/src/manager.ts:10-112; packages-user/data-state/src/loader/loader.ts:18-151]。
|
||||
- **阶段顺序**:core configs → `onCoreConfigLoaded` → extra configs → `onExtraConfigLoaded` → custom tasks → `loaded()` [VERIFIED: packages-user/data-state/src/loader/loader.ts:84-142]。
|
||||
- **配置格式**:`PrefixedJSONCProcessor` 用 `'// --- SYSTEM PREFIX END --- //'` 切分,前缀之后才是 JSON;切分失败告警 66,JSON 解析失败告警 67 [VERIFIED: packages-user/data-state/src/loader/jsoncProcessor.ts:4-28]。
|
||||
- **路径**:DEV 前置 `src/`,构建后不前置 [VERIFIED: packages-user/data-state/src/loader/loader.ts:74-82]。
|
||||
- **自定义配置**:`addCoreConfig` / `addExtraConfig` / `addCustomTask`;额外配置的 `url` 相对于 `src/content` [VERIFIED: packages-user/data-state/src/loader/types.ts:34-65]。
|
||||
- **自定义资源**:`new LoadTask({ identifier, url, dataType })` + `setProcessor` + `setStarter` + `addCustomTask`;`LoadDataType` 五值 [VERIFIED: packages/loader/src/task.ts:50-77; packages/loader/src/types.ts:82-88]。
|
||||
- **进度**:`LoadManager.load()` 是异步迭代器,产出已加载字节;`getLoadedTasks` / `getAddedTasks` / `getTaskRatio` / `getByteRatio` [VERIFIED: packages/loader/src/manager.ts:51-111]。
|
||||
- **默认配置 hook**:`DefaultDataLoaderHook` 从 `core.content` 添加 enemy/item/map-list/tile + 四个 default [VERIFIED: src/data/loader/hook.ts:7-29]。
|
||||
- **数据端编排**:`CoreState.load()` 先 `initialize()`,再 `loader.start()`,再 `afterLoad()` [VERIFIED: packages-user/data-state/src/core.ts:274-283]。
|
||||
- **渲染端小节**:`src/bootstrap.ts:12-16` 创建 ClientCore + LoadSceneUI;`src/client/entry.ts:23-38` 的 `createClientCoreConfig` 直接展开 `createCoreStateConfig(starter)`,所以渲染端复用同一套数据端配置与 loader;`LoadScene` 迭代 `loader.start()` 并用 manager 计数器画进度 [VERIFIED: src/client/ui/load.tsx:52-174];`RenderLoaderHooks` 添加渲染配置 [VERIFIED: packages-user/client-modules/src/loader/hook.ts:5-25];`ClientCore` 构造时 `loader.addCoreConfig('client', config.clientURL)` [VERIFIED: packages-user/client-modules/src/client.ts:107]。只写接口消费,不写 UI 生命周期。
|
||||
- **本地数据包(设计能力)**:`ILoadTaskStarter` 可替换为 File System Access API starter;仓库无实现,不写代码 [VERIFIED: packages-user/data-state/src/types.ts:73-77]。
|
||||
- **文末边界**:`addCustomTask` 只能在加载前或两个 hook 中调用;配置 id 重复告警 68;`LoadManager` 的 `getTotalByte` 实现疑点只报告不写入文档;`loading/dataLoaded` 守卫未赋值只报告不写入文档;`resolveURL` 依赖 Vite `import.meta.env`,自定义 starter 需自行处理路径。
|
||||
|
||||
### 3. `iii/replay-internals.md` — 录像系统底层
|
||||
|
||||
- **模型**:`ReplaySystem` = `ReplayArray` + 指令注册表 + 可选 `ReplaySandbox`;录制 `record()` 追加;播放沙箱读流 [VERIFIED: packages-user/data-common/src/replay/system.ts:21-114]。
|
||||
- **指令码**:`ReplayCode` 值 `Up` / `Right` / `Down` / `Left` / `Teleport` / `UseItem` / `Equip` / `Unequip`;数值属于格式,只增不改 [VERIFIED: packages-user/data-common/src/replay/types.ts:4-22; docs/advanced/ii/custom-replay-command.md:18]。
|
||||
- **指令对象**:`IReplayCommand.type` 为 `Active` / `Passive`;`execute(step)` 返回 `Success` / `Failed` / `Ignored`;可选 `finalize()` [VERIFIED: packages-user/data-common/src/replay/types.ts:24-38,62-78]。
|
||||
- **二进制布局**:见 System Models §2 的精确事实;写文档时用「指令区 + 参数区 + 索引区」三块解释,不逐字节展开 [VERIFIED: packages-user/data-common/src/replay/array.ts:66-84,143-149,227-309]。
|
||||
- **int64 / bigint**:int64 高低 32 位;bigint 幅值小端、长度 1 字节、上限 2^2040 [VERIFIED: packages-user/data-common/src/replay/array.ts:270-288,383-389,687-712]。
|
||||
- **索引与读流**:`indexArray` + `createReadStream` + `expired`;写操作过期旧流 [VERIFIED: packages-user/data-common/src/replay/array.ts:70-84,215-220,787-822]。
|
||||
- **编辑**:`insert` / `delete` / `set` 的合法区间与告警 179;O(n) 迁移 [VERIFIED: packages-user/data-common/src/replay/array.ts:445-584; packages-user/data-common/src/replay/types.ts:232-260]。
|
||||
- **沙箱**:`createReplaySandbox` 先 reset;`getPassive()` 返回 `{ step, next }`;主动/被动交错;`finalize`;`play/pause/resume/stop/step` [VERIFIED: packages-user/data-common/src/replay/system.ts:70-82; packages-user/data-common/src/replay/sandbox.ts:81-110,146-224,263-312]。
|
||||
- **存档**:`IReplaySystemSave` 四字段;`saveState` 返回内部 buffer 引用;`loadState` 重建索引 [VERIFIED: packages-user/data-common/src/replay/system.ts:97-113; packages-user/data-common/src/replay/types.ts:373-405]。
|
||||
- **旧 vs 新**:使用本文件「Old vs New Replay Storage」表;旧实现只作为对比,不写当前 API。
|
||||
- **Use vs customize**:使用 `record` / `registerCommand` / `createReplaySandbox`;自定义指令链接 `../ii/custom-replay-command.md`;数组编辑只作边界说明。
|
||||
- **文末边界**:参数最多 255 个,超出截断告警 153 [VERIFIED: packages-user/data-common/src/replay/array.ts:316-321];不支持的参数类型告警 148 并丢弃 [VERIFIED: packages-user/data-common/src/replay/array.ts:308-309];`get(index)` 注释明确「极为缓慢」[VERIFIED: packages-user/data-common/src/replay/types.ts:262-266];`setCommandWidth` 缩小到 Uint8 时 >255 的指令告警 154 [VERIFIED: packages-user/data-common/src/replay/array.ts:612-616];存档不承诺旧格式可读回 [VERIFIED: packages-user/data-common/src/replay/types.ts:400-402];`saveState` 返回直接引用,不要就地修改 [VERIFIED: packages-user/data-common/src/replay/system.ts:101-102]。
|
||||
|
||||
### 4. `iii/event-system.md` — 事件系统
|
||||
|
||||
- **模型**:eventStore → GameEvent → GameEventSystem.collectEvent → EventExecutor → AnonTokyoInterpreter;事件层是 `alias === 'event'` 的普通图层 [VERIFIED: packages-user/data-common/src/store/eventStore.ts:5-33; packages-user/data-system/src/event/system.ts:24-112; packages-user/data-base/src/map/mapState.ts:89-91]。
|
||||
- **store**:`addEvent(id, event)` 重复 id 告警 170 并覆盖;`getEvent(id)` 返回实例本身 [VERIFIED: packages-user/data-common/src/store/eventStore.ts:13-32; docs/advanced/data-stores.md:188-222]。
|
||||
- **事件对象**:`GameEvent` 持有 `trigger` / `rawEvent` / `compiled`;`compile()` 缓存;`setRaw` 清缓存;`setTrigger` 设置触发器 [VERIFIED: packages-user/data-common/src/event/event.ts:8-54]。
|
||||
- **收集**:点事件优先于图块事件;各自优先级降序;env 字段完整 [VERIFIED: packages-user/data-system/src/event/system.ts:44-111]。
|
||||
- **执行**:trigger 不匹配跳过;store/事件缺失告警 171;`CutIfFalsy` / `CutIfTruthy`;`NoReduce` / `OrReduce` / `AndReduce`;非布尔告警 172 [VERIFIED: packages-user/data-system/src/event/executor.ts:53-113]。
|
||||
- **触发器**:完整枚举见 System Models §3;本仓库只有 OnEnter/OnLeave/OnTouch 有派发点 [VERIFIED: src/data/hero/moverImpl.ts:72-82]。
|
||||
- **事件层**:`IGameMap.eventLayer` + `layerList`;事件层本质是图层 [VERIFIED: packages-user/data-base/src/map/types.ts:659,669]。
|
||||
- **自定义内建函数**:类 + `name` + `func`;加入 `createEventRegistrations()`;参数接口在 `src/data/event/types.ts`;注册进 `config.events` [VERIFIED: src/data/event/registrations.ts:9-24; src/data/event/map.ts:9-31; src/data/event/types.ts:1-66; src/data/entry.ts:15-20]。
|
||||
- **Use vs customize**:使用 `collectEvent` / `executor.execute` / `getEvent`;自定义内建函数;`EventTrigger` 新增值属于引擎级改动,不鼓励。
|
||||
- **文末边界**:`collectEvent` 坐标越界返回空;点/图块事件互不替代;同一优先级覆盖告警 136(来自 `point-and-tile-events.md`);事件 id 必须已注册;`@shouldReplay` 在执行器上;异步执行顺序;不要写序列化注册/地图 id 绑定。
|
||||
|
||||
### 5. `iii/anon-tokyo.md` — Anon-Tokyo 解释器模型
|
||||
|
||||
- **模型**:`LanguageFeature` → `AnonTokyoInterpreter` → `compile` → `AnonTokyoExecutable` → `AnonTokyoExecutionContext` / `AnonTokyoIterator` → `OP` 循环 [VERIFIED: packages/anon-tokyo/src/interpreter.ts:20-219]。
|
||||
- **内建函数**:`BuiltInFunction { name, save?, func(parameters, env) }`;`GameEventSystem` 用 `config.events` 构造 [VERIFIED: packages/anon-tokyo/src/interpreter.ts:11-18; packages-user/data-system/src/event/system.ts:33-36]。
|
||||
- **语句模型**:`StatementType` 11 值;`Statement` 联合;`Scope { args, local, env }`;`OPType` 4 值 [VERIFIED: packages/anon-tokyo/src/type.ts:1-17,86-95,197-216]。
|
||||
- **编译/执行**:`compile` 用 `getBuiltInFunction` 解析调用;`exec` 创建 context;`AnonTokyoIterator` 处理 Move/Call/Return/Exit [VERIFIED: packages/anon-tokyo/src/interpreter.ts:38-67,114-138,197-219]。
|
||||
- **GameEvent 集成**:`compile()` 缓存 `compiled`;`execute()` 优先 compiled;`setRaw` 清缓存 [VERIFIED: packages-user/data-common/src/event/event.ts:24-49]。
|
||||
- **Use vs customize**:使用事件对象即可;自定义 `BuiltInFunction` 或 `globalFunctions`(当前数据端传空数组);不要写 `compile.ts` 解析器细节。
|
||||
- **文末边界**:`getBuiltInFunction` / `getGlobalFunction` 找不到直接 `throw` 字符串 [VERIFIED: packages/anon-tokyo/src/interpreter.ts:164-178];`AnonTokyoExecutionContext.dump()` 是空实现,不要写成可用功能 [VERIFIED: packages/anon-tokyo/src/interpreter.ts:108];`BuiltInFunction.save` 在解释器中未被使用,不要赋予未证实语义 [VERIFIED: packages/anon-tokyo/src/interpreter.ts:11-18,143-219]。
|
||||
|
||||
### 6. `iii/passability-and-pathfinding.md` — 通行性谓词与寻路原理
|
||||
|
||||
- **模型**:`IPassPredicate` 是唯一语义契约;勇士移动与寻路图构建都调 `canPass` / `shouldHit`;寻路是「构图 → 搜索 → 交给移动器」 [VERIFIED: packages-user/data-base/src/map/types.ts:867-890; packages-user/data-system/src/path/graph.ts:123-126; packages-user/data-system/src/path/finder.ts:199-213]。
|
||||
- **同一语义、两个实例**:`DefaultHeroMoveTopImpl` 自建谓词;`MainDataPlugin` 为 finder 另建一个;文档要明确「同一契约/默认实现,不是同一对象」 [VERIFIED: src/data/hero/moverImpl.ts:20-27; src/data/initialize.ts:34-36]。
|
||||
- **默认实现**:事件层 `outPass` / `inPass`;其它图层只检查 `onlyEvents`;`shouldHit` 看 `eventPass` [VERIFIED: src/data/hero/predicate.ts:30-84]。
|
||||
- **勇士移动**:`CannotMove` / `Hit` / `Step` 三种结果;`cannotEnter` / `hit` / `leave` + `enter` [VERIFIED: packages-user/data-base/src/hero/mover.ts:201-217,256-301]。
|
||||
- **构图**:BFS 只含从起点可达节点;`shouldHit` 标 terminal;`resolveCost` 对 NaN/负数回退 1,Infinity 合法 [VERIFIED: packages-user/data-system/src/path/graph.ts:57-65,99-150]。
|
||||
- **搜索**:最小损失;terminal 只能作终点;`TargetUnder` / `InvalidInput` / `NoPath` / `Success` [VERIFIED: packages-user/data-system/src/path/finder.ts:135-213; packages-user/data-system/src/path/types.ts:107-116]。
|
||||
- **系统层**:`useMover` / `useFallbackPolicy` / `getPath` / `moveTo` / `teleportTo` / `interrupt`;`moveTo` 忙碌返回 null;`teleportTo` 策略 [VERIFIED: packages-user/data-system/src/path/system.ts:46-129]。
|
||||
- **自定义谓词**:实现 `IPassPredicate`;finder `usePassPredicate`;勇士侧换 `IHeroMoveTopImpl` 或改 `DefaultHeroMoveTopImpl` [VERIFIED: packages-user/data-base/src/map/types.ts:878-890; packages-user/data-system/src/path/types.ts:138-142; packages-user/data-base/src/hero/types.ts:358-368]。
|
||||
- **Use vs customize**:使用侧默认已装配;自定义损失/瞬移策略链接 `../ii/pathfinding.md`;本篇只讲谓词与构图/搜索模型。
|
||||
- **文末边界**:图层必须属于同一数据端(告警 182)[VERIFIED: packages-user/data-system/src/path/finder.ts:107-115];face handler 未注入构图返回 null [VERIFIED: packages-user/data-system/src/path/graph.ts:79-82];起点越界告警 183 [VERIFIED: packages-user/data-system/src/path/graph.ts:83-86];`NoPath` 正常;`TargetUnder` 不移动;换楼层重新 `useMapLayer`;瞬移跳过途经格 [CITED: docs/advanced/ii/pathfinding.md:303-319]。
|
||||
|
||||
### 7. `iii/core-state.md` — CoreState 组装与初始化
|
||||
|
||||
- **模型**:CoreState 是组合根;L0 → L1 → L2 → L3 构造;插件注册/排序/awake;`load()` 三阶段 [VERIFIED: packages-user/data-state/src/core.ts:109-156,274-283]。
|
||||
- **L0**:`roleFace` / `faceManager` / `tileStore` / `itemStore` / `mapStore` / `eventStore` / `enemyStore` / `replaySystem` [VERIFIED: packages-user/data-state/src/core.ts:111-119]。
|
||||
- **L1**:`flags` / `maps` / `hero` / `enemyManager` [VERIFIED: packages-user/data-state/src/core.ts:121-125]。
|
||||
- **L2**:`enemyContext` / `combat` / `eventSystem` / `pathfinding` [VERIFIED: packages-user/data-state/src/core.ts:127-131]。
|
||||
- **L3**:`loadManager` / `loader` + `addCoreConfig('core', coreURL)` [VERIFIED: packages-user/data-state/src/core.ts:133-137]。
|
||||
- **入口**:`src/data.ts` 只 re-export `createCoreState`;`src/data/entry.ts` 组装完整 config;`MainDataPlugin` 做主要初始化 [VERIFIED: src/data.ts:1; src/data/entry.ts:12-32; src/data/initialize.ts:17-84]。
|
||||
- **存档**:5 个 `addSaveableContent` id:`@system/hero` / `@system/flags` / `@system/maps` / `@system/enemy` / `@system/replay` [VERIFIED: src/data/initialize.ts:56-60; docs/advanced/saveable-content.md:226]。
|
||||
- **loadState**:清 disable flag;每个对象 disable/revert;执行器在包裹外 [VERIFIED: packages-user/data-state/src/core.ts:329-359]。
|
||||
- **游戏内第二实例**:`new CoreState(fullConfig)` 创建独立数据端;`coreNode.test.ts` 断言不共享 [VERIFIED: packages-user/data-state/__test__/coreNode.test.ts:16-40];可用于回放/比对,不必启动 CLI。
|
||||
- **Use vs customize**:使用 `createCoreState` / `state.load()` / 访问器;自定义插件 + `src/data/plugin/list.ts`;不要重排构造顺序。
|
||||
- **文末边界**:`load()` 必须被迭代;`ICoreStateConfig` 四字段必填;`addSaveableContent` 重复 id 告警 112;`bindSaveableExecuter` 未注册对象告警 113;`loadState` 缺 key 告警 177、多余 key 告警 178;插件状态未到 `Loaded` 告警 81。
|
||||
|
||||
### 8. `iii/plugin-internals.md` — 插件内部机制
|
||||
|
||||
- **模型**:插件注册表 → `dataDeps` 图 → `orderPlugins` → 状态机 → 三阶段 [VERIFIED: packages-user/data-state/src/plugin.ts:20-105; packages-user/data-state/src/core.ts:139-262]。
|
||||
- **状态**:`NotAwakened` / `Awakened` / `Initialized` / `Loaded` [VERIFIED: packages-user/data-state/src/types.ts:10-19]。
|
||||
- **排序**:缺失依赖检测、环检测、blocked 下游、反复扫描拓扑序 [VERIFIED: packages-user/data-state/src/plugin.ts:26-104]。
|
||||
- **执行**:awake 同步、initialize/afterLoad 异步;每个阶段只处理前一状态的插件 [VERIFIED: packages-user/data-state/src/core.ts:208-262]。
|
||||
- **失败**:环告警 76、缺失依赖告警 77、重复 id 告警 78、状态不符告警 80、未加载完告警 81 [VERIFIED: packages-user/data-state/src/core.ts:140-149,175-190,208-282]。
|
||||
- **取回**:`getDataPlugin` 返回 `Readonly<IDataPlugin<T>> | null`;启动阶段优先声明依赖 [VERIFIED: packages-user/data-state/src/core.ts:264-268; docs/advanced/plugin-system.md:313-355]。
|
||||
- **Use vs customize**:使用 `dataDeps` + 三时机;自定义插件类 + `src/data/plugin/list.ts`;`orderPlugins` 不供读者调用。
|
||||
- **文末边界**:依赖启动前固定,运行时不重算/不追加;环/缺失会阻断下游;`dataDeps` 与渲染端 `clientDeps` 是两套;`getDataPlugin` 的 `status` 必须检查。
|
||||
|
||||
## Use vs Customize(D-77)
|
||||
|
||||
| 页面 | 使用(高概率) | 自定义(高概率) | 重心判断 |
|
||||
|------|----------------|------------------|----------|
|
||||
| loading-system | `getConfig` / `state.load()` / 进度 | 新增配置、`LoadTask`、自定义 starter | 使用与自定义各半;自定义集中在「加配置/加资源」 |
|
||||
| replay-internals | `record` / `registerCommand` / 沙箱 | 新增指令(链接 II);数组编辑很少 | **使用为主**;原理用于理解格式 |
|
||||
| event-system | `collectEvent` / `executor.execute` / `getEvent` | 新增 `BuiltInFunction` 并注册 | **自定义为主**;事件系统本身就是扩展点 |
|
||||
| anon-tokyo | 通过事件对象使用 | 新增内建函数 / globalFunctions | **使用为主**;解释器内部几乎不需要改 |
|
||||
| passability-and-pathfinding | 默认谓词已装配;`getPath` / `moveTo` | 实现 `IPassPredicate`;换 `IHeroMoveTopImpl` | **自定义为主**;默认实现通常不改 |
|
||||
| core-state | `createCoreState` / `state.load()` / 访问器 | 新增插件、注册存档对象 | **使用为主**;装配顺序不要动 |
|
||||
| plugin-internals | 声明 `dataDeps` / 三时机 | 新增插件类并注册 | **使用为主**;内部排序只解释不修改 |
|
||||
|
||||
## What to avoid documenting
|
||||
|
||||
| 页面 | 不要写 |
|
||||
|------|--------|
|
||||
| 全部 | 渲染管线/场景/UI 内部架构;编辑器与 `.jsonc` 作者流程;legacy 包与 `public/libs/*` 作为当前 API;序列化事件注册 / 地图事件 id 绑定;把 TODO/占位标记写成正文叙述(D-47 的占位标记本身仍要保留) |
|
||||
| loading-system | File System Access API 实现代码;`getTotalByte` 的「总字节」语义;`loading`/`dataLoaded` 守卫的「只加载一次」承诺;渲染端 `ClientCore` 生命周期 |
|
||||
| replay-internals | 旧 `control.js` 的 `route` 字符串 API 作为现用接口;route folding;完整指令目录;`compile.ts` 解析器细节 |
|
||||
| event-system | 编辑器事件注册/识别;地图事件 id 绑定;完整内建事件目录;事件脚本语法全量 |
|
||||
| anon-tokyo | `compile.ts` 的解析/优化内部;性能结论;`dump()` 作为可用功能 |
|
||||
| passability-and-pathfinding | Dijkstra 算法证明/复杂度;渲染端移动动画;`ii/pathfinding.md` 已讲的使用教程 |
|
||||
| core-state | L4/L5 渲染层;`ClientCore` 构造;渲染端插件 |
|
||||
| plugin-internals | 渲染端插件内部;插件作者入门(链接 `plugin-system.md`);`ClientCore.afterLoad` 实现 |
|
||||
|
||||
## Boundary/edge cases per page(D-61 文末单节)
|
||||
|
||||
- **loading-system**:配置 id 重复(68);`addCustomTask` 调用时机;JSONC 前缀缺失(66/67);DEV/构建路径差异;自定义 starter 必须返回 `IResponseLike`;`LoadManager` 的字节进度在总大小未知时可能不准(`ILoadManagerHooks.onProgress` 注释已说明 total 可能不准确)[VERIFIED: packages/loader/src/types.ts:276-279]。
|
||||
- **replay-internals**:参数 >255 截断(153);不支持类型丢弃(148);`get(index)` 慢;insert/delete/set O(n) 且越界告警 179;bigint 上限 2^2040;Uint8 缩窄告警 154;存档 buffer 直接引用;旧格式不保证可读回。
|
||||
- **event-system**:坐标越界返回空;点/图块事件互不替代;同优先级覆盖(136);事件 id 必须已注册;trigger 不匹配跳过;`CutIf` 模式短路;`OrReduce`/`AndReduce` 非布尔告警 172;异步执行顺序。
|
||||
- **anon-tokyo**:缺失内建/全局函数直接 throw;`Statement` 是结构化对象而非字符串;`Scope` 三层;`Exit` 与 `Return` 区别;`dump()` 空实现。
|
||||
- **passability-and-pathfinding**:同一契约两个实例;图层必须同源(182);face 未注入构图 null;起点越界(183);terminal 只作终点;`NoPath` 正常;`TargetUnder` 不移动;换楼层重绑;瞬移跳过途经格。
|
||||
- **core-state**:`load()` 必须迭代;四字段 config;重复 saveable id(112);executor 未注册(113);缺 key(177)/多余 key(178);插件未 Loaded(81);`loadState` 期间录像禁用。
|
||||
- **plugin-internals**:依赖启动前固定;环(76)/缺失(77)/下游阻断;重复 id(78);状态不符(80);`dataDeps` 与 `clientDeps` 分离;`getDataPlugin` 判空 + 状态检查。
|
||||
|
||||
## Pitfalls
|
||||
|
||||
### Pitfall 1: 把「模型」写成「源码导读」
|
||||
**What goes wrong:** 一上来就列 `file:line` 或解释私有字段,读者不知道系统在做什么。
|
||||
**Why it happens:** D-80 禁止源码叙述,D-82 要求先讲模型;研究笔记里的 file:line 不能直接搬进正文。
|
||||
**How to avoid:** 每页先用 2-4 段讲「概念/组成/数据流」,file:line 只留在研究文件里,正文用接口名与行为描述。
|
||||
**Warning signs:** 正文出现「源码第 X 行」「这个私有字段」。
|
||||
|
||||
### Pitfall 2: 重复高级 I/II 已讲内容
|
||||
**What goes wrong:** `passability-and-pathfinding` 写成 `ii/pathfinding.md` 的翻版;`plugin-internals` 重讲插件写法;`replay-internals` 重讲自定义指令。
|
||||
**Why it happens:** 主题相邻,且 II 已把「怎么用」讲清。
|
||||
**How to avoid:** 每页开头写「本篇只讲 X;Y 见某页」,并在成稿后对照 II 的标题删重复。
|
||||
**Warning signs:** 页面里出现 `moveTo` 的完整用法、`IReplayCommand` 的完整示例、插件三时机的入门解释。
|
||||
|
||||
### Pitfall 3: 把旧录像写成当前 API
|
||||
**What goes wrong:** 读者照着 `core.status.route.push('item:...')` 写代码。
|
||||
**Why it happens:** 旧引擎代码仍在 `public/libs/*`,容易误读。
|
||||
**How to avoid:** 旧实现只放在「对比」节,明确标注「旧引擎基线,不是当前接口」;当前接口用 `ReplayArray` / `ReplaySystem`。
|
||||
**Warning signs:** 正文出现 `core.status.route`、`encodeRoute`、`LZString` 而没有「旧」标注。
|
||||
|
||||
### Pitfall 4: 把未实现/编辑器内容写成已实现
|
||||
**What goes wrong:** 写序列化事件注册、地图事件 id 绑定、编辑器识别规则、渲染端加载 UI 生命周期。
|
||||
**Why it happens:** 这些是明显的「下一步」,但当前不在数据端实现范围。
|
||||
**How to avoid:** 按 D-47/D-73 用占位标记;事件页只写 `collectEvent` / `BuiltInFunction`;加载页只写 loader 接口消费。
|
||||
**Warning signs:** 正文出现「在编辑器里」「地图事件 id 绑定」「ClientCore.afterLoad」。
|
||||
|
||||
### Pitfall 5: 代码块不完整 / 无法高亮
|
||||
**What goes wrong:** 只给一个方法体或一个对象字面量,读者无法放进类里。
|
||||
**Why it happens:** 研究笔记习惯摘片段。
|
||||
**How to avoid:** D-62/D-70:给完整类/函数/文件级片段;用注释省略无关成员;示例代码加解释性注释(D-65)。
|
||||
**Warning signs:** 代码块以 `function` 开头但没有闭合、或只有 `foo() { ... }`。
|
||||
|
||||
### Pitfall 6: 链接与导航漏注册
|
||||
**What goes wrong:** 页面写了但侧边栏没有入口;或 `docs/advanced/index.md` / `ii/index.md` 仍说「高级 III 尚未编写」。
|
||||
**Why it happens:** `config.ts` 的 sidebar 是手写数组;III 分组当前 `items: []` [VERIFIED: docs/.vitepress/config.ts:234-238]。
|
||||
**How to avoid:** 计划中必须有 `config.ts` 注册步骤 + 替换两处占位 + 构建 gate。
|
||||
**Warning signs:** 预览站点「高级 III」分组为空;构建 dead link。
|
||||
|
||||
### Pitfall 7: 路径假设错误(I 是否已移动)
|
||||
**What goes wrong:** III 页链接写成 `../plugin-system.md`,但 I 已被移到 `../i/plugin-system.md`,或反之。
|
||||
**Why it happens:** 本批与「高级 I 移动到 `docs/advanced/i/`」可能并行。
|
||||
**How to avoid:** 计划先确认移动是否同批完成;若同批,统一用 `../i/...`;若未移动,统一用 `../...`,并在移动批中一次性替换。不要两种混用。
|
||||
**Warning signs:** 构建 dead link,或链接指向 `docs/advanced/plugin-system.md` 但文件已在 `docs/advanced/i/`。
|
||||
|
||||
## Source/comment conflicts(D-72:只在对话报告,不写进文档)
|
||||
|
||||
以下均为本会话阅读时发现的**注释/实现或测试/实现不一致**。按 D-72,文档一律以方法注释为准;这些条目**只向用户报告**,不写入 `docs/` 正文。是否算缺陷由用户确认,本文件不擅自判定。
|
||||
|
||||
| # | 位置 | 观察 | 影响 |
|
||||
|---|------|------|------|
|
||||
| C1 | `packages/loader/src/manager.ts:88-94` | `getTotalByte()` 的注释是「获取总体需要加载的字节数」,实现却 `return this.loaded;`;`getByteRatio()` 用 `this.total` | 渲染端加载 UI 用 `getTotalByte()` 画总字节,显示可能不对;文档不要写「返回总字节」 |
|
||||
| C2 | `packages-user/client-modules/src/client.ts:193-206` | `afterLoad()` 对每个 client plugin 调用的是 `plugin.initialize(this, ...)`,不是 `plugin.afterLoad(...)`;状态却置 `Loaded` | 渲染端插件 `afterLoad` 可能永远不执行;渲染端加载/插件生命周期不宜写进文档 |
|
||||
| C3 | `packages-user/data-state/src/core.ts:130,139-155` vs `packages-user/data-state/__test__/coreNode.test.ts:7-12` | 构造器读 `config.events` 并 `for (const define of config.dataPlugins)`;测试只传 `loadStarter` / `coreURL`,没有 `dataPlugins` / `events` | 该测试写法与构造器契约不一致,可能无法运行;文档必须要求完整 `ICoreStateConfig` |
|
||||
| C4 | `packages-user/data-state/src/loader/loader.ts:27-29,84-85` | 注释说「当前是否正在加载/已经加载完毕」,但 `loading` / `dataLoaded` 从未赋值;`start()` 的重复进入保护实际无效 | 不要文档化「start 只会执行一次」;如需重复加载,行为未定义 |
|
||||
| C5 | `packages-user/data-state/src/loader/loader.ts:120-126` | 额外配置的 `task.loaded().then` 里检查 `configMap.has(id)` 并告警 68;Phase 8 记录该码不可达(`extraConfigs` 是 Map 键去重) | 不要文档化「重复 id 会稳定告警 68」 |
|
||||
| C6 | `packages/anon-tokyo/src/interpreter.ts:11-18` | `BuiltInFunction.save?` 字段存在,但 `interpreter.ts` 中没有读取它 | 不要给 `save` 赋予「自动存档/录像」语义 |
|
||||
| C7 | `packages/anon-tokyo/src/interpreter.ts:108` | `AnonTokyoExecutionContext.dump()` 是空实现 | 不要写成可用的执行上下文导出功能 |
|
||||
| C8 | `packages-user/data-system/src/path/system.ts:83-86` | `startMove` 先 `mover.tp(...)`,再触发 `onBeforeTeleportTo`;接口注释写「在瞬移自动寻路前触发」 | 文档按注释写「瞬移前」会与实现时序不一致;建议正文只写「瞬移分支中触发」,并在对话报告待用户裁决 |
|
||||
|
||||
> C1/C2 与渲染端相关,C3/C4 与加载/CoreState 相关,C5 与配置重复相关,C6/C7 与解释器相关,C8 与寻路瞬移钩子时序相关。它们都不影响本批「模型 + 接口」的写作,但会影响示例能否照抄,计划应避免把疑点写进正文。
|
||||
|
||||
## Build/verification commands(沿用前批 Phase 10 门禁)
|
||||
|
||||
- **格式化(D-45)**:`node node_modules/prettier/bin/prettier.cjs --write docs/advanced/iii/*.md`;收尾用 `--check` 确认 unchanged。
|
||||
- **构建**:`cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"`;期望输出 `build complete` 且无 `dead link`。`docs/.vitepress/apiSidebar.ts` 当前不存在(本会话 `Test-Path` = False),必须先跑 `init.ts`。
|
||||
- **预览**:`cmd /c "pnpm docs:preview"`(预览 `public/_docs`)。
|
||||
- **结构自检**:kebab-case 文件名、CRLF、单 H1、文末 `返回[高级 III 导览](./index.md)`、每页至少一个 `../ii/...` 或 `../i/...` 链接(按移动状态)。
|
||||
- **PowerShell 注意**:本机执行策略禁止 `pnpm.ps1`(本会话实测 `PSSecurityException`);一律用 `cmd /c "pnpm ..."` 或 `node node_modules/prettier/bin/prettier.cjs`。
|
||||
- **前批证据**:`10-31-READER-REVIEW.md:76,96-97,128-129`、`10-32-PLAN.md:131-132` 使用同一组 prettier + build + `build complete`/`dead link` 门禁 [CITED: .planning/phases/10-docs-restructure/10-32-PLAN.md:131-132]。
|
||||
|
||||
## Environment Availability
|
||||
|
||||
| Dependency | Required By | Available | Version | Fallback |
|
||||
|------------|------------|-----------|---------|----------|
|
||||
| Node.js | VitePress / init.ts | ✓ | v24.21.0 | — |
|
||||
| pnpm | 依赖与脚本 | ✓(经 `cmd /c`) | 12.5.1 | `node node_modules/...` 直接调用 |
|
||||
| `tsx` | `init.ts` | ✓ | `^4.21.0` | `node node_modules/tsx/dist/cli.mjs` |
|
||||
| `vitepress` | 构建 | ✓ | `^1.6.4` | — |
|
||||
| `docs/.vitepress/apiSidebar.ts` | `config.ts` 静态 import | ✗(本会话实测不存在) | — | 先跑 `tsx docs/.vitepress/init.ts` |
|
||||
|
||||
**Missing dependencies with no fallback:** 无。
|
||||
**Missing dependencies with fallback:** `apiSidebar.ts` → `init.ts` 生成。
|
||||
|
||||
## Validation Architecture
|
||||
|
||||
> `workflow.nyquist_validation` 未在 `.planning/config.json` 中显式关闭,按启用处理。
|
||||
|
||||
### Test Framework
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Framework | Vitest `^4.0.18`(仓库既有) |
|
||||
| Config file | `vite.config.ts` 的 `test` 块(`testTimeout` / `hookTimeout` 30000) |
|
||||
| Quick run command | `cmd /c "pnpm exec prettier --check docs/advanced/iii/*.md"` |
|
||||
| Full suite command | `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` |
|
||||
|
||||
### Phase Requirements → Test Map
|
||||
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|
||||
|--------|----------|-----------|-------------------|--------------|
|
||||
| D-09 | 构建通过 + 链接有效 | smoke | `cmd /c "pnpm exec tsx docs/.vitepress/init.ts && pnpm docs:build"` | ✅(脚本既有) |
|
||||
| D-45 | markdown 格式化 | format | `cmd /c "node node_modules/prettier/bin/prettier.cjs --check docs/advanced/iii/*.md"` | ✅ |
|
||||
| D-82 | 每页先模型后接口 | manual | 人工审阅(无自动检查) | ❌ 人工 |
|
||||
| D-61 | 边界单列文末 | manual | 人工审阅 | ❌ 人工 |
|
||||
| D-77 | 使用/自定义重心 | manual | 人工审阅 + 读者概率表(D-78) | ❌ 人工 |
|
||||
|
||||
### Sampling Rate
|
||||
- **Per page commit:** prettier `--check` + 该页链接人工点检。
|
||||
- **Per wave merge:** `tsx docs/.vitepress/init.ts && pnpm docs:build`。
|
||||
- **Phase gate:** 全量构建 green、dead link = 0、用户人工审阅通过。
|
||||
|
||||
### Wave 0 Gaps
|
||||
- [ ] `docs/.vitepress/apiSidebar.ts` — 构建前由 `init.ts` 生成,不检入。
|
||||
- [ ] `docs/advanced/iii/` 目录与 8 个文件 — 本批新增;文件名待 D-13 批准。
|
||||
- [ ] `docs/.vitepress/config.ts` 的 III sidebar 条目 — 本批编辑;当前 `items: []`。
|
||||
- [ ] `docs/advanced/index.md` / `ii/index.md` 的 III 占位替换 — 本批编辑。
|
||||
- [ ] 无新增测试文件;文档验收以 build + 人工审阅为准。
|
||||
|
||||
## Security Domain
|
||||
|
||||
本批只写 markdown,不引入运行时依赖、不处理用户输入、不包含密钥。VitePress 允许原始 HTML/mermaid;文档内容为仓库可信内容,不嵌入外部脚本或不可信 HTML。
|
||||
|
||||
| ASVS Category | Applies | Standard Control |
|
||||
|---------------|---------|-----------------|
|
||||
| V2 Authentication | no | 文档无认证 |
|
||||
| V3 Session Management | no | 文档无会话 |
|
||||
| V4 Access Control | no | 文档无权限 |
|
||||
| V5 Input Validation | yes(弱) | 不嵌入不可信 HTML/脚本;代码示例不执行 |
|
||||
| V6 Cryptography | no | 不涉及 |
|
||||
|
||||
| Pattern | STRIDE | Standard Mitigation |
|
||||
|---------|--------|---------------------|
|
||||
| 文档中嵌入外部脚本/iframe | Tampering | 不写 `<script>` / 外部 iframe;只用 VitePress 容器与 mermaid |
|
||||
| 示例泄露密钥/路径 | Information Disclosure | 示例只用占位路径与公开接口名 |
|
||||
| 旧引擎代码被误当当前 API | Tampering | 旧实现只放在对比节并显式标注 |
|
||||
|
||||
## Assumptions Log
|
||||
|
||||
| # | Claim | Section | Risk if Wrong |
|
||||
|---|-------|---------|---------------|
|
||||
| A1 | 高级 I 文档会移动到 `docs/advanced/i/`,因此 III 页用 `../i/...` 链接 | Page Split / Pitfalls | 链接全错,构建 dead link;需按实际移动状态统一替换 |
|
||||
| A2 | 8 页目录/文件名(`docs/advanced/iii/`、`loading-system.md` 等)会获 D-13 批准 | Page Split | 需重命名并改 sidebar/链接 |
|
||||
| A3 | 渲染端加载只写接口级小节即可满足读者 | loading-system | 若读者需要渲染端细节,需另派批次;当前渲染端架构不稳定 |
|
||||
| A4 | 「自定义事件触发器」主要指 `BuiltInFunction`,不是新增 `EventTrigger` 枚举值 | event-system | 若用户指后者,需补「改 `packages-user` + 派发点」的引擎级说明 |
|
||||
| A5 | 旧录像基线是 `public/libs/control.js` 等旧引擎,而不是某个更早的新引擎版本 | Old vs New | 对比事实可能对不上;需用户确认基线 |
|
||||
| A6 | `ReplayArray` 当前实现已修复 Phase 6 记录的编解码缺陷(本会话读到的是修复后代码) | replay-internals | 若仍有缺陷,文档示例可能踩坑;需用户确认 |
|
||||
|
||||
**If this table is empty:** 不适用;以上 6 条需要用户确认或计划中显式处理。
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **目录/文件名是否批准?**
|
||||
- What we know: 建议 `docs/advanced/iii/` + 8 个 kebab-case 文件。
|
||||
- What's unclear: D-13 要求命名先反馈;用户可能偏好不同文件名(如 `replay-system.md`、`events-internals.md`)。
|
||||
- Recommendation: 计划 Task 0 先向用户确认目录/文件名,再写正文。
|
||||
|
||||
2. **高级 I 是否同批移动到 `docs/advanced/i/`?**
|
||||
- What we know: 当前 I 文档在 `docs/advanced/*.md`,`advanced/index.md` 是 I 导览;`config.ts` 的 nav 指向 `/advanced/`。
|
||||
- What's unclear: 移动是否与本批同一 wave 完成。
|
||||
- Recommendation: 计划先确认;若同批,统一 `../i/...`;若不同批,III 先用 `../...`,移动批再统一替换。
|
||||
|
||||
3. **`anon-tokyo` 是否独立成页?**
|
||||
- What we know: 解释器模型足够独立,但篇幅可能偏短。
|
||||
- What's unclear: 用户是否希望它并入 `event-system`。
|
||||
- Recommendation: 先独立;若成稿 <150 行,合并到 event-system 的「脚本如何执行」一节,并请用户批准。
|
||||
|
||||
4. **`plugin-internals` 是否独立成页?**
|
||||
- What we know: `orderPlugins` + 失败模式 + 状态机内容足够。
|
||||
- What's unclear: 与 `core-state` 的边界是否会让读者觉得重复。
|
||||
- Recommendation: 独立;core-state 只讲装配顺序,plugin-internals 只讲排序/失败/状态。
|
||||
|
||||
5. **渲染端加载小节写到什么深度?**
|
||||
- What we know: `LoadScene` 只用 loader 接口;`ClientCore.afterLoad` 有疑点;`dev.md:136` 说渲染端架构仍要重构。
|
||||
- What's unclear: 用户是否希望提 `RenderLoaderHooks`。
|
||||
- Recommendation: 只写「渲染端也消费同一个 loader;配置由 `RenderLoaderHooks` 追加」,不写 UI/场景生命周期。
|
||||
|
||||
6. **C1–C8 是否要修?**
|
||||
- What we know: 都是本会话观察到的注释/实现或测试/实现不一致,按 D-72 只报告。
|
||||
- What's unclear: 是否属于用户已知的设计/进行中工作。
|
||||
- Recommendation: 本批不修、不写入文档;在计划汇报中列出,等用户裁决。
|
||||
|
||||
## Sources
|
||||
|
||||
### Primary (HIGH confidence)
|
||||
- `packages/loader/src/{manager,task,starter,types,stream}.ts` — 加载任务/管理器/starter。
|
||||
- `packages-user/data-state/src/{core,plugin,types,common,shared}.ts` — CoreState/插件/配置。
|
||||
- `packages-user/data-state/src/loader/{loader,types,jsoncProcessor}.ts` — MotaDataLoader。
|
||||
- `packages-user/data-common/src/replay/{array,system,sandbox,types,func}.ts` — 录像底层。
|
||||
- `packages-user/data-common/src/{event,store}/*` — 事件对象/仓库。
|
||||
- `packages-user/data-system/src/event/{system,executor,types}.ts` — 事件收集/执行。
|
||||
- `packages-user/data-system/src/path/{graph,finder,system,types}.ts` — 构图/搜索/系统。
|
||||
- `packages-user/data-base/src/{map,hero}/*` — IPassPredicate / 勇士移动。
|
||||
- `packages/anon-tokyo/src/{interpreter,type}.ts` — 解释器模型。
|
||||
- `src/data/**`、`src/client/{entry,initialize,ui/load}.tsx`、`src/bootstrap.ts`、`src/data.ts` — 游戏入口与初始化。
|
||||
- `packages-user/client-modules/src/{client,loader/hook}.ts` — 渲染端加载消费。
|
||||
- `public/libs/{control,core,utils,items,events,actions}.js` — 旧录像基线。
|
||||
- `docs/.vitepress/config.ts`、`package.json`、`dev.md` — 站点/脚本/项目约束。
|
||||
|
||||
### Secondary (MEDIUM confidence)
|
||||
- `docs/advanced/{index,plugin-system,saveable-content,replay-consistency,data-stores}.md`、`docs/advanced/ii/{index,pathfinding,point-and-tile-events,custom-replay-command}.md` — 现有风格与重叠边界。
|
||||
- `.planning/phases/10-docs-restructure/{10-CONTEXT.md,10-RESEARCH.md,10-31-READER-REVIEW.md,10-32-PLAN.md}` — 决策与门禁。
|
||||
|
||||
### Tertiary (LOW confidence)
|
||||
- 无(本批未使用 WebSearch;所有结论来自仓库源码/文档)。
|
||||
|
||||
## Metadata
|
||||
|
||||
**Confidence breakdown:**
|
||||
- Standard stack: HIGH — `package.json` / `config.ts` 直接读取。
|
||||
- Architecture: HIGH — 逐文件阅读核心实现;页面切分为 MEDIUM(依赖用户批准)。
|
||||
- Pitfalls: HIGH — 基于源码/现有文档的直接对照;C1–C8 为观察,不是缺陷判定。
|
||||
- Old vs new replay: HIGH — 旧/新源码逐行对照;A6 仍待用户确认。
|
||||
|
||||
**Research date:** 2026-10-26
|
||||
**Valid until:** 2026-11-25(30 天;若引擎继续重构,加载/CoreState 相关事实需重读)
|
||||
|
||||
## RESEARCH COMPLETE
|
||||
Loading…
Reference in New Issue
Block a user