[{"data":1,"prerenderedAt":921},["ShallowReactive",2],{"\u002F2026-03-30-77":3,"\u002F2026-03-30-77-rel":335},{"id":4,"title":5,"body":6,"column":320,"date":321,"description":12,"extension":322,"hero_image":323,"meta":324,"navigation":175,"path":325,"seo":326,"series_id":323,"severity":323,"stem":327,"summary":328,"tags":329,"__hash__":334},"posts\u002F2026-03-30-77个规则文件为什么一层通用规则不够用.md","77 个规则文件——一层通用规则不够用",{"type":7,"value":8,"toc":311},"minimark",[9,13,16,21,24,27,30,33,37,40,47,53,59,62,66,73,76,79,82,88,91,95,98,101,131,142,145,215,218,221,224,231,234,242,245,248,253,260,264,267,273,276,279,282,285,288,291,304,307],[10,11,12],"p",{},"建立规则系统时面临一个基础问题：通用规范和语言专用规范如何共存。",[10,14,15],{},"我采用的方案是分层存储：一份通用规则作为基础，11 个语言特定目录各自扩展，再加一份中文翻译层。77 个文件分布在 13 个目录里，但不是简单地重复 13 遍相同内容——每一层有明确的职责和覆盖关系。",[17,18,20],"h2",{"id":19},"问题单层平铺的陷阱","问题：单层平铺的陷阱",[10,22,23],{},"最直观的做法是把所有规则写进一个大文件。但这样做有三个问题。",[10,25,26],{},"首先是冗余。通用的编码原则（比如测试覆盖率 80%、禁止硬编码密钥）对所有语言都适用，不需要重复写 11 遍。",[10,28,29],{},"其次是维护成本。一处修改要同步到 11 个地方，漏掉一个就产生不一致。",[10,31,32],{},"第三是可迁移性差。选择采用 TypeScript 时不需要关心 Perl 的规则，把无关的内容混在一起反而增加认知负担。",[17,34,36],{"id":35},"方案三层分组","方案：三层分组",[10,38,39],{},"我把 77 个文件按职责分成三层。",[10,41,42,46],{},[43,44,45],"strong",{},"第一层是通用规则（common 目录，10 个文件）","。这里放所有语言都适用的原则：不可变性、文件大小上限（800 行）、错误处理策略、测试覆盖率（80%）、代码审查标准。这些是基线，整个体系的基座。",[10,48,49,52],{},[43,50,51],{},"第二层是语言特定规则（11 个语言目录，55 个文件）","。每个目录对应一门语言或语言族群（cpp 处理 C\u002FC++，typescript 覆盖 TypeScript 和 JavaScript）。语言特定目录里的 5 个文件（coding-style、testing、patterns、hooks、security）与通用层同名，但内容是该语言的方言。",[10,54,55,58],{},[43,56,57],{},"第三层是翻译层（zh 目录，11 个文件）","。这是通用规则的中文版本，与 common 目录的内容完全对应（多一份 README），满足中文环境的表述习惯。",[10,60,61],{},"数字加起来：10（common）+ 55（11×5）+ 11（zh）+ 1（rules\u002FREADME.md）= 77 个文件。",[17,63,65],{"id":64},"关键设计特定覆盖通用","关键设计：特定覆盖通用",[10,67,68,69,72],{},"分层的核心机制是",[43,70,71],{},"优先级约束","。语言特定的规则可以覆盖通用规则。",[10,74,75],{},"这个模型来自 CSS 特异性和 .gitignore 的优先级机制。不是\"多份规则随意叠加\"，而是\"特定优先，无特定则用通用默认\"。",[10,77,78],{},"具体例子：common\u002Fcoding-style.md 有一条铁律——\"始终返回新对象，禁止原地修改\"。这对 TypeScript、Python、Rust 等语言是对的，因为不可变数据模式防止隐藏的副作用。",[10,80,81],{},"但 Go 不同。惯用的 Go 代码使用指针接收器进行结构体变更——这是语言习惯，也是性能优化的标准实践。所以 golang\u002Fcoding-style.md 在文件开头就声明：",[83,84,85],"blockquote",{},[10,86,87],{},"This file extends common\u002Fcoding-style.md with Go specific content.",[10,89,90],{},"然后针对可变性做出例外说明。规则读者看 Go 项目时会先读通用层，再读语言层，遇到冲突时语言层赢。",[17,92,94],{"id":93},"陷阱安装时的覆盖风险","陷阱：安装时的覆盖风险",[10,96,97],{},"分层的好处显而易见，但也有一个隐藏的陷阱。",[10,99,100],{},"假设用这种方式安装规则：",[102,103,108],"pre",{"className":104,"code":105,"language":106,"meta":107,"style":107},"language-bash shiki shiki-themes github-light github-dark","cp rules\u002F* ~\u002F.claude\u002Frules\u002F\n","bash","",[109,110,111],"code",{"__ignoreMap":107},[112,113,116,120,124,128],"span",{"class":114,"line":115},"line",1,[112,117,119],{"class":118},"sScJk","cp",[112,121,123],{"class":122},"sZZnC"," rules\u002F",[112,125,127],{"class":126},"sj4cs","*",[112,129,130],{"class":122}," ~\u002F.claude\u002Frules\u002F\n",[10,132,133,134,137,138,141],{},"看似简洁，实际破坏了整个结构。因为通用目录和语言目录里",[43,135,136],{},"存在同名文件","（都有 coding-style.md、testing.md 等），展开通配符时语言特定文件会直接覆盖通用规则。README.md 里的 ",[109,139,140],{},"..\u002Fcommon\u002F"," 相对引用也会断掉。",[10,143,144],{},"README 的安装指南明确了正确做法：",[102,146,148],{"className":104,"code":147,"language":106,"meta":107,"style":107},"# 安装通用规则（所有项目必需）\ncp -r rules\u002Fcommon ~\u002F.claude\u002Frules\u002Fcommon\n\n# 安装语言特定规则\ncp -r rules\u002Ftypescript ~\u002F.claude\u002Frules\u002Ftypescript\ncp -r rules\u002Fpython ~\u002F.claude\u002Frules\u002Fpython\n# ...其他语言\n",[109,149,150,156,170,177,183,196,209],{"__ignoreMap":107},[112,151,152],{"class":114,"line":115},[112,153,155],{"class":154},"sJ8bj","# 安装通用规则（所有项目必需）\n",[112,157,159,161,164,167],{"class":114,"line":158},2,[112,160,119],{"class":118},[112,162,163],{"class":126}," -r",[112,165,166],{"class":122}," rules\u002Fcommon",[112,168,169],{"class":122}," ~\u002F.claude\u002Frules\u002Fcommon\n",[112,171,173],{"class":114,"line":172},3,[112,174,176],{"emptyLinePlaceholder":175},true,"\n",[112,178,180],{"class":114,"line":179},4,[112,181,182],{"class":154},"# 安装语言特定规则\n",[112,184,186,188,190,193],{"class":114,"line":185},5,[112,187,119],{"class":118},[112,189,163],{"class":126},[112,191,192],{"class":122}," rules\u002Ftypescript",[112,194,195],{"class":122}," ~\u002F.claude\u002Frules\u002Ftypescript\n",[112,197,199,201,203,206],{"class":114,"line":198},6,[112,200,119],{"class":118},[112,202,163],{"class":126},[112,204,205],{"class":122}," rules\u002Fpython",[112,207,208],{"class":122}," ~\u002F.claude\u002Frules\u002Fpython\n",[112,210,212],{"class":114,"line":211},7,[112,213,214],{"class":154},"# ...其他语言\n",[10,216,217],{},"不用通配符，逐个目录复制。这样保留了目录结构，相对引用才能生效。这是\"踩过才知道\"的约束——第一次装时很容易掉进去。",[17,219,220],{"id":220},"显式标注可覆盖项",[10,222,223],{},"但通用规则和语言规则之间哪些会冲突、哪些是兼容的，仅靠约定不够。",[10,225,226,227,230],{},"我在 README 里加了一个机制：",[43,228,229],{},"Language note 标记","。",[10,232,233],{},"在 common 目录里，可能被语言层覆盖的条目会标注一句：",[83,235,236],{},[10,237,238,241],{},[43,239,240],{},"Language note",": This rule may be overridden by language-specific rules for languages where this pattern is not idiomatic.",[10,243,244],{},"比如 common\u002Fcoding-style.md 的不可变性原则就带了这个标记。读规则的人一看到它，就知道\"这条原则大多数语言都遵守，但可能有例外\"。对应的语言目录里如果有覆盖，就形成了一个清晰的对话。",[10,246,247],{},"反过来，语言特定文件在覆盖时也要声明来源：",[83,249,250],{},[10,251,252],{},"Idiomatic Go uses pointer receivers for struct mutation — see common\u002Fcoding-style.md for the general principle, but Go-idiomatic mutation is preferred here.",[10,254,255,256,259],{},"这样做的好处是",[43,257,258],{},"可搜索","。grep 一下 \"Language note\"，所有潜在冲突点一目了然。新增语言时也知道哪些条目是\"容易冲突的\"。",[17,261,263],{"id":262},"规则与-skill-的分工","规则与 Skill 的分工",[10,265,266],{},"整套体系里还有一个角色是 skills——这个 2026 年 03 月的时间点还没有具体内容。",[10,268,269,270,230],{},"但分工边界已经定好了：",[43,271,272],{},"规则定标准，Skill 定做法",[10,274,275],{},"规则说\"测试覆盖率必须 80%\"是标准。Skill 说\"怎样用 pytest 构造 fixtures，怎样用 mock 隔离依赖\"是方法。规则说\"禁止硬编码密钥\"是底线。Skill 说\"把密钥存进 .env，用 dotenv 库加载\"是工具链实现。",[10,277,278],{},"规则通常是范围更广的原则和检查清单——应用到多个技术栈，形成同一个文化基准线。Skill 是特定任务的深入指南，可能只给某一个语言或框架用。",[10,280,281],{},"规则层的文件相对稳定（改一次要同步到 13 个目录有成本，所以需要重大理由）。Skill 可以快速迭代（技术动作变了，Skill 立即补）。",[17,283,284],{"id":284},"小结",[10,286,287],{},"77 个文件分成通用层、11 个语言目录和一套中文译本，看似复杂，实际是在规模和一致性之间的权衡。",[10,289,290],{},"关键的三个设计决策是：",[292,293,294,298,301],"ol",{},[295,296,297],"li",{},"特定优先——语言规则可以覆盖通用规则，类似 CSS 特异性的模型",[295,299,300],{},"结构保护——禁用通配符安装，逐目录复制，保留相对引用的完整性",[295,302,303],{},"冲突可见——用 Language note 标记潜在的覆盖点，避免隐藏的假设",[10,305,306],{},"这套结构一旦理解清楚，就成为一个快速导航点：看 common 知道所有项目的基线，看具体语言目录知道专项微调，看标记知道哪里有例外。",[308,309,310],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":107,"searchDepth":158,"depth":158,"links":312},[313,314,315,316,317,318,319],{"id":19,"depth":158,"text":20},{"id":35,"depth":158,"text":36},{"id":64,"depth":158,"text":65},{"id":93,"depth":158,"text":94},{"id":220,"depth":158,"text":220},{"id":262,"depth":158,"text":263},{"id":284,"depth":158,"text":284},"工程手记","2026-03-30","md",null,{},"\u002F2026-03-30-77",{"title":5,"description":12},"2026-03-30-77个规则文件为什么一层通用规则不够用","通用规则会自相矛盾——Go 的指针接收器和\"始终返回新对象\"直接冲突，而后者对 TypeScript 是对的。这是分层的起因。",[330,331,332,333],"规则系统","配置管理","代码标准","分层架构","VOb36qwNs4_qMXjkEcyX7qIRCQQvJMzYeTO_J4Duusw",[336,642,786],{"id":337,"title":338,"body":339,"column":320,"date":629,"description":343,"extension":322,"hero_image":323,"meta":630,"navigation":175,"path":631,"seo":632,"series_id":323,"severity":323,"stem":633,"summary":634,"tags":635,"__hash__":641},"posts\u002F2026-07-29-一年八千次提交AI辅助开发工作流.md","一年八千次提交，我是怎么干的",{"type":7,"value":340,"toc":612},[341,344,347,350,355,358,361,364,368,371,374,389,392,396,399,402,405,409,412,415,435,438,442,445,456,459,463,466,480,483,486,490,493,496,500,503,506,509,513,516,519,542,549,552,558,561,564,567,574,577,580,606,609],[10,342,343],{},"2026 上半年，主要项目累计提交 7787 次（其中 newCodex 因为是 fork 项目，1922 次提交含上游历史；纯新增代码的项目是 yun-claude、yun-claw、new-openclaw 等）。提交数看起来多，但每个提交背后的价值不在数量，而在流程的可重复性。这篇文章把工作流写清楚。",[17,345,346],{"id":346},"流程的六个环节",[10,348,349],{},"整个开发周期分六步：需求澄清 → 设计文档审阅 → 拆实施计划 → 分阶段实现 → 代码审查 → 故障归档。每一步都有明确的输入输出和验证方式。",[351,352,354],"h3",{"id":353},"_1-需求澄清","1. 需求澄清",[10,356,357],{},"开始前问清楚，不要猜。典型问题：这个功能要处理哪些用户场景？边界条件是什么？现有系统的哪部分会受影响？",[10,359,360],{},"这一步的产出是一份结构化的需求文档，包括功能范围、约束条件、风险假设。不是长篇幅的铺垫，而是一份清单式的澄清记录。",[10,362,363],{},"在 new-openclaw 项目里，这一步通常是用 Claude 的 superpowers:brainstorming skill 来展开。提问方式很重要：我会列出已知条件，然后问\"这个设计下会遇到什么问题？\"而不是\"你觉得应该怎么做？\"让 AI 在我的约束框架内工作。",[351,365,367],{"id":366},"_2-设计文档与人工审阅","2. 设计文档与人工审阅",[10,369,370],{},"这是整个流程里最关键的检查点。花一小时审一份 200 行的设计文档，比花五小时审一份 2000 行的代码便宜得多。而且回头修改设计的成本远低于修改实现。",[10,372,373],{},"new-openclaw 项目现有 30 份设计文档（specs 目录），每份文档都包括：",[375,376,377,380,383,386],"ul",{},[295,378,379],{},"范围：这个设计覆盖什么、不覆盖什么",[295,381,382],{},"决策与权衡：为什么选这个方案，放弃了什么",[295,384,385],{},"接口契约：如果涉及多个模块，清晰定义每个边界",[295,387,388],{},"风险清单：已知的坑和防护措施",[10,390,391],{},"写完设计文档以后，我会读一遍、问几个\"为什么\"，然后提出修改意见。这一步排除了 80% 的方向错误。常见的修改方向有：缩小范围（第一个版本不用处理那么多边界情况）、明确约束（系统资源、网络延迟、并发数的假设）、补充防护（熔断、限流、幂等）。",[351,393,395],{"id":394},"_3-实施计划与-dod-定义","3. 实施计划与 DoD 定义",[10,397,398],{},"设计文档定下来以后，拆成实施计划。计划的粒度是\"一个可验证的功能单元\"——通常是一个小时到半天的工作量，完成后能单独验证成功。",[10,400,401],{},"计划文档里必须写清完成定义（DoD，Definition of Done）。不是\"实现登录功能\"，而是\"写出能拒绝无效格式的登录端点、覆盖单点故障下的重试、有端到端的冒烟测试\"。",[10,403,404],{},"new-openclaw 项目现有 36 份实施计划（plans 目录），跨度从一周的 S0 阶段（骨架 + Mock 后台）到数周的 S1 阶段（真实业务后台）。每份计划都带着清晰的 checklist，这样我在执行时能随时问 Claude：\"下一步应该是什么？\"而不是脑子里模糊地记着进度。",[351,406,408],{"id":407},"_4-分阶段实现与逐步验证","4. 分阶段实现与逐步验证",[10,410,411],{},"有了计划以后，按顺序实现。关键是每一步都要有验证：单元测试、集成测试、或者一个小的 end-to-end 冒烟测试。验证不通过就停在这一步，不往下推。",[10,413,414],{},"这一步会用到三个代理：",[375,416,417,423,429],{},[295,418,419,422],{},[43,420,421],{},"superpowers:test-driven-development"," —— 先写测试，再写实现",[295,424,425,428],{},[43,426,427],{},"superpowers:subagent-driven-development"," —— 复杂任务拆成独立的子任务，并行推进",[295,430,431,434],{},[43,432,433],{},"superpowers:systematic-debugging"," —— 遇到测试失败，用这个代理追根溯源，不要盲目修改代码",[10,436,437],{},"实施过程中如果发现设计假设错了（比如某个接口响应时间远超预期，或者并发场景下出现竞态条件），就停下来回到第 2 步重新审视设计，而不是继续往下推。",[351,439,441],{"id":440},"_5-代码审查","5. 代码审查",[10,443,444],{},"实现完成以后，不是立即合并，而是过一遍 code-reviewer 代理。审查的重点不在代码风格（那个自动工具做），而在：",[375,446,447,450,453],{},[295,448,449],{},"这段代码实现的是设计文档里的哪一部分？偏离了吗？",[295,451,452],{},"错误处理有没有遗漏？边界情况有没有考虑？",[295,454,455],{},"有没有意外改动无关的代码？",[10,457,458],{},"审查通常能抓住两类问题。一类是逻辑问题：某个条件判断漏了一个分支，或者并发场景下两个操作的顺序反了。另一类是\"设计和实现对不上\"：实现了一个设计里没提到的特性，或者某个约束（比如\"这个值不能为空\"）没在代码里强制。",[351,460,462],{"id":461},"_6-故障归档","6. 故障归档",[10,464,465],{},"系统上线以后，bug 是难免的。重要的是怎么处理它。new-openclaw 项目有一套 bug 知识库规范（CLAUDE.md 里定义），每个 bug 修好以后都要新建一份独立文档，包括：",[375,467,468,471,474,477],{},[295,469,470],{},"现象和复现路径",[295,472,473],{},"根因分析：为什么会发生，触发链路是什么",[295,475,476],{},"修复方法：改了什么，为什么选这个方案",[295,478,479],{},"预防措施：代码改进、测试添加、还是构建期检查",[10,481,482],{},"80 份 bug 文档（截至 5 月中旬）不是问题的多，而是追根溯源的记录的多。下次遇到类似现象，能直接查库而不是重新排查。",[17,484,485],{"id":485},"三条铁律",[351,487,489],{"id":488},"_1-假设必须显式声明","1. 假设必须显式声明",[10,491,492],{},"不要猜。不清楚的地方就问，把问题写成澄清清单。\"这个 API 能处理多大的请求体？\"、\"离线场景下要缓存多长时间？\"、\"错误重试间隔是指数退避还是固定时间？\"。",[10,494,495],{},"这些问题看起来小，但决定了实现的复杂度和测试用例的多少。猜错了会导致前期设计精美，但方向错误，后面要推倒重来。",[351,497,499],{"id":498},"_2-修改必须精确","2. 修改必须精确",[10,501,502],{},"只改需要改的部分。这听起来像常识，但在实际工作中容易出现\"顺手改一下边上的代码\"的情况 —— 格式不规范了，变量命名不一致了，某个函数太长了，\"顺便\"重构一下。",[10,504,505],{},"结果是一个改动影响了五个文件，代码审查花了双倍时间，引入了新 bug 的风险。",[10,507,508],{},"规则是：改动必须对应需求的某一行。格式、风格、无关的重构，单独立项，不要混在功能改动里。",[351,510,512],{"id":511},"_3-成功标准必须可验证","3. 成功标准必须可验证",[10,514,515],{},"\"添加验证\"这个说法是模糊的。改写成：\"写出一个测试用例，输入非法邮箱格式，验证 API 返回 400；输入合法邮箱，验证返回 200 和预期数据结构\"。",[10,517,518],{},"每个 plan 文档里的 DoD 都是这样写的。拿一个阶段做例子：",[83,520,521,524],{},[10,522,523],{},"S0 阶段的 DoD：",[292,525,526,529,536,539],{},[295,527,528],{},"openapi\u002Fapi-v1.yaml 包含 spec 全部 11 个端点的字段级 schema，可被 swagger-ui 加载 ✓",[295,530,531,532,535],{},"backend\u002F Mock 服务 ",[109,533,534],{},"npm start"," 后能响应全部端点，返回符合契约的 mock 数据 ✓",[295,537,538],{},"launcher\u002F Rust 项目能编译为 launcher.exe，运行后完成所有阶段扫描并输出 diagnostics.json ✓",[295,540,541],{},"至少一个 end-to-end 冒烟测试通过（launcher 上报 → mock 后台收到 → 审计日志记录） ✓",[10,543,544,545,548],{},"每一条都能通过一个具体的命令来验证。\"能工作\"太模糊，\"运行 ",[109,546,547],{},"npm test"," 且所有测试通过\"才是可验证的。",[17,550,551],{"id":551},"流程失效的情况",[10,553,554,555,230],{},"这套流程在一个关键点会失效：",[43,556,557],{},"需求本身没想清楚时",[10,559,560],{},"我遇到过的例子是这样的。客户说\"要支持 USB 存储检测\"，这个需求很清楚，所以设计文档写了 20 多页，规划了三个阶段，列出了 30 多个 test case。然后两周后，客户补充说\"哦对了，还要处理网络驱动器\"。",[10,562,563],{},"这时前面的设计和计划都要回头改。检测逻辑复杂了，测试场景翻倍，阶段划分要调整。这不是流程的问题，这是需求的问题。流程本身反而帮助我及时暴露了这个风险 —— 如果没有设计文档，可能要到代码审查阶段，甚至系统上线以后才发现这个遗漏。",[10,565,566],{},"应对办法是在第 1 步（需求澄清）多花时间。列出你想到的所有场景，问\"还有其他我忽略的情况吗？\"。不是要求完全预测未来，而是把已知的不确定性显式写出来，而不是假设需求是固定的。",[10,568,569,570,573],{},"另一个失效的情况是",[43,571,572],{},"设计和实现的沟通不畅","。如果设计文档是给另一个人读的（或者给 AI 代理读的），但执行者没有理解透彻，实现出来会偏离设计。预防办法是在开始实施前，再过一遍设计文档，确认\"我清楚要做什么\"。",[17,575,576],{"id":576},"提交数字背后的故事",[10,578,579],{},"为什么能积累 7787 次提交？不是因为每次都在写新功能。真实的分布大概是：",[375,581,582,588,594,600],{},[295,583,584,587],{},[43,585,586],{},"功能实现","：40%",[295,589,590,593],{},[43,591,592],{},"设计文档编写和迭代","：25%",[295,595,596,599],{},[43,597,598],{},"测试编写","：20%",[295,601,602,605],{},[43,603,604],{},"bug 修复与回归测试","：15%",[10,607,608],{},"关键是这些提交都有上下文。每个提交的 message 都指向一个设计文档或一个 plan 的某个环节，或者一个 bug 记录。下次有问题要追查根因时，能快速定位到那个提交，看当时的设计决策是什么。",[10,610,611],{},"另外，有 80 份 bug 记录和 66 份设计 + 计划文档（30 specs + 36 plans）这件事本身说明了一点：文档不是负担，文档是工作的实际产出。代码只是文档的一个落地形式。",{"title":107,"searchDepth":158,"depth":158,"links":613},[614,622,627,628],{"id":346,"depth":158,"text":346,"children":615},[616,617,618,619,620,621],{"id":353,"depth":172,"text":354},{"id":366,"depth":172,"text":367},{"id":394,"depth":172,"text":395},{"id":407,"depth":172,"text":408},{"id":440,"depth":172,"text":441},{"id":461,"depth":172,"text":462},{"id":485,"depth":158,"text":485,"children":623},[624,625,626],{"id":488,"depth":172,"text":489},{"id":498,"depth":172,"text":499},{"id":511,"depth":172,"text":512},{"id":551,"depth":158,"text":551},{"id":576,"depth":158,"text":576},"2026-07-29",{},"\u002F2026-07-29-ai",{"title":338,"description":343},"2026-07-29-一年八千次提交AI辅助开发工作流","从需求澄清到故障归档，一套系统化的 AI 辅助开发流程：先设计文档过审，再分阶段实施验证，最后代码审查与故障存档，三条铁律支撑整个流程。",[636,637,638,639,640],"Claude","工作流","AI 辅助开发","代码审查","文档驱动","DN7sqbGmg2UybSvRXTMLW-dj0UURXET3KsSNeW_1V9I",{"id":643,"title":644,"body":645,"column":320,"date":773,"description":649,"extension":322,"hero_image":323,"meta":774,"navigation":175,"path":775,"seo":776,"series_id":323,"severity":323,"stem":777,"summary":778,"tags":779,"__hash__":785},"posts\u002F2026-07-20-远程桌面管理器DPAPI与60万次迭代.md","密码，不该由我保管",{"type":7,"value":646,"toc":767},[647,650,654,657,660,663,677,680,684,687,690,693,696,699,710,713,716,719,725,731,737,747,761,764],[10,648,649],{},"远程服务器资料管理工具需要妥善保存账户凭据。这个工具采用两层加密设计：本机存储用 Windows DPAPI，备份使用基于密码的密钥派生。两层各有边界，理解这些边界对使用决策至关重要。",[17,651,653],{"id":652},"第一层dpapi-的便利与代价","第一层：DPAPI 的便利与代价",[10,655,656],{},"本机存储的凭据使用 Windows DPAPI（Data Protection API）加密，由当前 Windows 用户身份保护。这是 Windows 内置的用户级加密机制，密钥由操作系统管理，与登录用户的 SID 和机器的本地安全数据库绑定。不需要用户记一个额外的主密码，启动应用即可直接使用保存的凭据。",[10,658,659],{},"DPAPI 的好处显而易见：启动应用即用，无需输入密码，用户体验最优。代价是它的加密密钥锁定在当前用户、当前机器。凭据无法直接迁移到另一台电脑或另一个 Windows 用户——这不是工具的限制，而是 DPAPI 的设计约束。Windows 操作系统就是这样设计的，其他工具也无法绕过。",[10,661,662],{},"实际操作中的含义很明确：",[375,664,665,668,671,674],{},[295,666,667],{},"重装 Windows 前必须先导出备份。原有凭据会因为用户 SID 变化和机器密钥更新而无法解密，即便登录同一账户也不行。",[295,669,670],{},"更换电脑前需要先创建备份并妥善保存备份密码，目标电脑上导入时需要重新输入这个备份密码。",[295,672,673],{},"在同一电脑上切换 Windows 用户登录，旧用户的凭据对新用户完全不可见，因为加密密钥是用户级的。",[295,675,676],{},"多用户共享一台电脑的场景下，凭据不会跨用户暴露。",[10,678,679],{},"这些限制会在实际使用中暴露出来——比如忙于工作时重装系统忽略了导出，或者在公用工作电脑上多个人使用。正因为如此，工具强制要求提供备份机制。备份不是可选功能，而是必需的。",[17,681,683],{"id":682},"第二层备份加密的固定迭代设计","第二层：备份加密的固定迭代设计",[10,685,686],{},"备份采用 PBKDF2-SHA256（基于密码的密钥派生函数 2，使用 SHA-256 哈希）加密，固定执行 600,000 次迭代。用户创建备份时设置一个密码，导入时输入这个密码。PBKDF2 通过重复应用哈希函数来增加破解难度，迭代次数越多，从密码派生密钥所需的计算时间越长，攻击者进行暴力破解也需要投入成倍的计算资源。",[10,688,689],{},"600,000 次迭代的来源是什么？这是一个有意识的设计决定，而不是随意选择。迭代次数越多，暴力破解的成本越高，但加密和解密的耗时也越长。设计者需要在两者之间找到平衡点：够强以抵御现代硬件的破解能力，又不能强到让普通用户的导入操作变得难以忍受。600,000 次这个数字反映的是这个平衡的结果。",[10,691,692],{},"为什么固定而非可配置？这是一个纪律问题。可配置听起来更灵活、更给用户掌控权，但在实践中会诱使用户为了更快的备份导入速度而降低迭代次数，从而削弱安全强度。安全不应该由便利让步。人们往往倾向于选择快速方案，尤其当他们没有安全专业知识时。固定的迭代次数消除了这种选择权，保证了所有备份都有相同的防护等级。工具的职责是做出最合理的决定，而不是把这个决定推给用户。",[17,694,695],{"id":695},"性能实测与数据解读",[10,697,698],{},"在 Windows 11 构建机上，对 1 MiB 大小的备份数据进行加密与解密的完整往返，预热缓存后的连续五次耗时分别为：108.919 ms、104.079 ms、100.418 ms、94.933 ms、103.470 ms。中位数为 103.470 ms。这意味着从你按下\"导入备份\"到凭据被解密并加载到内存，大约需要 100 毫秒的等待时间。",[10,700,701,702,705,706,709],{},"这组数据收集的目的是",[43,703,704],{},"记录性能表现","。它提供了一个具体的参考：用户在 Windows 11 系统上可以预期导入备份的延迟大约是这个量级。这对评估工具的可用性很有用。但它明确",[43,707,708],{},"不","作为调整迭代次数的依据。这种表述听起来有些冗余，但它是设计纪律的一部分——必须写下来的目的是防止后续有人看到\"100 毫秒确实有点慢\"就建议降低迭代次数。防止的是这样的推理：因为性能数据显示延迟不够快，所以降低迭代次数。这个逻辑链条在安全工程中是禁止的。",[10,711,712],{},"相反，如果实践证明 100 毫秒对用户体验构成问题，正确的做法是要么接受这个成本作为安全性的代价，要么在未来硬件更新换代后自然加速。绝不是削弱密钥派生强度。性能和安全的权衡应该在上层的需求决策中做，而不是在密码学参数中做。",[17,714,715],{"id":715},"工具的明确边界",[10,717,718],{},"这个工具的安全设计有明确的保护范围和限制：",[10,720,721,724],{},[43,722,723],{},"DPAPI 层的限制","：本机凭据的安全性最终依赖于 Windows 用户密码。如果 Windows 账户被破解，攻击者用该账户登录电脑，DPAPI 解密会自动进行。如果用户以管理员身份运行工具（虽然不需要管理员权限），攻击者获得管理员权限后理论上也可能绕过某些保护。安全链的强度由最弱的一环决定——如果你的 Windows 用户密码很弱，或者电脑物理上被他人访问，DPAPI 的保护就名存实亡。",[10,726,727,730],{},[43,728,729],{},"备份密码的强度","：导入备份时用户设置的密码决定了备份的抗暴力破解能力。PBKDF2 提供的防护再强，也无法弥补一个简单密码的缺陷。\"123456\"这样的备份密码，在 600,000 次迭代和现代 GPU 的破解能力面前，可能在几秒到几分钟内被破解。",[10,732,733,736],{},[43,734,735],{},"系统策略的约束","：工具运行在 Windows 系统上，不会绕过任何系统级的安全机制。Windows SmartScreen 对未签名程序的警告、远程桌面连接的安全确认对话、Group Policy 的限制——这些工具都无法绕过。安装包为未签名的内部制品，Windows SmartScreen 会在首次运行时显示\"未知发布者\"警告。这不是工具的缺陷，而是系统安全策略的正常行为。",[10,738,739,742,743,746],{},[43,740,741],{},"加密设计的范围","：这套两层加密设计防的是",[43,744,745],{},"离线攻击","——攻击者获得了备份文件或本机的加密数据，在没有用户交互的情况下尝试破解。它防不了的情况：",[375,748,749,752,755,758],{},[295,750,751],{},"备份密码通过社工或偷看被直接获取",[295,753,754],{},"备份文件在网络传输过程中被中间人截获（如果使用不安全的传输方式）",[295,756,757],{},"凭据被恶意软件在内存中窃取（工具启动后、密码解密到内存这段时间内）",[295,759,760],{},"Windows 账户本身被已经登录电脑的恶意软件控制",[10,762,763],{},"对这些风险的防护需要用户在安全习惯和网络安全措施上自行补足——设置强密码、在信任的网络上操作、定期更新系统补丁、使用反恶意软件工具。",[10,765,766],{},"使用这套工具前要明确：本机凭据带来了便利，代价是将安全依赖在 Windows 用户身份上；备份凭据提供了迁移能力，代价是密码强度必须由用户自己把关。都不是\"一次设置永久安全\"的方案，都需要持续的安全意识和维护。",{"title":107,"searchDepth":158,"depth":158,"links":768},[769,770,771,772],{"id":652,"depth":158,"text":653},{"id":682,"depth":158,"text":683},{"id":695,"depth":158,"text":695},{"id":715,"depth":158,"text":715},"2026-07-20",{},"\u002F2026-07-20-dpapi60",{"title":644,"description":649},"2026-07-20-远程桌面管理器DPAPI与60万次迭代","两层加密保护远程桌面凭据，本机用 DPAPI 便利性换易用性，备份用 PBKDF2 固定 60 万迭代；为什么迭代次数不可配置，性能数据如何解读。",[780,781,782,783,784],"Windows","DPAPI","PBKDF2","凭据存储","加密设计","TzKUoZoFLtAsO_VcB1TOOdWOpwcoDj6AUwEdS6d7pv0",{"id":787,"title":788,"body":789,"column":320,"date":908,"description":793,"extension":322,"hero_image":323,"meta":909,"navigation":175,"path":910,"seo":911,"series_id":323,"severity":323,"stem":912,"summary":913,"tags":914,"__hash__":920},"posts\u002F2026-07-17-把设计文档变成可讲的演示.md","转不成演示的设计文档，本来就没讲清",{"type":7,"value":790,"toc":902},[791,794,800,803,806,809,812,830,837,841,844,850,860,869,872,875,878,881,884,887,890,893,896,899],[10,792,793],{},"一年积累了 200 多份设计文档，最初想法是直接拿这些文档去讲。结果发现这些东西不适合讲。设计文档是给人写的，演示文稿是给人听的，形式完全不同。",[10,795,796,797,230],{},"解决这个问题的思路不是\"写个通用 PPT 编辑器\"，而是\"从结构化文档一键生成演示\"。本质差异在这里——编辑器要处理用户的任意编辑行为，演示工具只要转换",[43,798,799],{},"已有的结构",[17,801,802],{"id":802},"为什么选单向转换而不是编辑器",[10,804,805],{},"设计文档有稳定的模板：背景、方案、架构、取舍、参考。这个顺序不是随意的，恰好就是讲一个设计时的叙述顺序。",[10,807,808],{},"用户不需要\"先生成再改\"，需要的是\"文档秒变幻灯片\"。一旦你改，就回到编辑器的坑里去了——要支持拖拽、删除、排版，工作量爆炸，而且多数用户不会调，生成好的东西就是定版。",[10,810,811],{},"这个判断来自实际数据。我的工具做两个决策：",[292,813,814,824],{},[295,815,816,819,820,823],{},[43,817,818],{},"产物是自包含 HTML","，不是 Office 文件（",[109,821,822],{},".pptx"," 需要可编辑格式，门槛高；HTML 在浏览器里就能放映，自包含意味着内联了所有 CSS、JS、图片）。",[295,825,826,829],{},[43,827,828],{},"内容由 LLM 生成，不开放编辑面板","（用一个\"预览挑选\"的两阶段流程，让用户在 3 种风格的封面里选一个，然后生成整份）。",[10,831,832,833,836],{},"这两个约束听起来很严格，实际上契合了需求的本质：",[43,834,835],{},"文档的目的是记录决策，演示的目的是讲述决策","。不需要演示过程中再改决策，改了就回到文档去改。",[17,838,840],{"id":839},"什么样的结构能转什么样的不能","什么样的结构能转、什么样的不能",[10,842,843],{},"设计文档要是能一键转成演示，必须满足几个条件。",[10,845,846,849],{},[43,847,848],{},"架构图可以直接映射","。我的演示工具支持 AI 生图，也支持用户指定图片 URL。当文档里写了架构设计时，LLM 看到这个描述会生成对应的图，然后内联到演示文稿里。舞台是固定的 1920×1080，所有图片容器有最小尺寸约束（GSAP 时间轴动画要能在任意时间点求值，不能靠动态布局）。",[10,851,852,855,856,859],{},[43,853,854],{},"关键数据用表格记录最不容易犯错","。表格这种结构容易转化——",[112,857,858],{},"待补：具体表格如何转成演示页的实现规则","。如果文档里的数据以段落文字形式写，转成演示时就卡住了，LLM 要从自然语言反推结构。",[10,861,862,865,866],{},[43,863,864],{},"取舍（trade-off）部分","：",[112,867,868],{},"待补：设计文档中的取舍说明如何映射成演示内容的规则",[10,870,871],{},"一个更深的观察：文档转不出来好演示，通常不是演示工具的问题，是文档本身没讲清楚。",[10,873,874],{},"我见过一类项目文档，有 20 多份模板文件，但只要换个配色其他全一样。这说明什么？说明那些\"模板\"实际上没有结构差异，只有视觉差异。结构不清，自然转不出演示。或者反过来说，如果要证明自己的文档结构是清晰的，试试能不能一键转成演示——转不出来就是信号，说明需要先梳理文档本身。",[17,876,877],{"id":877},"工具的硬约束",[10,879,880],{},"演示不同于其他产物，有独特的硬约束。我的工具选择了 1920×1080 的固定舞台，所有动画用 GSAP 时间轴。这些看起来像限制，实际上是为了保证可靠性。",[10,882,883],{},"固定舞台尺寸意味着设计者写风格预设时要算好留白和排版，不能寄希望于\"容器自适应就行了\"。GSAP 时间轴的特点是能在任意时间点求值（渲染管线会 seek 到任意帧截图），不能用 CSS animation 这种依赖真实时间流逝的东西。这限制了动效，但换来的是确定性——动效一定会在预期时间点发生，不会因为网络卡而错位。",[10,885,886],{},"生成流程分两个阶段，有个细节很实用。第一阶段只生成 3 个风格的封面单页，第二阶段才生成整份演示文稿。这个设计看似多一步，实际上优雅地解决了两个问题。一是给用户选择风格的机会（不是非此即彼的\"生成或不生成\"）；二是掩盖异步生图的等待时间（生图 1-2 分钟，正好被用户在选择封面时吸收了）。",[17,888,889],{"id":889},"从文档能否转演示看结构清晰度",[10,891,892],{},"最后回到起点。为什么要做这个工具？",[10,894,895],{},"表面原因是 200 多份文档用演讲方式讲会更有力。深层原因是这个过程本身就是对文档质量的检验。",[10,897,898],{},"一份设计文档如果结构清晰——背景交代得清，方案对比得充分，架构图画得明确，取舍理由说得透彻——那转成演示就是平移内容，不费劲。转不出来、或者转出来很别扭，就是信号，说明某个环节讲得不够好。",[10,900,901],{},"这个反馈机制比任何 review 注释都直白。\"这个表述为什么转不成幻灯片？\"往往能逼出真实的问题——\"哦，因为我其实还没想清楚这个方案为什么比另一个好\"。",{"title":107,"searchDepth":158,"depth":158,"links":903},[904,905,906,907],{"id":802,"depth":158,"text":802},{"id":839,"depth":158,"text":840},{"id":877,"depth":158,"text":877},{"id":889,"depth":158,"text":889},"2026-07-17",{},"\u002F2026-07-17",{"title":788,"description":793},"2026-07-17-把设计文档变成可讲的演示","不做通用PPT编辑器，只做文档→演示的单向转换；关键在识别文档的稳定结构。",[915,916,917,918,919],"演示文稿","文档结构","设计工具","HTML","GSAP","Reh87_TKXZ8nl5BhDhHzVOONAzwRS-Im8LTYrIfeuNk",1785406912236]