<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://straymark.dev/zh-CN/blog</id>
    <title>StrayMark Blog</title>
    <updated>2026-05-23T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://straymark.dev/zh-CN/blog"/>
    <subtitle>The StrayMark chronicle.</subtitle>
    <icon>https://straymark.dev/zh-CN/favicon.ico</icon>
    <rights>Copyright © 2026 Strange Days Tech.</rights>
    <entry>
        <title type="html"><![CDATA[二进制藏不住的东西]]></title>
        <id>https://straymark.dev/zh-CN/blog/what-the-binary-couldnt-hide</id>
        <link href="https://straymark.dev/zh-CN/blog/what-the-binary-couldnt-hide"/>
        <updated>2026-05-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[一次 polish Charter 浮现了十个潜伏 gap，以及一个终于得到命名的反模式]]></summary>
        <content type="html"><![CDATA[<p><em>一次本应是美容清理的 polish Charter，在六个小时内浮现了十个生产 gap —— 其中两个已经交付到 <code>main</code> 并且整整十天从未真正工作过。反模式获得了名字，模式作为 <code>fw-4.18.0</code> 被写入文档，而 CLI helper 被有意推迟。</em></p>
<!-- -->
<blockquote>
<p><em>"polish Charter 尝试做的第一件事 —— 从 <code>main</code> 启动 <code>./sentinel</code> 来执行 §5 的 smoke —— 以两个不同的 panic 失败了。"</em></p>
</blockquote>
<p>5 月 22 日，Sentinel adopter 以 RFC 形式提交了 Issue <a href="https://github.com/StrangeDaysTech/straymark/issues/199" target="_blank" rel="noopener noreferrer" class="">#199</a>，正是这样开头。当 thread 在五条评论更新后收尾时，计数已经从两个增长到十个。polish Charter —— Etapa 2 的关闭 Charter，原本被规划为一份 WCAG 审计与 quickstart 验证的清单 —— 用六个小时做了另外一件事：捕获了一类潜伏回归，而之前八个 Charter 的所有 per-Charter 测试套件都没能抓住它，<em>也</em>不可能抓住它，因为它们的写法决定了这一点。</p>
<p>这篇文章是对那个早晨为何发生的重建，对底层那个反复出现的形状是什么的追问，以及在 PR <a href="https://github.com/StrangeDaysTech/straymark/pull/200" target="_blank" rel="noopener noreferrer" class="">#200</a> 中所做的深思熟虑的决定：<strong>为反模式命名，但暂时不为它打造工具</strong>。这个循环对追读过前几篇 —— 关于<a class="" href="https://straymark.dev/zh-CN/blog/emergent-observation-design">涌现观察</a>与<a class="" href="https://straymark.dev/zh-CN/blog/pattern-1-and-pattern-2-chain-evolution">链条演化</a> —— 的读者来说很熟悉：这是两周内第三个沿着同一弧线结晶的模式：在 N=1 中浮现 → 为元模式命名 → 在 N=2 验证之前，推迟跨项目工具化。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="真正分量重的两个案例">真正分量重的两个案例<a href="https://straymark.dev/zh-CN/blog/what-the-binary-couldnt-hide#%E7%9C%9F%E6%AD%A3%E5%88%86%E9%87%8F%E9%87%8D%E7%9A%84%E4%B8%A4%E4%B8%AA%E6%A1%88%E4%BE%8B" class="hash-link" aria-label="真正分量重的两个案例的直接链接" title="真正分量重的两个案例的直接链接" translate="no">​</a></h2>
<p>在 polish 会话浮现的十个 gap 中，八个或者是环境依赖腐烂（<code>huma</code> 升级在 <code>*string</code> 参数上引入了 panic；Go 1.22 收紧了 <code>http.ServeMux</code> 的 wildcard 语义），或者是 runbook 漂移（§boot 中缺失的 env vars、混淆了互斥模式的 smoke 配方、关于 fake provider 标准输出的声明而 fake 实际上从未写过任何东西）。每个都值得修。没有哪个改写了规则手册。</p>
<p>真正改写了规则手册的那两个不一样。它们共享一个形状。</p>
<p><strong>US3 Preference Center，5 月 12 日交付。</strong> 当 Sentinel 发送邮件的 recipients 点击 footer 中类似 unsubscribe 的链接时，他们落在一个 JWT-in-path 的 URL 上，形如 <code>/preferences/&lt;token&gt;</code>。handler 是有意公共-按-合约的：JWT <em>就是</em> auth；不期望 <code>Authorization</code> header。handler 的 doc-comment 这样写着。集成测试用 <code>humatest</code> 写成，通过测试 adapter 直接挂载 handler 并确认：给定 path 中一个合法的 JWT，handler 返回正确的 HTML。CI 通过。feature 被交付到 <code>main</code>。</p>
<p>从未发生的事：任何在那个 handler 前面执行生产 middleware 链的事情。<code>internal/core/middleware/auth.go</code> 有一个 <code>publicPrefixes</code> 列表，列出绕过 <code>Authorization</code> 检查的路由前缀。<code>/preferences/</code> 不在这个列表里。整整十天，每个点击 unsubscribe 链接的 recipient 都从 middleware 那里收到一个 <code>401 missing authorization header</code>，请求甚至没有触达那个本来知道不该期望 Authorization 的 handler。Preference Center 在测试中可达，在生产中不可达。</p>
<p><strong>发送 pipeline 的 OTel 可观测性 instruments，同一周交付。</strong> 八个 metric instruments —— counters、histograms、gauges —— 在 <code>internal/core/metrics/commshub.go</code> 中声明，在启动时与 OpenTelemetry meter 注册。Charter 的 <code>Constitution Check §IV</code> 正式宣布 FR-039..042 可观测性门已满足：API 层是正确的，注册都通过了，dashboards 已经规划好。这八个 instruments 中有七个在<strong>整个 commshub 模块中拥有零个 <code>.Add()</code> / <code>.Record()</code> 调用位置</strong>。声明了，注册了，从未被调用。ops 在这些 series 之上构建的 dashboards 已经收了十天的零数据。polish Charter 的手动 smoke —— 启动一个 OTel Collector，生成几个 sends，grep collector 输出查找 FR-039..042 名称 —— 立即浮现了这一点。</p>
<p>两个案例都不奇特。两个都不是硬 bug。两个都是同一个机械错误的实例：一个 artifact 在一处被声明，而本应连线它的实现住在另一处，并且代码库或 CI 里没有任何东西在关联两者。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="反模式想要的那个名字">反模式想要的那个名字<a href="https://straymark.dev/zh-CN/blog/what-the-binary-couldnt-hide#%E5%8F%8D%E6%A8%A1%E5%BC%8F%E6%83%B3%E8%A6%81%E7%9A%84%E9%82%A3%E4%B8%AA%E5%90%8D%E5%AD%97" class="hash-link" aria-label="反模式想要的那个名字的直接链接" title="反模式想要的那个名字的直接链接" translate="no">​</a></h2>
<p>当同一个形状出现得足够多以至于值得一个名字时，名字就重要起来。在 #199 的第三条评论更新时，Sentinel 作者已经数出了同一个底层错误的四个子类：</p>






























<table><thead><tr><th>声明位置</th><th>连线位置</th><th>机械检查</th></tr></thead><tbody><tr><td>operator runbook 中记录的 env var</td><td>代码中的 <code>os.Getenv(...)</code>（或对应 stack 的等价物）</td><td>每个记录的 env var 都至少有一个消费者</td></tr><tr><td>metrics 包中声明的 metric instrument</td><td>handler 或 worker 代码中的 <code>.Record()</code> / <code>.Add()</code> 调用位置</td><td>每个声明的 instrument 至少被记录一次</td></tr><tr><td>渲染/嵌入 HTML 中引用的 URL（<code>&lt;script src=...&gt;</code>、<code>&lt;link href=...&gt;</code>）</td><td>注册在同一 API surface 上的路由</td><td>被服务的 HTML 中的每个 <code>src=</code>/<code>href=</code> 都解析到一个已注册的路由</td></tr><tr><td>标记为公共-按-合约的路由（doc-comment、专用标记）</td><td>auth middleware 的公共前缀列表中的条目</td><td>每个公共-按-合约的 handler 都有匹配的前缀条目</td></tr></tbody></table>
<p>那个一句话的统一者 —— 新治理文档以此开篇的那一句 —— 是：</p>
<blockquote>
<p><em>每个被声明的 surface artifact 都至少有一个可从真实 request 触达的连线位置。</em></p>
</blockquote>
<p>这个反模式的名字 —— 它在 <code>dist/.straymark/00-governance/POLISH-CHARTER-PATTERN.md</code> 中作为典范落地 —— 是<strong>声明了表层但未连线</strong>（Surface declaration without wiring）。那才是交付物。polish Charter 是发现工具，不是论点本身。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="为什么集成测试会系统性地漏掉这些">为什么集成测试会系统性地漏掉这些<a href="https://straymark.dev/zh-CN/blog/what-the-binary-couldnt-hide#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95%E4%BC%9A%E7%B3%BB%E7%BB%9F%E6%80%A7%E5%9C%B0%E6%BC%8F%E6%8E%89%E8%BF%99%E4%BA%9B" class="hash-link" aria-label="为什么集成测试会系统性地漏掉这些的直接链接" title="为什么集成测试会系统性地漏掉这些的直接链接" translate="no">​</a></h2>
<p>这一节值得展开，因为它塑造了这篇文章其余部分的形状。四个子类共有的失败模式是：标准集成测试 harness —— Go 中的 <code>humatest.NewTestAdapter</code>、TypeScript、Python、Rust 中的对应物 —— <em>直接</em>通过测试 API 挂载 handler。被测的 handler 由 fixture 正确连线了。生产组合的那一步 —— 路由注册在那里与 middleware 链相遇，与 env-var 清单相遇，与嵌入资源表相遇 —— 才是坏掉的那一步。CI 的绿灯对它声明的内容是诚实的：handler 在正确的 request 下返回正确的响应。它对以下事情什么也没说：request 在生产中是否能触达 handler，以及 handler 所依赖的 artifact 是否曾经被连线到 runtime。</p>
<p>把这件事读成对 <code>humatest</code> 的批评是诱人的。它不是。<code>humatest</code>（以及它的等价物）正在做它被设计来做的事：让你以隔离方式、快速地、不需要立起整棵组合树就测试 handler 逻辑。那种隔离是一个 feature。隔离的代价就是我们刚刚命名的那个 —— 而代价只在隔离之外的某些事浮现出分歧时才变得可见。polish Charter 是浮现那种分歧最便宜的方法，因为它做了没有任何 test fixture 在做的事：它启动真实的 binary，对它执行真实的、已记录的 operator 配方。</p>
<p>这是新 pattern doc 那项承重的主张。不是说 polish Charter 出于美容原因而有价值。而是说它是<strong>唯一</strong>让生产组合在外部可读的规范（operator runbook）面前被 end-to-end 执行的地方。如果你把 polish Charter 当作美容清理，你也就把那种浮现能力当作美容。Sentinel 的数据论证了相反的方向。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="决定b不是-b">决定：B′，不是 B<a href="https://straymark.dev/zh-CN/blog/what-the-binary-couldnt-hide#%E5%86%B3%E5%AE%9Ab%E4%B8%8D%E6%98%AF-b" class="hash-link" aria-label="决定：B′，不是 B的直接链接" title="决定：B′，不是 B的直接链接" translate="no">​</a></h2>
<p>RFC 提出了三个选项。PR #200 中的决定是它们都不照原样采纳 —— 称之为 B′。值得说出原因，因为结构性的决定比表面看上去更重要。</p>
<p><strong>RFC 的 Option B</strong> 要求一个 pattern doc 放在 <code>docs/patterns/</code> 之下。那个目录今天在 StrayMark 中并不存在，创建它会分裂项目的文档约定。已经住在正典里的经验模式 —— <code>FOLLOW-UPS-BACKLOG-PATTERN.md</code>、<code>CHARTER-CHAIN-EVOLUTION.md</code>、<code>EMERGENT-OBSERVATION-DESIGN.md</code>、<code>SPECKIT-CHARTER-BRIDGE.md</code> —— 都住在 <code>dist/.straymark/00-governance/</code>。它们共享一个 i18n 镜像基础设施（每个治理 doc 都有英文、西班牙文、简体中文的兄弟版本）。增加第三个文档 surface 会成倍放大 i18n 开销，并把项目的模式撒在两个家之间。把新 doc 留在 <code>00-governance/</code> 内部以零边际成本复用了既有基础设施。</p>
<p><strong>RFC 的 Option C</strong> 要求一个 <code>straymark charter polish-checklist</code> 或 <code>straymark analyze declared-vs-wired</code> 的 CLI helper。Sentinel 作者已经原型化了一个（CHARTERs 25/26/27，三个预备性 CI 守卫），并愿意把它种到 upstream。这里的决定是保守的：推迟。不是因为原型没有价值 —— 它有 —— 而是因为 Sentinel 是 N=1。那四个子类是<em>一个 adopter、一个 stack</em> 所浮现的。framework 必须预先考虑才能交付一个有用的跨项目 CLI 的第五、第六、第七个子类，目前还不存在，因为没有第二个 adopter 把它们推上来。我们走过这条路。<a href="https://github.com/StrangeDaysTech/straymark/blob/main/dist/.straymark/00-governance/FOLLOW-UPS-BACKLOG-PATTERN.md" target="_blank" rel="noopener noreferrer" class=""><code>FOLLOW-UPS-BACKLOG-PATTERN.md</code></a> 作为 v0 在 <code>fw-4.10.0</code> 中交付，并在那里住了一个半月，等待第二个 adopter 在它毕业为 <code>straymark followups</code> 子命令之前完成验证。同一道闸门在这里适用。新 doc 的 <code>## Open questions</code> 明确说了：「结晶为 <code>straymark analyze declared-vs-wired</code> CLI 子命令 …… 闸门：N=2 adopters。」</p>
<p><strong>B′ 是落地的版本。</strong> 一个新的治理 pattern doc 放在典范位置，以三种语言。Charter 模板里的第七个 <code>Format conventions</code> 项目符号，当作者关闭一个 Etapa 或 SpecKit <code>Polish</code> 阶段时把他们指向它。QUICK-REFERENCE <code>## Patterns</code> 表格里的一行。一个 <code>fw-4.18.0</code> 版本号 bump，附带在三十几份文档间的页脚级联。没有 CLI 子命令。没有新的 frontmatter 字段。没有 schema 变更。反模式有了名字；发现仪式有了一份文档；工具化在等。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="反复重演的弧线">反复重演的弧线<a href="https://straymark.dev/zh-CN/blog/what-the-binary-couldnt-hide#%E5%8F%8D%E5%A4%8D%E9%87%8D%E6%BC%94%E7%9A%84%E5%BC%A7%E7%BA%BF" class="hash-link" aria-label="反复重演的弧线的直接链接" title="反复重演的弧线的直接链接" translate="no">​</a></h2>
<p>三周前，follow-ups backlog 模式沿同一弧线结晶：一个 adopter 浮现了一个需求，模式在 v0 被命名，CLI helper 被推迟。两周前，链条演化的两个模式结晶：Pattern 1（pre-declare SpecKit refresh）和 Pattern 2（post-close audit-driven Batch N.4），由同一个 Sentinel adopter 浮现，在 <code>fw-4.16.0</code> 中被命名，并以 <code>straymark charter refresh-suggest</code> 作为软 helper 而非硬闸门。一周前，元模式 —— <code>EMERGENT-OBSERVATION-DESIGN.md</code> —— 编码了让上述所有观察首先成为可能的东西：形式化的交叉引用加上文化上的许可。这周，这个模式。</p>
<p>这是一个月里的四次结晶，全都来自同一个 adopter，全都遵循同一个形状：<strong>在 N=1 中浮现 → 为元模式命名 → 只在 N=2 之后才工具化</strong>。当一个模式以鲜明的方式浮现 —— 六小时里十个生产 gap 就是鲜明的 —— 的诱惑是跳过命名，直接去做工具。纪律恰好相反。名字承担了大部分工作。工具化在到来时，是被至少两个 adopter 所浮现的内容参数化的；建造在 N=1 之上时，它是对一个 stack 与一个团队失败模式的外推，并倾向于把那些选择固化成不通用的 framework 默认值。</p>
<p>这一轮迭代里有一个真正新的、值得标出来的东西。新 pattern doc 命名了一个 Sentinel 作者已经承诺要发表的可证伪预测：下一个 Etapa 的 polish Charter，在刚刚在 Sentinel CHARTERs 25/26/27 中落地的三个预备性 CI 守卫的对照下执行，应该浮现大约少 80% 的 gap。如果那个预测成立，把 Option C 从「open question」升级为「CLI 子命令」就获得了定量支撑。如果它失败 —— 如果下一个 polish Charter 仍然浮现十个 gap 但落在没被预料到的第五个子类里 —— 失败本身就是下一个数据点，并会在打造工具之前重塑规范。任何一种结果都有用。两种都还没被观察到。我们在等。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="关于刻意没有做的事的一则说明">关于刻意没有做的事的一则说明<a href="https://straymark.dev/zh-CN/blog/what-the-binary-couldnt-hide#%E5%85%B3%E4%BA%8E%E5%88%BB%E6%84%8F%E6%B2%A1%E6%9C%89%E5%81%9A%E7%9A%84%E4%BA%8B%E7%9A%84%E4%B8%80%E5%88%99%E8%AF%B4%E6%98%8E" class="hash-link" aria-label="关于刻意没有做的事的一则说明的直接链接" title="关于刻意没有做的事的一则说明的直接链接" translate="no">​</a></h2>
<p>Charter frontmatter 里没有 <code>phase: polish</code> 字段。没有 <code>straymark charter polish-checklist &lt;ID&gt;</code>。没有自动化 analyzer 在扫描 Go（或任何其他语言）代码中已声明但未连线的符号。pattern doc 把上述每一项都列为候选演化，明确以 N=2 或 Etapa-3 之后的回顾为闸门。在 <code>fw-4.18.0</code> 中它们一个都不存在。</p>
<p>这是有意的，而且值得公开说出来，因为对一个鲜明发现的自然反应是过度建造响应。每一项推迟都是用短期完备性换长期可移植性。一个 frontmatter 字段把 framework 锁定到下一个 adopter 可能不共享的词汇上。一个 CLI helper 把 framework 锁定到一个映射某个具体 stack 失败模式的 runtime 上。一个 analyzer 把 framework 锁定到扫描一种语言，而它的约定可能与下一个 adopter 的截然不同。任何这样的承诺都不应基于一个领域的信号来做，无论信号多干净。pattern doc 本身可以被修订；一个 CLI 子命令不能在不破坏 adopters 的情况下被收回。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="如果你读到这里我会建议你看什么">如果你读到这里，我会建议你看什么<a href="https://straymark.dev/zh-CN/blog/what-the-binary-couldnt-hide#%E5%A6%82%E6%9E%9C%E4%BD%A0%E8%AF%BB%E5%88%B0%E8%BF%99%E9%87%8C%E6%88%91%E4%BC%9A%E5%BB%BA%E8%AE%AE%E4%BD%A0%E7%9C%8B%E4%BB%80%E4%B9%88" class="hash-link" aria-label="如果你读到这里，我会建议你看什么的直接链接" title="如果你读到这里，我会建议你看什么的直接链接" translate="no">​</a></h2>
<p>如果你运营一个有基于 mock-adapter 的集成测试的代码库 —— 大多数代码库都是 —— 你可以做一项有用的内部练习，完全不需要采纳 StrayMark 的任何东西。挑你的团队最近交付的、同时具有（a）docs 一侧声明（env vars、OpenAPI specs、嵌入式 HTML、metric instruments）与（b）住在另一份文件里的连线位置的最新 feature。在一个干净的 shell 里启动 binary。把 runbook 中面向 operator 的配方端到端跑一遍。如果第一次就跑通了，你的代码库还没有这个模式所捕获的潜伏债务 —— 或者它有但你这次走运。如果跑不通，数一下 gap。这个数字就是你需要的校准，用来判断 polish-Charter-作为债务检测的仪式在你的项目里是否值得它的开销。</p>
<p>如果你已经采纳了 StrayMark 并且正在关闭一个用 <code>humatest</code> 风格 adapter 测试 handler 的 Etapa，新 pattern doc 已经为你 scoped 好四个子类检查。把 polish Charter 预算为 L，不是 XS 或 S。预期会有 emergent 的 follow-on Charter，而不是残余清理 scope creep。在 scoped 工作之前去读 <a href="https://github.com/StrangeDaysTech/straymark/blob/main/dist/.straymark/00-governance/POLISH-CHARTER-PATTERN.md" target="_blank" rel="noopener noreferrer" class=""><code>POLISH-CHARTER-PATTERN.md</code></a>，而不是在之后。</p>
<p>而如果你是 Go 之外某个 stack —— TypeScript、Python、Rust、Elixir —— 上的 adopter，那四个子类是与语言无关的，但具体的检查形态不是。在你的 stack 里浮现一个第五或第六子类正是模式要从 v0 毕业所需要的第二-adopter 信号。从「在你的项目里一个有趣的观察」到「在 StrayMark 正典中被命名」的路径与 #199 走过的是同一条：开一个 issue。开它的成本很低；让模式处于欠验证状态的成本是真实的。</p>
<hr>
<p><em>StrayMark <code>fw-4.18.0</code> —— Issue <a href="https://github.com/StrangeDaysTech/straymark/issues/199" target="_blank" rel="noopener noreferrer" class="">#199</a> · PR <a href="https://github.com/StrangeDaysTech/straymark/pull/200" target="_blank" rel="noopener noreferrer" class="">#200</a> · tag <a href="https://github.com/StrangeDaysTech/straymark/releases/tag/fw-4.18.0" target="_blank" rel="noopener noreferrer" class=""><code>fw-4.18.0</code></a>。Sentinel 锚点：<a href="https://github.com/StrangeDaysTech/sentinel/pull/93" target="_blank" rel="noopener noreferrer" class=""><code>AIDEC-2026-05-22-001</code></a> · <a href="https://github.com/StrangeDaysTech/sentinel/pull/94" target="_blank" rel="noopener noreferrer" class="">CHARTERs 25/26/27 PR</a>。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="charters" term="charters"/>
        <category label="governance" term="governance"/>
        <category label="polish-charter" term="polish-charter"/>
        <category label="sentinel" term="sentinel"/>
        <category label="anti-pattern" term="anti-pattern"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[智能体发现了一件没人要求它发现的事]]></title>
        <id>https://straymark.dev/zh-CN/blog/emergent-observation-design</id>
        <link href="https://straymark.dev/zh-CN/blog/emergent-observation-design"/>
        <updated>2026-05-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[为一个已在悄然运转的设计属性命名]]></summary>
        <content type="html"><![CDATA[<p><em>一个智能体在没人要求的情况下，主动指出 spec 已经过时。那个早晨的重建，以及让这件事成为可能的设计属性 —— 事后被编入 StrayMark 的第 8 条原则。</em></p>
<!-- -->
<blockquote>
<p><em>"嗨，我们处理过 Issue #153 了吗？"</em></p>
</blockquote>
<p>对话就这样开始了，今天，5 月 16 日，在上午。一个简短的、几乎只是例行确认的问题，准备开始做其他事情之前顺口一问。答案是没有 —— Issue #153 是故意保持开放的，等待第二个领域来验证这个机制，然后再将其固化（StrayMark 第 12 条原则：在正式纳入规范之前，需经过第二个领域的验证）。但这个问题打开了另一扇门，在那次对话结束时，我已经将一个在框架中默默运转了几个月的设计属性正式编入了文档 —— 这一切，都是由两天前发生的一件事所引出的。</p>
<p>这篇文章是对那段弧线的重建，因为它值得留存。不是因为结果 —— 一个新的规范文档、一条第 8 原则、一个已合并的 PR —— 而是因为它所揭示的机制。StrayMark 背后有一个人的项目：确保在智能体辅助开发中，负责所产出软件的工程师不会失去掌控。这件事暴露了一个推动这一方向的具体设计属性。</p>
<p>关于创作的一点说明：StrayMark 首先在我自己的项目上接受测试。Sentinel 就是其中之一 —— 私有项目，但它与 StrayMark 的交互记录在 StrayMark 仓库的公开 Issue 中，因此上下文是可链接的。我是这个框架的作者，也是第一个使用者。第 12 条原则要求我在宣布任何新机制为规范之前，等待第二个领域的验证；但第一个领域就是我自己，这篇文章毫不掩饰地承认这一点。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="事件经过一个智能体标记了没人要求它标记的事">事件经过：一个智能体标记了没人要求它标记的事<a href="https://straymark.dev/zh-CN/blog/emergent-observation-design#%E4%BA%8B%E4%BB%B6%E7%BB%8F%E8%BF%87%E4%B8%80%E4%B8%AA%E6%99%BA%E8%83%BD%E4%BD%93%E6%A0%87%E8%AE%B0%E4%BA%86%E6%B2%A1%E4%BA%BA%E8%A6%81%E6%B1%82%E5%AE%83%E6%A0%87%E8%AE%B0%E7%9A%84%E4%BA%8B" class="hash-link" aria-label="事件经过：一个智能体标记了没人要求它标记的事的直接链接" title="事件经过：一个智能体标记了没人要求它标记的事的直接链接" translate="no">​</a></h2>
<p><strong>两天前，5 月 14 日</strong>，在 Sentinel 中，一个智能体主动提出了一个我没有要求的观察。它正准备帮我填写 CHARTER-18 —— 这是一个已经很长的链条中的一项具体工作 —— 在写下任何一行代码之前，它说了大意如下的话：</p>
<blockquote>
<p><em>(从西班牙语原文翻译)</em> 有一个问题。<code>specs/002-commshub/plan.md</code> 在 4 月 21 日被冻结。此后经历了七个连续的 Charter，在该链条的 AILOG 记录中，有十二项实证经验尚未反映到 plan 中，这些经验在实质上影响了 US5 的范围。如果我们在阅读这份过时的 plan 之后填写 CHARTER-18，那么在下一个审计周期中，关闭前将会原子式地修复各种偏差 —— 由于过时前提的继承，出现至少一个严重或高优先级发现的概率大约为 50%。</p>
</blockquote>
<p>它还引用了具体的 AILOG ID，以及具体的代码引用。</p>
<p>没有人要求这个分析。没有配置任何触发器。没有任何 CLI 命令的目的是产生这样的输出。这个智能体是来帮我处理下一个任务的，而不是来审计项目状态的。但在为下一个任务做准备的过程中，它发现了 StrayMark 文档明确关联的两个来源之间存在偏差，并选择在继续之前将其标记出来。</p>
<p>当天下午我提交了 Issue <a href="https://github.com/StrangeDaysTech/straymark/issues/150" target="_blank" rel="noopener noreferrer" class="">#150</a> 作为 RFC，在下午 5 点到深夜之间，手动 spec 刷新规范被讨论并落地到 <code>fw-4.14.3</code>。第二天，<strong>5 月 15 日</strong>，我提出了 Issue <a href="https://github.com/StrangeDaysTech/straymark/issues/156" target="_blank" rel="noopener noreferrer" class="">#156</a>，将这次演练所揭示的两个模式上游化：<em>预声明 SpecKit 刷新</em> 和 <em>收尾后的 Batch N.4 修正</em>。到 <strong>5 月 16 日</strong> 凌晨，它们已在 <code>fw-4.16.0</code> 中以 <code>CHARTER-CHAIN-EVOLUTION.md</code> 的形式被正式纳入规范，附带专项遥测和 CLI 辅助命令（<code>straymark charter refresh-suggest</code>）。行为得到了复现，模式得到了固化，循环就此闭合。</p>
<p>但当我在 16 日同一天上午坐下来，以 Issue #153 开启对话 —— <em>"这个我们处理过了吗？"</em> —— 继而反问自己另一个问题 —— <em>"等等，究竟是什么让那个观察成为可能的？"</em> —— 我意识到这个循环只完成了一半。<code>fw-4.16.0</code> 中编入的是模式的<strong>应用</strong>。尚待编入的，是产生该模式的<strong>机制</strong>。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="用技术语言说出的一个人的问题">用技术语言说出的一个人的问题<a href="https://straymark.dev/zh-CN/blog/emergent-observation-design#%E7%94%A8%E6%8A%80%E6%9C%AF%E8%AF%AD%E8%A8%80%E8%AF%B4%E5%87%BA%E7%9A%84%E4%B8%80%E4%B8%AA%E4%BA%BA%E7%9A%84%E9%97%AE%E9%A2%98" class="hash-link" aria-label="用技术语言说出的一个人的问题的直接链接" title="用技术语言说出的一个人的问题的直接链接" translate="no">​</a></h2>
<p>我在与生成这篇文章的智能体的对话中是这样表述的：</p>
<blockquote>
<p><em>(从西班牙语原文翻译)</em> 那个智能体的行为让我有些惊讶 —— 它注意到了已积累的知识，如果不加以应用，将在 Charter 18 的实现中造成问题。也就是说，这既不是我要求它分析的任务，也不是某个提示词触发器被激活的结果，我认为这要归功于 StrayMark 的文档。我想更深入地了解这是如何发生的，我认为这是积极的，这样我们就可以利用这次经验，将其钩入 StrayMark 的习惯性行为 —— 这个从智能体及其与生成的文档的关系中涌现出来的模式非常有趣。</p>
</blockquote>
<p>我问的不是要实现一个功能。而是要理解一个机制，以便复现它。这个区别很重要。当你要求实现某个东西时，路径是清晰的：spec → 任务 → 代码。当你要求理解为什么某件事奏效时，路径则更为罕见：你必须诊断一个运转中的系统，识别是哪些部分使其运转，并将偶然的与结构性的分开。</p>
<p>当你在构建与 AI 智能体协作的工具时，这类问题有一种特别的紧迫性。因为如果奏效的是偶然，下一个智能体 —— 或者同一个智能体的下一个版本 —— 可能无法复现它。如果奏效的是结构，那么它就可以被刻意地保留下来。对于一个旨在让工程师掌控 AI 辅助开发的项目而言，这两者之间的差异，就是运气与设计之间的差异。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="审计straymark-的哪些部分让这个观察成为可能">审计：StrayMark 的哪些部分让这个观察成为可能？<a href="https://straymark.dev/zh-CN/blog/emergent-observation-design#%E5%AE%A1%E8%AE%A1straymark-%E7%9A%84%E5%93%AA%E4%BA%9B%E9%83%A8%E5%88%86%E8%AE%A9%E8%BF%99%E4%B8%AA%E8%A7%82%E5%AF%9F%E6%88%90%E4%B8%BA%E5%8F%AF%E8%83%BD" class="hash-link" aria-label="审计：StrayMark 的哪些部分让这个观察成为可能？的直接链接" title="审计：StrayMark 的哪些部分让这个观察成为可能？的直接链接" translate="no">​</a></h2>
<p>我让一个智能体系统性地梳理 StrayMark 的文档，带着三个具体问题：</p>
<ol>
<li class="">哪些文档机制明确地将各来源相互关联？（前置元数据、规范章节、稳定 ID、跨来源的 CLI 命令。）</li>
<li class="">文档在哪里给了智能体<strong>明确的许可</strong>，以标记超出所要求任务范围的事情？</li>
<li class="">除了引发本次案例的那对来源之外，还存在哪些来源对，如果基础设施就位，可能产生类似的涌现观察？</li>
</ol>
<p>报告以结构化形式返回，每个发现都附有文件:行号。重要的不是个别细节（带有 <code>originating_charter:</code> 的前置元数据、<code>§Risk: R&lt;N&gt;</code> 章节、稳定 ID 约定、<code>charter drift</code> 等命令），而是将它们放在一起审视时所涌现的模式。两个属性始终共存：</p>
<p><strong>属性一：强制的结构化交叉引用。</strong> 每种文档类型都有<em>必填字段</em>和<em>规范章节</em>，在文档自身的结构中声明它所指向的其他文档。当智能体阅读一份 AILOG 时，它不必猜测它属于哪个 Charter —— <code>originating_charter:</code> 会告诉它。当它看到一个风险时，它不是一段自由散文中的观察 —— 而是规范的、可计数的章节中的一个 <code>R&lt;N&gt;</code> 条目。当 spec 指向 Charter 时，是正式的；当 Charter 指向 AILOG 时，也一样。这是一个智能体无需创造性工作就可以三角定位的显式关联图。</p>
<p><strong>属性二：文化层面的许可，不设阻断式门控。</strong> AGENT-RULES §6 告诉智能体：<em>"保持主动 —— 识别潜在风险，在明显时提出改进建议，就技术债务发出警报"</em>。PRINCIPLES §2 告诉它：<em>"不隐藏相关信息"</em>。而关键的是，AGENT-RULES §3 赋予它无需操作员预先批准就可以<em>创建</em>文档（AILOG、AIDEC、TDE）的自主权。操作员保留的是优先级排序权，而非创建权。智能体不必请求许可才能标记某件事：标记它是规则的执行，而非价值判断。</p>
<p>有趣的是，这两个属性单独存在都无法产生该行为。如果你只有正式的关联（属性一）而没有文化许可，你会拥有一个可查询的语料库，但没有智能体敢主动查询它。如果你只有文化许可（属性二）而没有结构，你会得到模糊的标记 —— <em>"我觉得某个地方可能有问题"</em> —— 操作员无法据此行动。</p>
<p>两者结合，产生了值得拥有自己名字的东西：智能体将*"我应该说点什么吗？"<em>外化为</em>"有没有一个规范章节可以容纳这件事？"*。如果答案是肯定的，标记就不再是一个情绪化的决定，而成为机械式的执行。标记的成本降低了，因为目的地 —— 规范章节 —— 已经存在。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="为何这超越了-straymark-本身">为何这超越了 StrayMark 本身<a href="https://straymark.dev/zh-CN/blog/emergent-observation-design#%E4%B8%BA%E4%BD%95%E8%BF%99%E8%B6%85%E8%B6%8A%E4%BA%86-straymark-%E6%9C%AC%E8%BA%AB" class="hash-link" aria-label="为何这超越了 StrayMark 本身的直接链接" title="为何这超越了 StrayMark 本身的直接链接" translate="no">​</a></h2>
<p>我们正处于 AI 智能体写代码之快如思维流转的时代，<em>如何不失去对过程的掌控</em>的问题是真实且紧迫的。这不是一个末日论的问题 —— 这是一个操作层面、工艺层面的问题，由正在构建软件、需要交付的人们提出。这件事的速度本身就是证明：从智能体的诊断到被正式纳入规范的元模式，不到 <strong>72 小时</strong>。这种速度是一种收益，但也正是为什么可见性机制如此重要。</p>
<p>业界今天的主流回应，是<em>更严格的提示词、更多上下文中的规则、CI 中更多门控</em>的某种变体。这些方法是有效的，但有一个值得命名的特征：它们将智能体变得更像一个在可验证流水线中执行命令的工人。这解决了可靠性问题，但代价是牺牲涌现观察。一个只做被命令之事的智能体，无论其推理能力多强、提示词多长，都不会去标记没人要求它标记的事情。</p>
<p>Sentinel 事件所展示的是一种不同的、互补的回应：<strong>构建文档装置，使重要的偏差在结构上可见，并赋予智能体文化层面的许可，让它无需请求批准就可以标记这些偏差。</strong> 这将涌现观察保留为系统的一个属性，而非取决于智能体有多强或提示词有多长的运气。</p>
<p>这种立场中有一种近乎道家的意味：我们不告诉智能体要寻找什么；我们构建一个让需要被看见的事物可见的环境。"可见的结构 + 命名的许可"这对组合完成了工作。套用一些同事的话，智能体成为了活文档与代码状态之间的<em>诚实中间人</em>。</p>
<p>这正是 StrayMark 背后的人的项目所追求的，即使它很少被这样言说：让 AI 辅助开发令人眩晕的变化，不将我们推入一个工程师失去对所构建内容之可见性的世界。可见性不是通过更多的官僚控制来保留的；而是通过设计智能体所工作的环境来保留的，使重要的事情自然可见。工程师不会成为无尽 Pull Request 的审阅者 —— 而会成为智能体所带来的、已结构化且已命名的观察的优先级排序者。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="我们做了什么">我们做了什么<a href="https://straymark.dev/zh-CN/blog/emergent-observation-design#%E6%88%91%E4%BB%AC%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88" class="hash-link" aria-label="我们做了什么的直接链接" title="我们做了什么的直接链接" translate="no">​</a></h2>
<p>诊断清晰后，问题是如何将其钩入 StrayMark 的习惯性行为，同时又不扼杀其涌现特质。这个张力是真实的：每当你将一个自发行为变成义务，你就将其变成了另一种东西。硬性规则可以是健壮的，但也可能在不合时宜的情境中触发时产生智能体的抵触或虚假信号。</p>
<p>决定是保守的：<strong>命名元模式，而非强制要求其使用。</strong></p>
<p>具体而言，在 PR <a href="https://github.com/StrangeDaysTech/straymark/pull/160" target="_blank" rel="noopener noreferrer" class="">#160</a> 中，以 <code>fw-4.17.0</code> 发布：</p>
<ul>
<li class="">
<p>一个新的规范文档 —— <code>EMERGENT-OBSERVATION-DESIGN.md</code> —— 阐明了该设计属性及其两个组件，以 Sentinel 的实证案例为锚点，并构建了一个<em>实例金字塔</em>：元模式在顶端，其下是所有已正式纳入规范的应用（链条演进的模式 1 和模式 2；Charter 漂移；后续事项积压漂移；TDE 对比 <code>R&lt;N&gt;</code> 的升级；外部审计检查点）。之前看似各自独立的模式，被揭示为同一底层原则的应用。这个可视化本身就值得：当你看到七件看似不同的事情，突然认出它们有着共同的形状，框架就在没有新增任何东西的情况下获得了内在的一致性。</p>
</li>
<li class="">
<p>一条新的第 8 原则 —— <em>跨来源不一致性浮现（Cross-Source Dissonance Surfacing）</em> —— 在 <code>PRINCIPLES.md</code> 中。这是用五行字凝练的文化规则。智能体在进入项目时读到它，并递归地应用它。</p>
</li>
<li class="">
<p>扩展了 <code>AGENT-RULES.md §6 "保持主动"</code>，加入了值得关注的具体偏差示例：过时的 spec、未升级为 TDE 的累积 <code>R&lt;N&gt;</code>、被实现所矛盾的 ADR、跨越积压模式阈值的后续事项、收尾后出现的审计发现。这些不是义务；而是<em>一个主动的智能体会注意到什么</em>的<em>示例</em>。这个区别对于保留涌现特质很重要。</p>
</li>
<li class="">
<p>识别了四个开放轴作为空缺 —— 交叉引用基础设施部分存在但模式尚未命名的地方：MCARD ↔ 已部署的模型代码，SBOM ↔ 锁定文件，有效 ADR ↔ 矛盾的实现，Constitution Check ↔ 框架版本升级。每一个在固化之前都需要 N=1 的实证验证 —— 第 12 条原则适用。它们已被记录在 Issue <a href="https://github.com/StrangeDaysTech/straymark/issues/161" target="_blank" rel="noopener noreferrer" class="">#161</a> 中作为跟踪 RFC，等待第二个领域来推动其中之一。</p>
</li>
<li class="">
<p>明确的反模式：元模式是如何失效的。如果一个新的文档类型附带可选的前置元数据关联，交叉引用就会出现盲点。如果一个规范章节被自由散文取代，可查询性就会消失。如果在 AILOG/AIDEC/TDE 的创建中引入阻断式门控，文化许可就会破裂。如果遥测演进时没有保留 <code>r_n_plus_one_emergent_count</code> 等信号，反馈回路就会断裂。这些反模式是廉价但有效的保护，可防止意外侵蚀：任何未来的变更提案都可以与这份清单对照，以检测它是否在退化元模式。</p>
</li>
</ul>
<p>从诊断到合并以及发布的 <code>fw-4.17.0</code>，全程发生在同一天的上午 9 点到凌晨 4 点之间。十五小时。这正是这个博客所承认的那种节奏：你必须学会在这个时钟下运作，否则掌控就会溜走。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="我从这个过程中学到的">我从这个过程中学到的<a href="https://straymark.dev/zh-CN/blog/emergent-observation-design#%E6%88%91%E4%BB%8E%E8%BF%99%E4%B8%AA%E8%BF%87%E7%A8%8B%E4%B8%AD%E5%AD%A6%E5%88%B0%E7%9A%84" class="hash-link" aria-label="我从这个过程中学到的的直接链接" title="我从这个过程中学到的的直接链接" translate="no">​</a></h2>
<p>当我从回答*"不，Issue #153 还是开放的"*开始，有三件事是我没有预料到会学到的：</p>
<p><strong>第一，编入元模式与编入模式是不同的。</strong> 元模式不增加义务；它增加了<em>词汇</em>和对底层属性的<em>可见性</em>。那个属性已经在做它的工作；所缺少的是给它一个名字，以便在框架未来的演进中保护它。阅读 <code>EMERGENT-OBSERVATION-DESIGN.md</code> 的人不会学到什么新的做法 —— 他们会学会<em>认出</em>已经存在的东西。这才是让人能够刻意保留它的前提。</p>
<p><strong>第二，如果过度规范化，涌现属性就会消失。</strong> 在对话中有那么一刻，我选择不将模式 1 变成硬性义务（<em>"智能体在声明 Charter-(N+1) 之前必须运行 <code>refresh-suggest</code>"</em>），而是保留它作为建议，加上对元模式的命名。如果你来自*"更多控制 = 更多可靠性"*的世界，这个决定是反直觉的，但对于这类属性而言，它是正确的选择。智能体的主动性是脆弱的：一旦它成为强制要求，它就不再是主动性了。能够扩展的是文化引擎（"保持主动"）加上结构化关联，而非具体义务的清单。</p>
<p><strong>第三，我们为智能体构建的文档装置同时也在教育我们人类自己。</strong> 当我看到散布在各个治理文档中的七个模式，认出它们是同一元模式的实例时，我理解了一些我之前不曾理解的关于 StrayMark 的东西 —— 而 StrayMark 是我花了几个月构建的。框架正在向其自己的作者揭示东西。这个属性 —— 结构化文档在被审计时会<em>产生关于自身的洞见</em> —— 是智能体在标记偏差时所做之事的一个内部变体。是同一机制，应用于不同的尺度。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背后的人的项目">背后的人的项目<a href="https://straymark.dev/zh-CN/blog/emergent-observation-design#%E8%83%8C%E5%90%8E%E7%9A%84%E4%BA%BA%E7%9A%84%E9%A1%B9%E7%9B%AE" class="hash-link" aria-label="背后的人的项目的直接链接" title="背后的人的项目的直接链接" translate="no">​</a></h2>
<p>我是认真的，当我说 StrayMark 有一个人的项目。我不是因为痴迷于前置元数据字段而在构建一个文档治理框架。我是因为有一种直觉 —— 越来越不像是臆测 —— 软件工程的下五年将会是智能体所提供的速度与工程师需要保持的可见性之间持续谈判的五年。如果这场谈判向智能体一侧倾斜，我们会进入一个代码被编写而没有人知道为什么的世界。如果它向官僚控制倾斜，我们会失去证明这个实验合理性的生产力收益。</p>
<p>这个平衡不是理论性的。它是用具体的决定构建的：在哪里放一个前置元数据字段，哪个章节应该是规范的而非自由的，何时给予智能体自主权、何时保留优先级排序权。每一个决定都使平衡向一个方向或另一个方向倾斜。而知道平衡是否正确的唯一方法，就是测试它 —— 并在它奏效时命名奏效之物，以便它能在框架的下一次变更中存活下来。</p>
<p>两天前的 CHARTER-18 事件，是一次测试。它奏效了。我今天写下的，是给它一个名字。下一次测试已经有了候选者：MCARD、SBOM、有效 ADR 对比实现、Constitution Check ↔ 框架升级。四个轴，如果我们填补基础设施空缺，元模式可以在其上复现。我会等待第二个领域在实证上推动其中之一，然后再将其固化。第 12 条原则 —— 在固化之前需经过第二个领域的验证 —— 仍然是护栏。</p>
<p>如果你正在阅读这篇文章，并且你与智能体一起开发软件，我邀请你用这两个问题审视你自己的文档装置：<em>哪些来源是正式关联的，具有智能体无需创造性工作就可以三角定位的显式关联？</em> 以及 <em>当智能体检测到偏差时，对没被要求的事情进行标记，其成本有多高？</em> 如果第一个问题的答案是"很少"，第二个的答案是"很高"，你可能正将涌现观察留在桌上。这不是世界末日 —— 但这恰恰是一个<em>处于人类掌控下</em>的 AI 辅助开发过程，与一个逐渐溜走的过程之间的区别所在。</p>
<hr>
<p><em>StrayMark fw-4.17.0 — Issue <a href="https://github.com/StrangeDaysTech/straymark/issues/150" target="_blank" rel="noopener noreferrer" class="">#150</a> · <a href="https://github.com/StrangeDaysTech/straymark/issues/156" target="_blank" rel="noopener noreferrer" class="">#156</a> · <a href="https://github.com/StrangeDaysTech/straymark/issues/161" target="_blank" rel="noopener noreferrer" class="">#161</a> · PR <a href="https://github.com/StrangeDaysTech/straymark/pull/160" target="_blank" rel="noopener noreferrer" class="">#160</a> · tag <a href="https://github.com/StrangeDaysTech/straymark/releases/tag/fw-4.17.0" target="_blank" rel="noopener noreferrer" class=""><code>fw-4.17.0</code></a></em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="ai-agents" term="ai-agents"/>
        <category label="governance" term="governance"/>
        <category label="emergent-design" term="emergent-design"/>
        <category label="charter" term="charter"/>
        <category label="sentinel" term="sentinel"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Pattern 1 与 Pattern 2 —— 链演化]]></title>
        <id>https://straymark.dev/zh-CN/blog/pattern-1-and-pattern-2-chain-evolution</id>
        <link href="https://straymark.dev/zh-CN/blog/pattern-1-and-pattern-2-chain-evolution"/>
        <updated>2026-05-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[二十七小时后，手工纪律被命名为两个模式：Pattern 1 —— pre-declare SpecKit refresh；Pattern 2 —— post-close audit-driven Batch N.4。正典文档、遥测 schema、CLI helper 随之落地，再过一小时，一个把两者向上重新吸收的元模式出现了。]]></summary>
        <content type="html"><![CDATA[<p><em>二十七小时后，这套手工纪律被命名为两个模式：模式 1 —— pre-declare SpecKit refresh；模式 2 —— post-close audit-driven Batch N.4。正典文档、遥测 schema、CLI helper —— 再过一小时，一个把两者向上重新吸收的元模式出现了。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-二十七小时为它们命名">1. 二十七小时，为它们命名<a href="https://straymark.dev/zh-CN/blog/pattern-1-and-pattern-2-chain-evolution#1-%E4%BA%8C%E5%8D%81%E4%B8%83%E5%B0%8F%E6%97%B6%E4%B8%BA%E5%AE%83%E4%BB%AC%E5%91%BD%E5%90%8D" class="hash-link" aria-label="1. 二十七小时，为它们命名的直接链接" title="1. 二十七小时，为它们命名的直接链接" translate="no">​</a></h2>
<p>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/152" target="_blank" rel="noopener noreferrer" class="">#152</a> 关闭了第 9 篇的故事，于 5 月 14 日 21:39 UTC 合并。它带来了 <code>fw-4.14.3</code> 以及手动 refresh 的三个已正典化的关卡。</p>
<p>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/157" target="_blank" rel="noopener noreferrer" class="">#157</a> —— 本篇要讲述的那个 —— 于 5 月 16 日 00:31 UTC 合并，整整二十七小时之后。它带来了 <code>fw-4.16.0 / cli-3.14.0</code>，以及两个具名的元模式：</p>
<ul>
<li class=""><strong>Pattern 1 —— <em>pre-declare SpecKit refresh</em></strong>。在链中声明下一个 Charter 之前，先刷新 spec。</li>
<li class=""><strong>Pattern 2 —— <em>post-close audit-driven Batch N.4</em></strong>。当外部审计在关闭后发现问题时，修正一个已关闭的 Charter，而无需开启新的 Charter。</li>
</ul>
<p>第 9 篇记录了手动应用于 CHARTER-18 的纪律。本篇记录之后发生的事：两个具名模式、为它们命名的正典文档（<code>CHARTER-CHAIN-EVOLUTION.md</code>）、度量它们的遥测 schema、两个用于搭建脚手架的 <em>CLI helper</em> —— 以及作为整个编辑弧线的收尾，七十七分钟后，一个哲学层面的元模式将两者向上重新吸收。</p>
<p>这是本弧线的最后一篇。第 1 篇从前方打开的博客，在此从后方合拢。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-pattern-1--声明前-refresh">2. Pattern 1 —— 声明前 refresh<a href="https://straymark.dev/zh-CN/blog/pattern-1-and-pattern-2-chain-evolution#2-pattern-1--%E5%A3%B0%E6%98%8E%E5%89%8D-refresh" class="hash-link" aria-label="2. Pattern 1 —— 声明前 refresh的直接链接" title="2. Pattern 1 —— 声明前 refresh的直接链接" translate="no">​</a></h2>
<p>正典文档中的字面定义：</p>
<blockquote>
<p><em>"一个专用的 refresh PR 在 Charter-N 关闭与 Charter-(N+1) 声明之间落地。它只触及 SpecKit 制件的非锁定章节（plan.md、data-model.md、contracts、quickstart.md、research.md）。"</em></p>
</blockquote>
<p>这正是 Sentinel 在第 9 篇中为 CHARTER-18 手动执行的操作。第 9 篇与第 10 篇的区别在于性质：第 9 篇记录流程；第 10 篇记录其<em>条件激活</em> —— 框架在何时向采用者推荐应用 Pattern 1。</p>
<p>激活标准是可量化的，文档毫不含糊地列出：</p>
<ul>
<li class="">该模块在同一 spec 上已有 <strong>≥ 3 个已关闭的 Charter</strong>。</li>
<li class="">遥测中 <code>agent_quality.r_n_plus_one_emergent_count</code> 字段最近 3 个 Charter 的滚动均值<strong>大于 6</strong>。</li>
<li class="">自上一个 <em>branch point</em> 以来，没有 <em>refresh PR</em> 落地。</li>
</ul>
<p><code>&gt; 6</code> 的阈值并非随意为之。它是 Sentinel CHARTER-18 转化为规则的结果：CHARTER-15、16、17 期间涌现发现（Charter 执行过程中发现的、原计划未声明的风险）的平均值，正是触发"spec 累积了过多技术债"这一感觉的临界点。框架不要求采用者凭直觉判断，而是给出一个遥测数据可以与之比较的数字。</p>
<p>Pattern 1 应用后产出：</p>
<ul>
<li class=""><code>research.md</code> 中一张分类表，将先前 Charter 中的发现归入四类：<em>可复用模式</em>、<em>代码缺口</em>、<em>纪律模式</em>、<em>经验修正</em>。</li>
<li class="">若分类揭示了权衡取舍，则给出明确的操作决策（<code>D1</code>、<code>D2</code> 等）。</li>
<li class="">在下一个 Charter 的 <code>§Context</code> 中，以 <code>id</code> 引用已整合条目。</li>
<li class="">在接收 refresh 的 Charter 的 <code>charter-telemetry.yaml</code> 中，一个可选的 <code>pre_declare_refresh:</code> 遥测块。</li>
</ul>
<p>支撑该模式的度量指标直接引自 Sentinel CHARTER-18，在 <code>CHARTER-CHAIN-EVOLUTION.md</code> 中字面引用：</p>
<blockquote>
<p><em>"Sentinel CHARTER-18 是一条 7-Charter 链中第一个无需中途补救 Charter 便干净关闭的 Charter。<code>estimation_drift_factor: 1.0</code>，<code>pre_work.items_discovered_during_planning: 0</code>，<code>overall_satisfaction: 5/5</code>。"</em></p>
</blockquote>
<p>单一领域，<code>N=1</code>。文档毫不掩饰地说明了这一点：该模式在 <em>Sentinel</em> 中得到了经验验证。等第二个领域的数据到来，再将其晶化至更高层级。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-pattern-2--涌现时修正">3. Pattern 2 —— 涌现时修正<a href="https://straymark.dev/zh-CN/blog/pattern-1-and-pattern-2-chain-evolution#3-pattern-2--%E6%B6%8C%E7%8E%B0%E6%97%B6%E4%BF%AE%E6%AD%A3" class="hash-link" aria-label="3. Pattern 2 —— 涌现时修正的直接链接" title="3. Pattern 2 —— 涌现时修正的直接链接" translate="no">​</a></h2>
<p>字面定义：</p>
<blockquote>
<p><em>"修正提交搭载与原始 Charter 相同的 execute 分支（该分支仍可合并至 main；修正提交叠加在其上）。"</em></p>
</blockquote>
<p>其思路是：当一个已关闭的 Charter 收到关闭后外部审计发现的 <em>critical</em> 或 <em>high</em> 问题时，框架不强制开启新的 Charter。操作者可以在 <code>execute</code> 分支仍可合并至 <code>main</code> 的时间窗口内，<em>修正</em>已关闭的 Charter。</p>
<p>理由，同样字面引自文档：</p>
<blockquote>
<p><em>"开启一个新 Charter 将为约 6 小时的集中工程工作带来数周的治理开销。"</em></p>
</blockquote>
<p>这种不对称 —— 数周治理对比六小时工程 —— 正是 Pattern 2 所要解决的。为五个关闭后发现开启新 Charter 意味着：声明新上下文、重复外部审计、生成新 AILOG、从头注册遥测。而代码层面的修正只需一个下午。Pattern 2 的回答是：不必如此。</p>
<p>激活标准，同样可量化：</p>
<ul>
<li class="">Critical/High <em>发现</em>在关闭后、外部审计后出现。</li>
<li class="">这些发现使 Charter 的关闭标准实质上未被满足。</li>
<li class="">修复涉及文件少于 25 个。</li>
<li class="">不需要重新打开架构层面的问题（原始 Charter 视为已定论的设计决策）。</li>
</ul>
<p>四个标准全部成立，Pattern 2 适用。任一不符，则必须开启新 Charter。</p>
<p>应用后产出：</p>
<ul>
<li class="">与原始 Charter 相同的 <code>execute</code> 分支上的一个<em>修正</em>提交。</li>
<li class="">一个新的 AILOG（不修改原始 AILOG），包含 <code>risk_level: high</code>、<code>review_required: true</code>，以及一个指向原始 AILOG 的 <code>amends:</code> 字段。</li>
<li class="">原始 AILOG 中的 <code>## Historical correction (YYYY-MM-DD)</code> 章节，向<em>前</em>指向新的 AILOG。这是存档性的：原始 AILOG 不被改写，而是被链接。</li>
<li class=""><code>charter-telemetry.yaml</code> 中的 <code>post_close_amendment:</code> 遥测块。</li>
</ul>
<p>支撑 Pattern 2 的经验指标，字面引用：</p>
<blockquote>
<p><em>"Sentinel CHARTER-18 于 2026-05-15 关闭，审计报告于 2026-05-15 至 05-17 到达。五个发现（4 个来自 gpt-5.3-codex，1 个来自 gemini-2.5-pro）均为代码层面的修复 —— DI 接线、重试头部解析、多租户过滤器、超时默认值。Batch 7.4 修正在一个内聚的提交中（19 个文件，+2257/-106 行）关闭了全部五个发现。"</em></p>
</blockquote>
<p>一次提交，19 个文件，两个审计模型汇聚于相似发现，五项修复。若开启新 Charter：声明、外部审计、关闭之间至少两周。替代方案：同一分支上的一次<em>修正</em>，六小时。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-组合而非排斥">4. 组合，而非排斥<a href="https://straymark.dev/zh-CN/blog/pattern-1-and-pattern-2-chain-evolution#4-%E7%BB%84%E5%90%88%E8%80%8C%E9%9D%9E%E6%8E%92%E6%96%A5" class="hash-link" aria-label="4. 组合，而非排斥的直接链接" title="4. 组合，而非排斥的直接链接" translate="no">​</a></h2>
<p>文档中最有趣的部分 —— 我认为也是最能收尾整个弧线的部分 —— 是关于两个模式如何关联的章节。字面引用：</p>
<blockquote>
<p><em>"收到 Pattern 1 的 Charter 更可能规避 Pattern 2，因为 refresh 吸收了执行前的风险，否则这些风险会以关闭后发现的形式浮现。但 CHARTER-18 两者都需要 —— refresh 处理了 spec 层面的漂移；修正处理了 refresh 触及不到的运行时层面的漂移。"</em></p>
</blockquote>
<p>它们不是竞争性的模式，而是同一原则在周期不同时刻的应用。Pattern 1 吸收 <em>spec 层面</em>的漂移 —— 原始计划已不再准确描述的内容。Pattern 2 吸收<em>运行时层面</em>的漂移 —— 只有代码运行起来、外部审计员以全新视角审视、问题从代码本身涌现后才会暴露的内容。</p>
<p>Sentinel CHARTER-18 两者都需要。刷新后的 spec 消除了后续审计周期原本需要原子性修复的涌现发现；后续<em>修正</em>吸收了 refresh 无法预见的内容 —— DI 接线、重试头部解析、多租户过滤器、超时默认值。这些是计划无从得知的事情，只有在代码存在之后才会涌现。</p>
<p>框架通过命名两个模式并明确声明其组合关系，做了一件我想着重指出的事：它将纪律不是线性的这一事实记录在案。不存在单一的检查点；在每个 Charter 之前有一个（Pattern 1），之后也有一个（Pattern 2），同一个 Charter 中两者都可能是必要的。链在演化；模式也在演化。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-schema-与-helper">5. Schema 与 helper<a href="https://straymark.dev/zh-CN/blog/pattern-1-and-pattern-2-chain-evolution#5-schema-%E4%B8%8E-helper" class="hash-link" aria-label="5. Schema 与 helper的直接链接" title="5. Schema 与 helper的直接链接" translate="no">​</a></h2>
<p><code>fw-4.16.0</code> 在遥测 schema 中新增了两个可选的子对象，位于 <code>charter-telemetry.schema.v0.json</code> v0.2 中：</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">pre_declare_refresh</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">enabled</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> boolean</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">refresh_pr</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> string </span><span class="token punctuation" style="color:#393A34">|</span><span class="token plain"> null</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">refresh_aidec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> string </span><span class="token punctuation" style="color:#393A34">|</span><span class="token plain"> null</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">reusable_patterns_integrated</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> integer  </span><span class="token comment" style="color:#999988;font-style:italic"># ≥ 0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">code_gaps_integrated</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> integer</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">discipline_patterns_integrated</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> integer</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">empirical_corrections_integrated</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> integer</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">operator_decisions_ratified</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> integer</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">post_close_amendment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">applied</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> boolean</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">trigger</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> external_audit </span><span class="token punctuation" style="color:#393A34">|</span><span class="token plain"> production_incident </span><span class="token punctuation" style="color:#393A34">|</span><span class="token plain"> deferred_implementation</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ailog_id</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> string</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">findings_closed</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> integer</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">files_modified</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> integer</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">effort_hours</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> number</span><br></div></code></pre></div></div>
<p>整数计数器，而非叙述性文字。这是有意为之的设计：遥测 schema 所捕获的，是智能体或操作者可以机械计数的内容。无法计数的 —— <em>问题有多深、修复有多巧妙</em> —— 存在于 AILOG 中，而非遥测里。Schema 度量工作的<em>形态</em>；文档承载工作的<em>内容</em>。</p>
<p><code>cli-3.14.0</code> 新增了两个 helper，均不是变更器：</p>
<ul>
<li class="">
<p><strong><code>straymark charter refresh-suggest &lt;module&gt; [--threshold N]</code></strong> —— <em>只读</em>。它读取该模块最近三个已关闭 Charter 的遥测，计算 <code>r_n_plus_one_emergent_count</code> 的滚动均值，并给出建议：<em>refresh-now</em>、<em>not-needed</em> 或 <em>insufficient-data</em>。<em>始终以 Exit 0 退出</em>。它是信息性的，永远不是 CI 门控。</p>
</li>
<li class="">
<p><strong><code>straymark charter amend &lt;CHARTER-ID&gt; --trigger &lt;kind&gt; --ailog-title &lt;title&gt; [--findings-closed N] [--merge-into &lt;PATH&gt;]</code></strong> —— 脚手架。它创建一个包含正确字段的 AILOG stub（<code>risk_level: high</code>、<code>review_required: true</code>、指向原始 AILOG 的 <code>amends:</code>），在原始 AILOG 中添加 <code>## Historical correction</code> 章节，并渲染 <code>post_close_amendment:</code> YAML 块。<strong>不触及 git。</strong></p>
</li>
</ul>
<p>让两个 helper 保持谦逊 —— 一个只推荐，另一个只准备 —— 与第 4 篇的 A1 决策一脉相承：<em>"CLI 编排，但不调用 API"</em>。在此：CLI 建议，但不决定；搭建脚手架，但不变更。人类仍然是纪律发生的节点。框架只是把工具放在触手可及之处。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-七十七分钟后">6. 七十七分钟后<a href="https://straymark.dev/zh-CN/blog/pattern-1-and-pattern-2-chain-evolution#6-%E4%B8%83%E5%8D%81%E4%B8%83%E5%88%86%E9%92%9F%E5%90%8E" class="hash-link" aria-label="6. 七十七分钟后的直接链接" title="6. 七十七分钟后的直接链接" translate="no">​</a></h2>
<p>在弧线收尾之前，还有一件值得记录的事。PR #157 于 5 月 16 日 00:31 UTC 合并。PR #160 —— 第 1 篇的那个，<code>EMERGENT-OBSERVATION-DESIGN.md</code>，fw-4.17.0 —— 于同日 01:48 UTC 合并。七十七分钟后。</p>
<p>这七十七分钟里发生的事，是弧线的倒置收尾。第 1 篇所命名的哲学元模式 —— <em>由正式链接加上允许标记的文化许可所构成的涌现观察</em> —— 恰好在本篇的两个操作性元模式之后<strong>一小时十七分钟</strong>晶化。哲学元模式不先于纪律存在；它在纪律之后出现。它是对同一证据的升序解读。</p>
<p><code>CHARTER-CHAIN-EVOLUTION.md</code> 在其 <code>§Related</code> 章节中明确记录了这一点：</p>
<blockquote>
<p><em>"EMERGENT-OBSERVATION-DESIGN.md —— Pattern 1 与 Pattern 2 作为其应用的元模式（正式交叉引用 + 允许浮现的文化许可）。"</em></p>
</blockquote>
<p>Pattern 1 是正式链接（遥测中的 <code>r_n_plus_one_emergent_count</code>、<code>§Context</code> 中按 <code>id</code> 的引用、分类的 AILOG）与文化许可（智能体发现 spec 与代码之间的漂移时应当标记的规则）的组合。Pattern 2 是类似的组合（带 <code>amends:</code> 的 AILOG、向前链接的 <code>Historical correction</code> 章节）与允许标记关闭后发现而非静默吸收的许可。两个模式都是第 1 篇从上方命名的同一文化+结构原则的应用。</p>
<p>这是一个我持续喜欢的结尾：以"智能体看见了没人要求它看见的东西"开篇的博客，以在时间上更早的这一篇收尾，说的是："这里是智能体因为框架为它们命了名而能够看清的两件事"。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-合拢弧线">7. 合拢弧线<a href="https://straymark.dev/zh-CN/blog/pattern-1-and-pattern-2-chain-evolution#7-%E5%90%88%E6%8B%A2%E5%BC%A7%E7%BA%BF" class="hash-link" aria-label="7. 合拢弧线的直接链接" title="7. 合拢弧线的直接链接" translate="no">​</a></h2>
<p>十篇文章。十一个故事片段（<code>H-01</code> 至 <code>H-13</code>，其中若干合并成篇）。一个编辑决策 —— 追溯发布 —— 由第 2 篇引入，此后九篇严格遵守。一项贯穿始终的双语承诺。</p>
<p>写这个博客过程中学到的一些东西，值得在收尾前记录下来：</p>
<ol>
<li class="">
<p><strong>模式诞生于纪律。</strong> 这句话在第 9 篇中已经出现，但整个弧线都在印证它。本篇的元模式不是被设计出来的，而是被提炼出来的。每个记录的里程碑都先有操作性的故事，后有名字。</p>
</li>
<li class="">
<p><strong>节奏并不均匀。</strong> 从第一次提交（第 2 篇，1 月 27 日）到第一个有外部审计的有界单元（第 3 篇，4 月 25 日）之间隔了十九天。从手动应用纪律到正典化纪律（第 9 篇，5 月 14 日）之间只有五小时。从两个元模式到它们的哲学解读（第 10 篇与第 1 篇，5 月 16 日）之间只有七十七分钟。项目经历了每一种速度，博客将它们全部记录在案。</p>
</li>
<li class="">
<p><strong>框架对其采用者并非中立。</strong> Sentinel 使得每个模式在晶化之前都能得到经验验证。博客不掩盖这一出处；它将其作为方法论前提加以强调。</p>
</li>
<li class="">
<p><strong>未被命名的，就不会被看见。</strong> 第 5 篇将其表述为<em>结构性可见性</em>；第 7 篇将其应用于横向技术债；第 8 篇将其指向语言异常值。这是整个弧线中最持久的规律性。作为技术制件存在是不够的。名字激活制件。</p>
</li>
</ol>
<p>博客要讲述的弧线 —— 里程碑 <code>H-01</code> 至 <code>H-13</code>、项目的四个名字、六个 Plan、一等公民 Charter、外部审计周期、Issue #113、更名为 StrayMark、TDE、审计提示异常值、手动纪律，以及两个元模式 —— 在此合拢。剩余的明确技术债：<code>H-14</code>，<code>straymark explore</code> TUI（在第 8 篇中作为可见性工具横向出现），以及 <code>PLAN-INVESTIGACION.md §1.43-55</code> 列出的待处理候选里程碑 <code>H-?-A</code> 至 <code>H-?-E</code>。可能的未来第二批次。不作承诺。</p>
<p>最后一个观察，与第 1 篇对称。那篇以"智能体看见了没人要求它看见的东西"开篇。博客在十篇之后收尾所记录的，是这句话的另一半：智能体之所以能看见，是因为框架让它具备了看见的条件。第 1 篇的涌现观察是证明；本篇的具名模式是前提条件。智能体在弧线覆盖的四个月里浮现的每一件事，都因为某个先前的故事片段在原本空无一物之处放置了一个制件、一个触发器、一个正式链接、一个可见的表面而成为可能。</p>
<p>博客讲完了这个故事。框架继续前行。</p>
<hr>
<p><em>锚点：<a href="https://github.com/StrangeDaysTech/straymark/issues/156" target="_blank" rel="noopener noreferrer" class="">Issue #156</a>。PR <a href="https://github.com/StrangeDaysTech/straymark/pull/157" target="_blank" rel="noopener noreferrer" class="">#157</a> —— <code>fw-4.16.0 / cli-3.14.0</code>（合并于 2026-05-16 00:31 UTC）。正典文档：<a href="https://github.com/StrangeDaysTech/straymark/blob/main/dist/.straymark/00-governance/CHARTER-CHAIN-EVOLUTION.md" target="_blank" rel="noopener noreferrer" class=""><code>CHARTER-CHAIN-EVOLUTION.md</code></a>。Schema：<code>dist/.straymark/schemas/charter-telemetry.schema.v0.json</code> v0.2。后继元模式：<a href="https://github.com/StrangeDaysTech/straymark/blob/main/dist/.straymark/00-governance/EMERGENT-OBSERVATION-DESIGN.md" target="_blank" rel="noopener noreferrer" class=""><code>EMERGENT-OBSERVATION-DESIGN.md</code></a>，<code>fw-4.17.0</code>（合并于 2026-05-16 01:48 UTC，77 分钟后）。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>
<p><em>—— StrayMark 博客 H-01 → H-13 弧线终结。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="charters" term="charters"/>
        <category label="链演化" term="链演化"/>
        <category label="治理" term="治理"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[AGENTS.md 作为通用标准]]></title>
        <id>https://straymark.dev/zh-CN/blog/agents-md-as-a-universal-standard</id>
        <link href="https://straymark.dev/zh-CN/blog/agents-md-as-a-universal-standard"/>
        <updated>2026-05-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[项目的第一份 AIDEC 与把它落地的开放标准之间相隔四个月。`AGENTS.md` 放在仓库根目录，被十五个不同厂商的 CLI 读取，替代了 CLAUDE.md / GEMINI.md / .cursorrules 以及十几个其他文件的扩散。]]></summary>
        <content type="html"><![CDATA[<p><em>项目的第一份 AIDEC —— "AI 智能体是同一个技术受众" —— 与把它落地的开放标准之间相隔四个月。<code>AGENTS.md</code> 放在仓库根目录，被十五个不同厂商的 CLI 读取，替代了 CLAUDE.md / GEMINI.md / .cursorrules 以及十几个其他文件的扩散。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-直觉与标准之间的四个月">1. 直觉与标准之间的四个月<a href="https://straymark.dev/zh-CN/blog/agents-md-as-a-universal-standard#1-%E7%9B%B4%E8%A7%89%E4%B8%8E%E6%A0%87%E5%87%86%E4%B9%8B%E9%97%B4%E7%9A%84%E5%9B%9B%E4%B8%AA%E6%9C%88" class="hash-link" aria-label="1. 直觉与标准之间的四个月的直接链接" title="1. 直觉与标准之间的四个月的直接链接" translate="no">​</a></h2>
<p>2026 年 5 月 15 日，UTC 时间 19:05，PR <a href="https://github.com/StrangeDaysTech/straymark/pull/155" target="_blank" rel="noopener noreferrer" class="">#155</a> 以 <code>fw-4.15.0 / cli-3.13.2</code> 的形式合入框架。它带来的是一个新文件，加入了 CLI 注入的指令列表：<code>AGENTS.md</code>，位于采用者仓库的根目录，与已有的文件并列（<code>CLAUDE.md</code>、<code>GEMINI.md</code>、<code>.github/copilot-instructions.md</code>、<code>.cursorrules</code>、<code>.cursor/rules/straymark.md</code>）。</p>
<p>这本来可以是一件微不足道的事，但恰好在四个月前的 2026 年 1 月 27 日，项目的第一份 AIDEC —— 第 2 篇文章中以"年份笔误"为切入点讲述的那份 —— 曾宣示：</p>
<blockquote>
<p><em>"AI 智能体（Claude、Gemini、Copilot、Cursor）能同等流畅地处理任何语言的指令，因此翻译它们的配置文件不会带来功能上的收益。"</em></p>
</blockquote>
<p>这句话写于项目<em>初始提交</em>六小时后，包含着框架此后不断付诸实践的一个直觉：智能体是同一个技术受众。配置文件（<code>CLAUDE.md</code> 等）保持英文（而人类文档被翻译成三种语言），原因在于"AI 智能体"这一受众并不从翻译中获益。若要把这个直觉彻底落地，所缺的只是给它一个单一的指向——而无需为每个不同厂商各复制一遍文件。</p>
<p>本文正是围绕这件事展开：<code>AGENTS.md</code> —— 一个于 2025 年 12 月捐赠给 Agentic AI Foundation 的开放标准 —— 如何在四个月后完成了第一份 AIDEC 于 1 月所开启的那个决策。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-agentsmd-所解决的碎片化问题">2. <code>AGENTS.md</code> 所解决的碎片化问题<a href="https://straymark.dev/zh-CN/blog/agents-md-as-a-universal-standard#2-agentsmd-%E6%89%80%E8%A7%A3%E5%86%B3%E7%9A%84%E7%A2%8E%E7%89%87%E5%8C%96%E9%97%AE%E9%A2%98" class="hash-link" aria-label="2-agentsmd-所解决的碎片化问题的直接链接" title="2-agentsmd-所解决的碎片化问题的直接链接" translate="no">​</a></h2>
<p>2024 年至 2026 年间，随着 AI 智能体 CLI 的大量涌现，每家厂商都另起炉灶，发明了自己的指令文件格式：</p>
<ul>
<li class="">Anthropic 随第一版 Claude Code 发布了 <code>CLAUDE.md</code>。</li>
<li class="">Google 为 Gemini CLI 采用了 <code>GEMINI.md</code>。</li>
<li class="">GitHub 为 Copilot CLI 确立了 <code>.github/copilot-instructions.md</code>。</li>
<li class="">Cursor 以 <code>.cursorrules</code> 起步，后来迁移到 <code>.cursor/rules/*.md</code>。</li>
<li class="">OpenAI 的 Codex CLI 使用另一套路径；Aider、Devin、Continue、Roo Code、Factory Droids、Sourcegraph Amp、Zed AI、Windsurf、Amazon Q 各有各的方式。</li>
</ul>
<p>一个同时使用两三个 CLI 的 StrayMark 采用者，不得不维护两三份内容相同的副本，靠手工同步，祈祷它们不要彼此偏离。在 <code>fw-4.14.x</code> 之前，框架的 <code>straymark init</code> 命令承认了这种碎片化现实：它向最常见的五个特定于厂商的文件注入内容。但其余十到十五个 CLI 则被排除在外，采用者只能靠手工复制内容来处理它们。</p>
<p>2025 年底，生产智能体 CLI 的开源项目社区做出了碎片化代价高昂时常见的选择：就标准达成一致。<code>AGENTS.md</code> 置于仓库根目录，所有人都读取它。将其捐赠给 Agentic AI Foundation（Linux Foundation，2025 年 12 月）赋予了它机构层面的治理。规范刻意保持极简：一个 Markdown 文件，不强制任何模式，位置可预测。每个 CLI 如何处理这个文件是其自己的事；那个核心问题——<em>"这个仓库的智能体配置在哪里？"</em>——现在有了唯一的答案。</p>
<p>到 2026 年 5 月，读取 <code>AGENTS.md</code> 的 CLI 列表包括（直接引用 PR #155 正文）：</p>
<blockquote>
<p><em>"Claude Code、OpenAI Codex CLI、Cursor、Aider、Devin、Sourcegraph Amp、Google Jules、Zed AI、Continue、Roo Code、Factory Droids、GitHub Copilot、Gemini CLI、Windsurf、Amazon Q 及其他。"</em></p>
</blockquote>
<p>十五个 CLI。其中一些出于历史兼容性仍然<em>同时</em>读取各自特定于厂商的文件。但全部十五个都知道优先查找 <code>AGENTS.md</code>。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-采纳而不消灭自己的格式">3. 采纳，而不消灭自己的格式<a href="https://straymark.dev/zh-CN/blog/agents-md-as-a-universal-standard#3-%E9%87%87%E7%BA%B3%E8%80%8C%E4%B8%8D%E6%B6%88%E7%81%AD%E8%87%AA%E5%B7%B1%E7%9A%84%E6%A0%BC%E5%BC%8F" class="hash-link" aria-label="3. 采纳，而不消灭自己的格式的直接链接" title="3. 采纳，而不消灭自己的格式的直接链接" translate="no">​</a></h2>
<p><code>fw-4.15.0</code> 最有趣的决策不是采纳 <code>AGENTS.md</code>；一旦标准达到临界质量，那是必然之举。有趣的是<em>如何</em>采纳。PR 正文毫无含糊地表述了这一点：</p>
<blockquote>
<p><em>"与 CLAUDE.md / GEMINI.md 并列：标记块指向 STRAYMARK.md，其下附有最小可行正文，供无法跟随相对链接的读者使用。"</em></p>
</blockquote>
<p>三个关键词：<strong>并列</strong>、<strong>标记块</strong>、<strong>最小可行正文</strong>。</p>
<p><em>并列</em>意味着 <code>AGENTS.md</code> 不替换特定于厂商的文件，而是共存。框架仍然注入 <code>CLAUDE.md</code>、<code>GEMINI.md</code>、<code>.cursorrules</code> 等文件。理由很务实：某些 CLI（尤其是旧版 Cursor）要求将完整内容内嵌，而非通过相对链接指向。保留兄弟文件维持了这种兼容性，无需每个采用者为自己特定的 CLI 单独费心。</p>
<p><em>标记块</em>是框架在其他特定于厂商的文件中已使用的惯例：一个 HTML 注释块，夹在 <code>&lt;!-- straymark:begin --&gt;</code> 与 <code>&lt;!-- straymark:end --&gt;</code> 之间，将 <code>STRAYMARK.md</code> 指向为真实来源。每次 <code>straymark update-framework</code> 只替换标记之间的内容。采用者在标记<em>之外</em>写入的内容——例如项目专属指令——保持完整。框架尊重采用者的文件；它只治理自己的那一节。</p>
<p><em>最小可行正文</em>是对不跟随相对链接的 CLI 的妥协。标记块之下，<code>AGENTS.md</code> 携带一段简短正文，包含要素：智能体身份、审查要求、提交前检查清单、监管前置元数据片段、NIST AI 600-1 风险分类、可观测性规则、命名规范。对于无法读取 <code>STRAYMARK.md</code> 的智能体而言，这已足以正常运作；但它不足以替代规范文档。这种不对称是刻意为之的：我们希望智能体<em>主动</em>去打开 <code>STRAYMARK.md</code>。</p>
<p>CLI 的防御性细节（<code>cli/src/commands/remove.rs:13</code>）封堵了操作层面的漏洞：<code>AGENTS.md</code> 被加入了 <code>LEGACY_DIRECTIVE_TARGETS</code>。如果采用者丢失了 <code>.straymark/dist-manifest.yml</code> 并运行 <code>straymark remove</code>，旧版回退逻辑也会清理 <code>AGENTS.md</code>，而不是留下孤立文件。这是枯燥的日常维护，但正是这种差别决定了一个框架能否彻底卸载，还是留下残骸。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-没有-adr">4. 没有 ADR<a href="https://straymark.dev/zh-CN/blog/agents-md-as-a-universal-standard#4-%E6%B2%A1%E6%9C%89-adr" class="hash-link" aria-label="4. 没有 ADR的直接链接" title="4. 没有 ADR的直接链接" translate="no">​</a></h2>
<p>有一个缺席值得点名。采纳 <code>AGENTS.md</code> 的决策没有获得自己的 ADR。</p>
<p>这与第 6 篇文章中 StrayMark 品牌重塑形成刻意对比——那次有公开的 <code>ADR-2026-05-08-001</code>，包含三个评估过的替代方案和列举的后果。它也与第 12 篇文章中 Phase 2 的情形不同，那次有一份包含架构论证的详尽 CHANGELOG 条目。<code>fw-4.15.0</code> 的文档论证只有 PR 正文——一篇好的正文，论点清晰，但不是 ADR。</p>
<p>为什么？操作层面的答案：这个决策被认为是<strong>附加性的</strong>，而非结构性的。采纳一个与现有格式共存的开放标准，并不改变框架的模型，只是扩展了它。没有哪个被否定的替代方案需要正式论证：不采纳 <code>AGENTS.md</code> 意味着出于审美偏好将十五个 CLI 排除在外，而没有人持有这个论点。</p>
<p>但这里有一个值得记录的教训。博客已经见过这种模式——Phase 2 关闭了一个经验闭环，品牌重塑改变了身份，第 10 篇文章的元模式命名了已然存在的东西——每次那个问题*"这值得写一份 ADR 吗？"*的答案都不同。框架似乎在不明言的情况下采纳了这样一条操作规则：<strong>当决策关闭了代价高昂的替代方案、并留下了那些未被选择之方案的记录时，ADR 才是必要的</strong>。<code>fw-4.15.0</code> 并未关闭代价高昂的替代方案——它只是增加了一个层次。所以 PR 已经足够。</p>
<p>我在这里将其记录在案，因为值得承认：并非每项变更都值得一份 ADR，也并非每个 PR 都是历史文献。存档纪律有其自身的标准。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-智能体可见性的两个层次">5. 智能体可见性的两个层次<a href="https://straymark.dev/zh-CN/blog/agents-md-as-a-universal-standard#5-%E6%99%BA%E8%83%BD%E4%BD%93%E5%8F%AF%E8%A7%81%E6%80%A7%E7%9A%84%E4%B8%A4%E4%B8%AA%E5%B1%82%E6%AC%A1" class="hash-link" aria-label="5. 智能体可见性的两个层次的直接链接" title="5. 智能体可见性的两个层次的直接链接" translate="no">​</a></h2>
<p><code>fw-4.15.0</code> 发布十天前的 5 月 9 日，Issue <a href="https://github.com/StrangeDaysTech/straymark/issues/113" target="_blank" rel="noopener noreferrer" class="">#113</a> 被关闭——第 5 篇文章将这段插曲记录为*"对智能体不可见的 Charter"*。当时，操作者与一个能力出众、专注认真的智能体合作了六小时，规范的入门装置已全部加载，但智能体从未提议使用 Charter。那篇文章的结论：<em>结构性可见性</em>是框架的责任，而非智能体的属性。</p>
<p><code>fw-4.15.0</code> 涵盖了同一问题的另一面。</p>
<p>第 5 篇文章关注的是<strong>内部结构可见性</strong>：如何确保已在读取我们仓库的智能体发现框架的核心概念。PR #122（第 5 篇）的答案是在 Charter 作为概念出现的内部表面上做乘法：<code>STRAYMARK.md §15</code>、<code>CLAUDE.md</code>/<code>GEMINI.md</code> 指令、<code>/straymark-*</code> 技能、<code>status</code> 输出、模板、通往 SpecKit 的桥接。九个内部表面。</p>
<p>本文关注的是<strong>外部结构可见性</strong>：如何确保任何进入仓库的智能体——无论其厂商——都能找到框架的装置。<code>fw-4.15.0</code> 的答案是采纳社区选定的规范接入点。一个外部表面，被十五个厂商读取。</p>
<p>两个层次共享同一概念轴——<em>一个技术制品只有在表面命名它时才对智能体存在</em>——但各有不同的作用域。内部可见性保证智能体在读取 <code>STRAYMARK.md</code> 时看到 Charter。外部可见性保证智能体进入仓库时看到 <code>STRAYMARK.md</code>，无论是哪个 CLI 在编排它。缺少任何一个层次都是不完整的。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-运行-update-framework-时采用者的操作">6. 运行 <code>update-framework</code> 时采用者的操作<a href="https://straymark.dev/zh-CN/blog/agents-md-as-a-universal-standard#6-%E8%BF%90%E8%A1%8C-update-framework-%E6%97%B6%E9%87%87%E7%94%A8%E8%80%85%E7%9A%84%E6%93%8D%E4%BD%9C" class="hash-link" aria-label="6-运行-update-framework-时采用者的操作的直接链接" title="6-运行-update-framework-时采用者的操作的直接链接" translate="no">​</a></h2>
<p>发布的操作部分，直接引用 CHANGELOG：</p>
<blockquote>
<p><em>"straymark update-framework 会带来新模板并在项目根目录注入 AGENTS.md。注入遵循与所有其他指令目标相同的规则：若文件不存在则创建，后续运行时替换标记块；若文件已存在但没有 StrayMark 标记则安全追加（这在 2026 年非常普遍——许多采用者已经手工维护着自己的 AGENTS.md）。"</em></p>
</blockquote>
<p>三种重要行为：</p>
<ol>
<li class=""><strong>若文件不存在</strong>，CLI 使用规范模板创建它。</li>
<li class=""><strong>若文件已存在且带有 StrayMark 标记</strong>，CLI 只替换标记之间的内容。</li>
<li class=""><strong>若文件已存在但没有标记</strong>——2026 年非常普遍，因为许多采用者已经在手工编写自己的 <code>AGENTS.md</code>——CLI 会在文件末尾<strong>追加其标记</strong>，不触碰采用者已写入的内容。</li>
</ol>
<p>这与博客在第 3 和第 6 篇中记录的存档尊重原则相同：框架治理<em>它自己的那一节</em>，而非整个文件。如果采用者想在其 <code>AGENTS.md</code> 中编写项目专属指令——内部政策、命名规范、特定代码库的专属指令——这些指令在每次 <code>update</code> 时都保持完整。StrayMark 块存在于它那个小小的标记边界之内，更新也在那个边界之内进行。</p>
<p>CHANGELOG 说明以一条值得记录的小操作警告作结：<em>"如果你的 .gitignore 排除了 AGENTS.md，请在运行 update-framework 之前调整，以确保注入内容能进入版本控制。"</em> 这是对框架自身无法解决的边界情况的坦诚——采用者选择忽略该文件是其主权，而非错误。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/agents-md-as-a-universal-standard#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>我从这个过程中提炼出四点：</p>
<ol>
<li class="">
<p><strong>直觉与落地之间四个月的周期，正是把一个闭环做好所需要的时间。</strong> 1 月的 AIDEC 携带着直觉——<em>"智能体是同一个受众"</em>。<code>fw-4.15.0</code> 用一个开放标准将其关闭。两者之间，框架在特定于厂商的原型上持续迭代，直到通用替代方案出现。早期的直觉是对的；时间线是生态系统成熟所必需的。</p>
</li>
<li class="">
<p><strong>采纳开放标准不需要放弃自己的格式。</strong> <code>AGENTS.md</code> 与 <code>CLAUDE.md</code>、<code>GEMINI.md</code>、<code>.cursorrules</code> 共存。这个决策不是"一个替换其他"，而是"当它有所贡献时，一个加入其他"。框架获得了外部可见性，同时保留了对需要特定格式的 CLI 的兼容性。</p>
</li>
<li class="">
<p><strong>并非每项变更都值得一份 ADR。</strong> StrayMark 品牌重塑关闭了代价高昂的替代方案，并留下了被舍弃方案的正式记录。<code>fw-4.15.0</code> 只是增加了一个通用层：任何 ADR 都不会丰富这个决策。存档纪律有其操作标准，而非教条。</p>
</li>
<li class="">
<p><strong>智能体的可见性在两个层次上运作。</strong> 内部层次（第 5 篇，<code>fw-4.12.0</code>）确保智能体在读取仓库时看到框架的概念。外部层次（<code>fw-4.15.0</code>）确保智能体无论由哪个 CLI 编排，都能从正确的门进入仓库。缺少其中任何一个，要么是有能力的智能体在沉默中读取，要么是迷失方向的智能体找不到入口。</p>
</li>
</ol>
<hr>
<p><em>参考锚点：PR <a href="https://github.com/StrangeDaysTech/straymark/pull/155" target="_blank" rel="noopener noreferrer" class="">#155</a> — <code>fw-4.15.0 / cli-3.13.2</code>（合并于 2026-05-15 19:05 UTC）。原始 AIDEC（第 2 篇）：<code>.chronicle/07-ai-audit/decisions/AIDEC-2025-01-27-001-i18n-strategy.md</code>（提交 <code>7b7193e</code>，含年份笔误的 ID）。注入模板：<code>dist/dist-templates/directives/AGENTS.md</code>。标准规范：<a href="https://agents.md/" target="_blank" rel="noopener noreferrer" class="">agents.md</a> —— 捐赠给 Agentic AI Foundation（Linux Foundation），2025 年 12 月。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="agents-md" term="agents-md"/>
        <category label="治理" term="治理"/>
        <category label="开放标准" term="开放标准"/>
        <category label="i18n" term="i18n"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[打开这个框架]]></title>
        <id>https://straymark.dev/zh-CN/blog/opening-the-framework</id>
        <link href="https://straymark.dev/zh-CN/blog/opening-the-framework"/>
        <updated>2026-05-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[七十二小时内的两个动作：补上 i18n 覆盖度最后的 5%（治理文档在 EN + ES + zh-CN 上达到 20/20/20）以及在 README 中加入 CLA 徽章。分开看都很小；放在一起，正是框架不再只对自己讲话、开始向任何想读的人讲话的那一刻。]]></summary>
        <content type="html"><![CDATA[<p><em>七十二小时内的两个动作：补上 i18n 覆盖度最后的 5%（治理文档在 EN + ES + zh-CN 上达到 20/20/20）以及在 README 中加入 CLA 徽章。分开看都很小；放在一起，正是框架不再只对自己讲话、开始向任何想读的人讲话的那一刻。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-七十二小时内的两个动作">1. 七十二小时内的两个动作<a href="https://straymark.dev/zh-CN/blog/opening-the-framework#1-%E4%B8%83%E5%8D%81%E4%BA%8C%E5%B0%8F%E6%97%B6%E5%86%85%E7%9A%84%E4%B8%A4%E4%B8%AA%E5%8A%A8%E4%BD%9C" class="hash-link" aria-label="1. 七十二小时内的两个动作的直接链接" title="1. 七十二小时内的两个动作的直接链接" translate="no">​</a></h2>
<p>2026 年 5 月 13 日，UTC 06:20，<code>fw-4.13.4</code> 合并。它填补了两个小的 i18n 覆盖缺口——ISO-25010 参考文档此前只有英文版，而 Charter 模板的简体中文版缺失。二十二个规范框架文件达到了结构上的 EN/ES/zh-CN 均衡。</p>
<p>65 小时后，5 月 15 日 UTC 00:14，PR <a href="https://github.com/StrangeDaysTech/straymark/pull/154" target="_blank" rel="noopener noreferrer" class="">#154</a> 合并。它在 README 中添加了一枚徽章——CLA Assistant 的那枚，那个小小的可见方块，告诉访客这个仓库接受在贡献者许可协议下的贡献。CLA 政策此前已在 <code>CONTRIBUTING.md</code> 中存在了数月；唯一改变的是它现在在 README 中一眼可见。</p>
<p>本文覆盖这两个动作。分开看，它们都很小——一个完成了某项技术工作最后的 5%，另一个则将已有的内容公开发布。放在一起，它们是框架决定向外部展示自身的那一刻。一个开源项目停止对自己讲话、开始向任何想读的人讲话时所做的事情。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-i18n-覆盖度最后的-5">2. i18n 覆盖度最后的 5%<a href="https://straymark.dev/zh-CN/blog/opening-the-framework#2-i18n-%E8%A6%86%E7%9B%96%E5%BA%A6%E6%9C%80%E5%90%8E%E7%9A%84-5" class="hash-link" aria-label="2. i18n 覆盖度最后的 5%的直接链接" title="2. i18n 覆盖度最后的 5%的直接链接" translate="no">​</a></h2>
<p><code>fw-4.13.4</code> 并非一个重大发布。PR <a href="https://github.com/StrangeDaysTech/straymark/pull/144" target="_blank" rel="noopener noreferrer" class="">#144</a> 有 511 行新增内容，几乎全是翻译。但 PR 摘要说了一件值得记录的事：</p>
<blockquote>
<p><em>"将治理文档在 EN + ES + zh-CN 上带到完整的 20/20/20 对等。"</em></p>
</blockquote>
<p>20/20/20。<code>dist/.straymark/00-governance/</code> 中的二十二个规范文件（数量后来增加；5 月时是二十个），全部在 <code>i18n/es/</code> 和 <code>i18n/zh-CN/</code> 中有其对应版本。完整对等，而非近似。</p>
<p>填补对等缺口的两个文件是具体的：</p>
<ul>
<li class=""><code>ISO-25010-2023-REFERENCE.md</code>——对框架原则中引用的国际软件质量标准的规范性参考。直到 <code>fw-4.13.4</code>，它只存在英文版。该发布添加了 <code>i18n/es/ISO-25010-2023-REFERENCE.md</code> 和 <code>i18n/zh-CN/ISO-25010-2023-REFERENCE.md</code>，各 150 行。</li>
<li class=""><code>charter-template.md</code> 的 zh-CN 版。Charter 模板已经翻译成了西班牙语；简体中文版缺失，共 211 行。</li>
</ul>
<p>这不是令人兴奋的工作。这是关闭一个框架的那一块。但它在两件事上很重要。第一：在该发布之前，克隆框架的中文使用者会发现只有英文版的核心模板，这与框架提供三语言版本的承诺相悖。第二：博客在第 8 篇文章中已经记录了框架的 i18n 覆盖度接近完整，仅剩两处小缺口作为已记录的债务。<code>fw-4.13.4</code> 偿清了这笔债务。一篇关于审计 prompt 异常值的文章中看似旁注的内容，最终成了一个独立发布，有其自己的 CHANGELOG 条目。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-哪些内容被翻译哪些不被翻译">3. 哪些内容被翻译，哪些不被翻译<a href="https://straymark.dev/zh-CN/blog/opening-the-framework#3-%E5%93%AA%E4%BA%9B%E5%86%85%E5%AE%B9%E8%A2%AB%E7%BF%BB%E8%AF%91%E5%93%AA%E4%BA%9B%E4%B8%8D%E8%A2%AB%E7%BF%BB%E8%AF%91" class="hash-link" aria-label="3. 哪些内容被翻译，哪些不被翻译的直接链接" title="3. 哪些内容被翻译，哪些不被翻译的直接链接" translate="no">​</a></h2>
<p>PR #144 明确编入代码的一条操作规则值得记录，因为它是框架在 i18n 方面最清晰的规则：</p>
<blockquote>
<p><em>"LLM 处理的资产（skills、工作流、schemas）保持纯英文；以人为主要受众的制品会被翻译。"</em></p>
</blockquote>
<p>也就是说：AI 智能体读取的内容——skills、<code>.claude/</code>、<code>.gemini/</code>、<code>.agent/</code> 中的工作流、<code>dist/.straymark/schemas/</code> 中的 schemas、<code>dist/.straymark/audit-prompts/</code> 中的 prompts——只以英文存在。人类阅读的内容——文档、原则、贡献指南、面向人类的模板文本——则翻译为三种语言。</p>
<p>这与第 13 篇文章记录为通用 <code>AGENTS.md</code> 知识源头的原则相同：智能体是单一的技术受众，不会从翻译中受益。此处值得记录的是，5 月时这条规则已作为操作标准写入 PR，而非作为直觉存在。技术提案引用它；审阅者用它来决定新文件是否进入 <code>i18n/</code> 目录。</p>
<p>PR #144 中还有一个细节值得逐字记录：</p>
<blockquote>
<p><em>"docs/contributors/TRANSLATION-GUIDE.md 的缺口是有意为之——其目标受众本就能读英文才能阅读该指南。"</em></p>
</blockquote>
<p>翻译指南有意保留英文，因为其受众——将框架翻译为其他语言的贡献者——必然已能读英文。这是操作上的诚实：20/20/20 对等并非仓库每个文件都是 22/22/22。它是对翻译能增加用户价值的文件达到 20/20/20。总覆盖率与适用覆盖率的区别。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-那枚没有引入任何新事物的徽章">4. 那枚没有引入任何新事物的徽章<a href="https://straymark.dev/zh-CN/blog/opening-the-framework#4-%E9%82%A3%E6%9E%9A%E6%B2%A1%E6%9C%89%E5%BC%95%E5%85%A5%E4%BB%BB%E4%BD%95%E6%96%B0%E4%BA%8B%E7%89%A9%E7%9A%84%E5%BE%BD%E7%AB%A0" class="hash-link" aria-label="4. 那枚没有引入任何新事物的徽章的直接链接" title="4. 那枚没有引入任何新事物的徽章的直接链接" translate="no">​</a></h2>
<p>PR #154 是 <code>fw-4.13.4</code> 的教学对立面。<code>fw-4.13.4</code> 关闭技术工作，PR #154 则发布已经存在的内容。其 12 行变更向 README 中添加了这些内容（英文、西班牙文和中文各一份）：</p>
<div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token url" style="color:#36acaa">[</span><span class="token url content" style="color:#36acaa">![CLA assistant</span><span class="token url" style="color:#36acaa">](</span><span class="token url" style="color:#36acaa">https://cla-assistant.io/readme/badge/StrangeDaysTech/straymark</span><span class="token url" style="color:#36acaa">)</span><span class="token plain">](https://cla-assistant.io/StrangeDaysTech/straymark)</span><br></div></code></pre></div></div>
<p>一枚徽章。一张出现在 README 顶部、与其他项目徽章（许可证、版本、下载量）并排的小图片。视觉上平平无奇。</p>
<p>但这枚徽章不是装饰。它是一个信号。这个信号说了一件具体的事：<em>"这个仓库接受外部贡献，规则在这里"</em>。任何打开 README 的访客都可以点击该徽章，来到 CLA Assistant——这个服务会自动在任何贡献者的第一个 pull request 上留言，要求他们签署贡献者许可协议。一次签署涵盖未来对该项目的所有贡献。</p>
<p>重要的是，CLA、<code>CONTRIBUTING.md</code>、完整的审阅政策，这一切在 PR #154 之前就已存在。它已编码、运行、生效。框架已经接受外部贡献——只是偶然来访的仓库访客必须打开 <code>CONTRIBUTING.md</code> 才能了解其运作方式。</p>
<p>PR #154 做了一个值得命名的区分：<strong>拥有政策</strong>和<strong>发布政策</strong>是两件不同的事。前者是内部纪律；后者是公开邀请。在 5 月 15 日之前，框架拥有该政策。从 5 月 15 日起，它将其公开发布。</p>
<p>这是一个编辑性变更，而非架构性变更。但一个开源项目决定在 README 顶部放上 CLA Assistant 徽章的那一天，就是它决定正式向第三方呈现自身为可采用项目的那一天。这是框架停止成为一个"一位开发者与其使用者（Sentinel）"的项目、成为一个<em>接受</em>拥有更多使用者、更多贡献者、更多双手的项目的时刻。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-当一个框架感到准备好被看见">5. 当一个框架感到准备好被看见<a href="https://straymark.dev/zh-CN/blog/opening-the-framework#5-%E5%BD%93%E4%B8%80%E4%B8%AA%E6%A1%86%E6%9E%B6%E6%84%9F%E5%88%B0%E5%87%86%E5%A4%87%E5%A5%BD%E8%A2%AB%E7%9C%8B%E8%A7%81" class="hash-link" aria-label="5. 当一个框架感到准备好被看见的直接链接" title="5. 当一个框架感到准备好被看见的直接链接" translate="no">​</a></h2>
<p>这两个里程碑放在一起读，记录了一个年轻框架生命中的特定时刻：它感到准备好被那些没有写过它的人看见的时刻。</p>
<p><code>fw-4.13.4</code> 兑现了技术承诺。如果框架说它支持西班牙语-英语双语并实验性地支持简体中文，那么二十二个规范文件必须以三种语言全部存在。不是二十分之十九。不是二十一分之二十还有两个待处理。20/20/20。完整对等是当访客核验时让承诺可信的东西。</p>
<p>PR #154 兑现了社会承诺。如果框架说它接受外部贡献，README 中的 CLA 徽章就是行业标准的表达方式。没有徽章，开放性存在但需要自行发现；有了徽章，它就有了路标。访客的第一次点击就能找到路径。</p>
<p>值得命名这两个动作的不对称性。关闭技术工作最后的 5% 恰好需要那些成本——一个专项发布、两个翻译文件、一个 CHANGELOG 条目。发布一项已有政策只需要十二行 Markdown。便宜的那个有时是操作上最重要的姿态，因为它改变了外部世界对项目的感知，而没有改变项目本身的任何事情。</p>
<p>这是第 5 篇文章以<em>结构性可见度</em>记录的模式的"打开框架"版本：存在但没有命名的东西，对智能体来说不存在。这里是：存在但没有发布的东西，对外部使用者来说不存在。5 月的七十二小时，两个小里程碑——一个技术性的，一个编辑性的——框架跨越了*"它能工作，但只有写它的人知道"<em>与</em>"它能工作，任何人都能看见"*之间的界线。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-弧线的象征性收束">6. 弧线的象征性收束<a href="https://straymark.dev/zh-CN/blog/opening-the-framework#6-%E5%BC%A7%E7%BA%BF%E7%9A%84%E8%B1%A1%E5%BE%81%E6%80%A7%E6%94%B6%E6%9D%9F" class="hash-link" aria-label="6. 弧线的象征性收束的直接链接" title="6. 弧线的象征性收束的直接链接" translate="no">​</a></h2>
<p>有了这篇文章，博客覆盖了 <code>RETOMAR-AQUI.md §5</code> 在主 H-01 → H-13 弧线关闭后列出的第二批中最后待处理的里程碑。四个已识别的候选——<code>H-?-C</code>（validate + schemas）、<code>H-?-A</code>（通用 AGENTS.md）、<code>H-?-B</code>（完整 i18n 覆盖度）、<code>H-?-E</code>（CLA 徽章）——全部已覆盖：三个有各自的文章（第 12、13 篇和本篇），一个合并覆盖。</p>
<p>写第二批时，将四个里程碑放在一起回看，我学到的是：</p>
<ul>
<li class=""><strong>第 2 阶段 / validate</strong>（第 12 篇）是结构性的：框架开始验证它一直在承诺的事情。</li>
<li class=""><strong>TUI explore</strong>（第 11 篇）是关于可见度的：框架变得可导航。</li>
<li class=""><strong>通用 AGENTS.md</strong>（第 13 篇）是关于覆盖范围的：框架变得可被任何 CLI 发现。</li>
<li class=""><strong>完整 i18n 覆盖度 + CLA 徽章</strong>（本篇）是关于开放的：框架变得对外部访客可见。</li>
</ul>
<p>每一个都覆盖了同一个想法的不同维度，而博客在早期文章中已经编码了这个想法：框架声称自己是什么，不足以让它成为那样；它必须在每一个某人——智能体、外部使用者、访客——可以构建项目模型的表面上存在。第二批的里程碑是第 5 篇文章以一个治理 Issue 命名的<em>结构性可见度</em>的成熟版本。</p>
<p>现在，在第 14 篇之后，队列中没有更多候选了。如果在今天到博客下一个会话激活之间出现新的框架里程碑，RETOMAR-AQUI 将再次被用作恢复点。如果没有出现，这篇文章就是真正的收束——完成博客着手覆盖的清单的收束。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/opening-the-framework#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>我从这个过程中得到的，用四个论断表达：</p>
<ol>
<li class="">
<p><strong>总覆盖率在工作末尾代价很小，在开头代价很大。</strong> 最后的 5%——那两个中文文件——是一个专项发布。但它之所以只是轻微地可关闭，是因为其余 95% 已经完成。20/20/20 对等不是从一开始就有的；它被追求了数月，最终以一个小 PR 关闭。</p>
</li>
<li class="">
<p><strong>拥有政策和发布政策是两件不同的事。</strong> 框架的 CLA 在徽章之前就已存在了数月。十二行 Markdown 将一项内部政策变成了公开邀请。你放上徽章的那天，你没有发明任何东西；你宣告你准备好被找到了。</p>
</li>
<li class="">
<p><strong>什么内容被翻译的操作规则具有存档价值。</strong> 人类阅读的内容会被翻译；智能体阅读的内容以英文存在。这个区分，从一月份第一次 AIDEC 中直觉得出，在 5 月成为明确的审阅标准。每个新框架文件在选择目的地之前都要经过那个问题。</p>
</li>
<li class="">
<p><strong>框架何时变得可被第三方采用是一个决定，而非一种状态。</strong> 有一天，运营者决定框架可以被没有写它的人看见而不感到难堪。那一天以小动作具体化——完成翻译、发布徽章——这些动作不改变功能，但改变姿态。框架停止对自己讲话。</p>
</li>
</ol>
<p>至此，博客关闭第二批。从项目史前史（第 2 篇，一月）开始追溯、以元模式（第 10 篇，五月）完成的弧线，现在也有了其推论：使框架可被采用的里程碑。十四篇文章，二十八个文件（十四篇西班牙语 + 十四篇英语），每种语言两万五千到两万六千字。正如第 10 篇结语所说：<em>博客讲完了这个故事。框架继续前行。</em></p>
<hr>
<p><em>锚点：PR <a href="https://github.com/StrangeDaysTech/straymark/pull/144" target="_blank" rel="noopener noreferrer" class="">#144</a> — <code>fw-4.13.4</code>（2026-05-13 06:20 UTC 合并，完整 i18n 覆盖度）。PR <a href="https://github.com/StrangeDaysTech/straymark/pull/154" target="_blank" rel="noopener noreferrer" class="">#154</a> — README 中的 CLA Assistant 徽章（2026-05-15 00:14 UTC 合并）。完整政策：<code>CONTRIBUTING.md</code>（仓库根目录 + <code>docs/i18n/{es,zh-CN}/CONTRIBUTING.md</code> 中的翻译版本）。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>
<p><em>——StrayMark 博客第二批结束。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="i18n" term="i18n"/>
        <category label="cla" term="cla"/>
        <category label="治理" term="治理"/>
        <category label="开放" term="开放"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[模式之前的手工纪律]]></title>
        <id>https://straymark.dev/zh-CN/blog/manual-discipline-before-the-pattern</id>
        <link href="https://straymark.dev/zh-CN/blog/manual-discipline-before-the-pattern"/>
        <updated>2026-05-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Sentinel 在一个已经一个月没有刷新的 plan 上连续跑了七个 Charter，靠手工执行 CHARTER-18，心里清楚任何一步走错都会埋掉前六个 Charter 的工作。五个小时之内，把这套手工纪律写进了上游治理 —— 当时还没给这个模式起名字。]]></summary>
        <content type="html"><![CDATA[<p><em>Sentinel 在一个已经一个月没有刷新的 plan 上连续跑了七个 Charter，靠手工执行 CHARTER-18，心里清楚任何一步走错都会埋掉前六个 Charter 的工作。五个小时之内，把这套手工纪律写进了上游治理 —— 当时还没给这个模式起名字。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-正确的问题">1. 正确的问题<a href="https://straymark.dev/zh-CN/blog/manual-discipline-before-the-pattern#1-%E6%AD%A3%E7%A1%AE%E7%9A%84%E9%97%AE%E9%A2%98" class="hash-link" aria-label="1. 正确的问题的直接链接" title="1. 正确的问题的直接链接" translate="no">​</a></h2>
<p><a href="https://github.com/StrangeDaysTech/straymark/issues/150" target="_blank" rel="noopener noreferrer" class="">Issue #150</a> 于 5 月 14 日 16:59 UTC 开启，寻求的不是一个<em>是/否</em>的答案。操作员要求的是重新表述这个问题：</p>
<blockquote>
<p><em>"bridge 文档真正需要回答的是如何做，而不是是否做：在多 Charter 执行期间，什么纪律能确保 spec 与代码保持同步，同时避免刷新步骤对已经交付的部分撒谎。"</em></p>
</blockquote>
<p>这套纪律已经存在了。就在那时，它正在 Sentinel 中被手工应用于 <code>CHARTER-18</code>，操作员心里清楚任何一步走错都会埋掉前六个 Charter 的工作。缺少的——Issue 以令人不安的精确度所记录的——是名字。是<em>时机</em>。是未来的采用者无需经历自己摸索之痛就能遵循的规则。</p>
<p>这篇文章涵盖从那个 Issue 到将纪律规范化为上游治理的 PR 之间的五个小时、为规范化提供依据的 12 条实证经验，以及在模式存在之前就演练了该模式的具体操作事件——Sentinel 的 <code>CHARTER-18</code>。下一篇文章将介绍 27 小时后发生的事：名字。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-在同一个-plan-上跑七个-charter">2. 在同一个 plan 上跑七个 Charter<a href="https://straymark.dev/zh-CN/blog/manual-discipline-before-the-pattern#2-%E5%9C%A8%E5%90%8C%E4%B8%80%E4%B8%AA-plan-%E4%B8%8A%E8%B7%91%E4%B8%83%E4%B8%AA-charter" class="hash-link" aria-label="2. 在同一个 plan 上跑七个 Charter的直接链接" title="2. 在同一个 plan 上跑七个 Charter的直接链接" translate="no">​</a></h2>
<p>一句话背景：Sentinel 执行 <code>specs/002-commshub/plan.md</code>——写于 2026 年 4 月 21 日 commit <code>be29421</code>——历经<strong>连续七个 Charter</strong>（<code>CHARTER-07</code> 至 <code>CHARTER-17</code>），整整一个日历月，一次都没有刷新 plan。</p>
<p>原始 plan 写得很好。它经过精心撰写，详细描述了四个用户故事（US1-US4），声明了数据模型，勾勒了契约。但这个 plan 是操作员在四月时对代码行为的一个快照式设想。执行过程中发现了 plan 无法预见的事情：</p>
<ul>
<li class="">执行<em>过程中</em>出现的基础设施模式，并被证明是可复用的（<code>core.processed_events</code> 中的命名空间、<code>withRLS</code> 辅助函数、用于不变量执行的 PL/pgSQL 触发器）。</li>
<li class="">plan 假设已解决的代码缺口（<code>AnomalyDetector</code> <em>半成品</em>、缺失幂等性去重、<code>Recipient.TenantID</code> schema 缺口）。</li>
<li class="">在执行过程中逐渐固化的 Charter/审计模式（跨家族审计周期、HTTP 层测试覆盖缺口、带严格大于的游标分页）。</li>
</ul>
<p>当需要为 US5 规划 <code>CHARTER-18</code> 时，plan 已经一个月没更新，而代码上面积累了一个月的实证经验。两者不同步。而 <code>SPECKIT-CHARTER-BRIDGE.md</code>——通常应该回答*"这里该怎么做？"<em>的框架文档——对这种情况保持沉默。规则</em>"在多 Charter 执行期间如何让 spec 与代码保持同步"*还不存在。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-十二条实证经验">3. 十二条实证经验<a href="https://straymark.dev/zh-CN/blog/manual-discipline-before-the-pattern#3-%E5%8D%81%E4%BA%8C%E6%9D%A1%E5%AE%9E%E8%AF%81%E7%BB%8F%E9%AA%8C" class="hash-link" aria-label="3. 十二条实证经验的直接链接" title="3. 十二条实证经验的直接链接" translate="no">​</a></h2>
<p>Issue #150 列举了在 plan 中未经反思而积累的十二条经验。这份按类型分组的原始表格值得保留在视野中：</p>






































































<table><thead><tr><th>#</th><th>类型</th><th>经验</th></tr></thead><tbody><tr><td>1</td><td>基础设施模式</td><td>使用命名空间 <code>module='commshub'</code> 的 <code>core.processed_events</code> 可复用——plan 描述的是自己的表。</td></tr><tr><td>2</td><td>基础设施模式</td><td><code>EnvSecretLoader</code> + Secret Manager 映射（PRs #70/71）——plan 描述的是直接访问。</td></tr><tr><td>3</td><td>基础设施模式</td><td><code>withRLS</code> 辅助函数作为租户隔离的唯一真实来源——plan 没有锚定它。</td></tr><tr><td>4</td><td>基础设施模式</td><td>FR-005 PL/pgSQL 触发器作为不变量执行——plan 描述的是应用层检查。</td></tr><tr><td>5</td><td>代码缺口</td><td><code>AnomalyDetector</code> <em>半成品</em>——plan 暗示它是可用的。</td></tr><tr><td>6</td><td>代码缺口</td><td><code>delivery_log</code> 层的幂等性去重缺失——plan 暗示已协调好。</td></tr><tr><td>7</td><td>代码缺口</td><td><code>Recipient.TenantID</code> schema 缺口——plan 引用 <code>tenant_id</code> 时假设它已存在。</td></tr><tr><td>8</td><td>代码缺口</td><td>每层速率限制覆盖缺失——plan 说*"3 层层次结构"*但没有提到 US1 实现的是统一层。</td></tr><tr><td>9</td><td>Charter 模式</td><td>跨家族外部审计周期强制（连续 6 次）——plan 未提及。</td></tr><tr><td>10</td><td>Charter 模式</td><td>HTTP 层测试覆盖缺口——plan 对测试分层保持沉默。</td></tr><tr><td>11</td><td>Charter 模式</td><td>带严格大于的游标分页元组——plan 保持沉默。</td></tr><tr><td>12</td><td>Charter 模式</td><td>Dispatcher 接口稳定交换模式——plan 保持沉默。</td></tr></tbody></table>
<p>执行期间发现或完善的四个基础设施模式。plan 未预见到的四个代码缺口。当场固化的四个 Charter/审计模式。十二条，在一个月内，全部真实。plan 没有它们。而下一个 Charter——<code>CHARTER-18</code>——将要读取 plan，仿佛它们不存在。</p>
<p>操作员在 Issue 中记录的估算是诚实的：</p>
<blockquote>
<p><em>"由过时前提继承导致 ≥1 个关键/高危发现的估计概率：~50%。"</em></p>
</blockquote>
<p>一句话，就是掷硬币。一半在过时 plan 上启动的 Charter 会以一个后续审计周期必须在关闭前原子性修复的关键发现结束。另一半只是侥幸逃脱。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-方案四手工应用的纪律">4. 方案四：手工应用的纪律<a href="https://straymark.dev/zh-CN/blog/manual-discipline-before-the-pattern#4-%E6%96%B9%E6%A1%88%E5%9B%9B%E6%89%8B%E5%B7%A5%E5%BA%94%E7%94%A8%E7%9A%84%E7%BA%AA%E5%BE%8B" class="hash-link" aria-label="4. 方案四：手工应用的纪律的直接链接" title="4. 方案四：手工应用的纪律的直接链接" translate="no">​</a></h2>
<p>同一天，在这套纪律还没有名字之前，Sentinel 内部发生的事：操作员评估了五个具体方案，选择了第四个。Sentinel 的私有 AIDEC——<code>AIDEC-2026-05-14-001-speckit-plan-scope-limited-us5-refresh.md</code>——毫不掩饰地记录了这一分析。我在这里转述它，因为详细内容在私有仓库中，但推理结构才是关键所在：</p>
<ul>
<li class=""><strong>方案一——<em>"和以前一样读"</em>。</strong> 保持 plan 过时状态，像前六次一样读取。代价：在后续审计周期中偿还债务，约 50% 的关键发现概率。</li>
<li class=""><strong>方案二——用 <code>/speckit-plan</code> 重新生成一切。</strong> 风险：覆盖关于 US1-US4 的断言，而真实代码<em>并不</em>满足这些断言；生成器不知道当前状态。</li>
<li class=""><strong>方案三——不受限制地在另一个 PR 中刷新。</strong> 与方案二相同的风险，加上评审摩擦。</li>
<li class=""><strong>方案四——范围限定刷新 + 3 个关卡。</strong> 选定方案。</li>
<li class=""><strong>方案五——推迟 CHARTER-18，先做重构 Charter。</strong> 日历成本高，相对方案四边际收益低。</li>
</ul>
<p>第四个方案由三个具体机制组成：</p>
<p><strong>第一机制——<em>范围限定提示词</em>。</strong> 带着明确限制性<em>提示词</em>运行 <code>/speckit-plan</code>：仅重新生成对应 US5 的第 7+8 阶段；将 US1-US4 逐字节保留原样，用智能体必须遵守的 <code>&lt;!-- LOCKED: prior US shipped --&gt;</code> 注释标记。没有这个明确限制，SpecKit 的重新生成是破坏性的：它重写整个 plan，覆盖真实代码已经与之矛盾的描述。</p>
<p><strong>第二机制——<em>关卡 (a)：对代码现实的验证</em>。</strong> 一个临时脚本，约 30 行 bash + grep，将 <code>data-model.md</code> 中每个非 US5 实体与 <code>db/migrations/*.sql</code> 中的真实 schema 进行差异比较，并将 <code>contracts/*.md</code> 中每个非 US5 端点与 <code>internal/modules/commshub/handler_*.go</code> 中的实际处理程序签名进行比较。任何分歧都会阻止合并。脚本存在于 Sentinel 的仓库中；它不优雅，它是操作性的。框架后来规范化为关卡 (a) 的东西当时已经在那里了，以手工 bash 的形式。</p>
<p><strong>第三机制——<em>关卡 (b)：逐 hunk 细粒度评审</em>。</strong> 逐文件、逐 hunk 评审 <code>git diff</code>，对<em>锁定</em>部分的任何变更都不接受，除非有理由说明。操作员在 AIDEC 的 R1 中明确记录了预期的摩擦：如果智能体重新生成的 hunk 比必要的多，人工评审者会感到疲惫，纪律就会崩溃。缓解措施：超过 50 行的 hunk 触发 P1 评审。</p>
<p><strong>第四机制——<em>关卡 (c)：两个 PR 拆分</em>。</strong> 刷新是一个独立的 PR，针对<strong>当前代码</strong>进行评审。随后的 Charter 填充是另一个 PR，针对已经刷新的 plan 进行评审。不要混合两个评审——因为如果混合在一起，刷新的债务就会藏在新工作后面。</p>
<p>四个机制。手工应用，没有任何上游文档支持。一个单独的采用者（我），一个单独的会话，以及始终伴随着的感觉——在解决一个以前从未有人解决过的案例。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-结果">5. 结果<a href="https://straymark.dev/zh-CN/blog/manual-discipline-before-the-pattern#5-%E7%BB%93%E6%9E%9C" class="hash-link" aria-label="5. 结果的直接链接" title="5. 结果的直接链接" translate="no">​</a></h2>
<p><code>CHARTER-18</code> 当天就关闭了。Charter 遥测指标，逐字记录在关闭 YAML 中：</p>
<ul>
<li class=""><code>estimation_drift_factor: 1.0</code> —— Charter 恰好在估计的小时范围内完成。</li>
<li class=""><code>pre_work.items_discovered_during_planning: 0</code> —— 规划过程中没有出现意外事项。</li>
<li class=""><code>overall_satisfaction: 5/5</code> —— 操作员在整个链条中最好的主观校准评分。</li>
</ul>
<p>比指标更重要的是：<code>CHARTER-18</code> 是<strong>七个 Charter 链条中第一个在不需要中途修复 Charter 的情况下关闭的</strong>。前六个各自至少需要一次原子性事前关闭修复，以弥补声明与交付之间的分歧。CHARTER-18 没有。操作员的结语，直接引用自后来的 <code>CHARTER-CHAIN-EVOLUTION.md</code>：</p>
<blockquote>
<p><em>"PR #76 的 SpecKit 刷新消除了大部分导致先前 Charter 漂移的歧义。不需要中途修复 Charter——research.md 中的 EC1..EC15 实证修正清单将本来是执行前风险的东西吸收为执行中意识。"</em></p>
</blockquote>
<p>手工纪律奏效了。它不只是干净地关闭了 Charter；它改变了风险的性质。过去是执行前风险（在 Charter 执行期间发现分歧，原子性修复）的东西变成了规划前意识（<code>EC1..EC15</code> 清单在声明 Charter 之前列举实证修正）。风险没有被消除；它被移到了一个可以管理的时刻。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-四小时四十分钟">6. 四小时四十分钟<a href="https://straymark.dev/zh-CN/blog/manual-discipline-before-the-pattern#6-%E5%9B%9B%E5%B0%8F%E6%97%B6%E5%9B%9B%E5%8D%81%E5%88%86%E9%92%9F" class="hash-link" aria-label="6. 四小时四十分钟的直接链接" title="6. 四小时四十分钟的直接链接" translate="no">​</a></h2>
<p>时间线值得精确记录，因为这正是博客所要记录的：</p>
<ul>
<li class=""><strong>5 月 14 日，16:59 UTC</strong> —— Issue #150 开启。十二条经验被列举，三个关卡被提出，概率分析被公开。</li>
<li class=""><strong>5 月 14 日，21:39 UTC</strong> —— PR <a href="https://github.com/StrangeDaysTech/straymark/pull/152" target="_blank" rel="noopener noreferrer" class="">#152</a> 合并。<code>fw-4.14.3</code> 发布。*"多 Charter 执行期间的 spec 维护"*作为 <code>SPECKIT-CHARTER-BRIDGE.md</code> 的新章节添加，三个关卡被逐字规范化为框架治理。问题与上游答案之间相隔<strong>四小时四十分钟</strong>。</li>
</ul>
<p>PR #152 作为治理文档记录的，正是私有 AIDEC 在几小时前手工应用的。三个关卡同名：对代码现实的验证（关卡 a）、逐 hunk 细粒度评审（关卡 b）、两个 PR 拆分（关卡 c）。另加四条<em>何时刷新</em>的启发式规则：同一 plan 上 ≥3 个已关闭 Charter、≥4 周 + ≥2 个 Charter、<code>R&lt;N&gt;(new)</code> 数量 &gt; 6、目标 US 触及已完善的基础设施。另加一条明确说明为何不重新运行 <code>/speckit-tasks</code> 的注释（因为它会销毁 <code>[X]</code> 标记和构成历史追踪的 <code>*CHARTER-NN:* &lt;sha&gt;</code> 注解）。另加一条路线图注释，提到未来将机械化关卡 (a) 的 <code>straymark spec-drift</code> CLI。</p>
<p>四小时。毫不夸张地说，框架在操作员另一个窗口里还开着 <code>git diff</code> 的时候就写好了指导文档。纪律与其规范化几乎是同时发生的。这值得记录，因为它颠倒了学术治理机构通常假设的顺序：先理论，后实践。这里反过来了。先实践，在它还疼的时候。再理论，在疼痛还新鲜的时候。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/manual-discipline-before-the-pattern#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>我从这个过程中得到的，用四个论点：</p>
<ol>
<li class="">
<p><strong>模式源于纪律，而不是反过来。</strong> <code>fw-4.14.3</code> 的三个关卡不是抽象设计的；它们是从一个具体的操作事件中<em>提取</em>的，在手工应用数小时后。当框架尊重顺序时，治理是后实证的。</p>
</li>
<li class="">
<p><strong>明确的概率就是纪律。</strong> *"过时前提继承导致关键发现的概率约 50%"<em>不是贝叶斯精确度；它是一个公开声明，迫使决策必须有理由支撑。没有这个数字，</em>"我们应该刷新吗？"*的问题由直觉决定。有了它，就由风险算术决定。</p>
</li>
<li class="">
<p><strong>三十天内十二条经验是实际的节奏。</strong> 这不是可以用*"以后再读"*捕获的噪声。这是原始 plan 无法预见的实证密度，它摧毁了任何长期规划假设。智能体辅助开发的节奏就是如此。</p>
</li>
<li class="">
<p><strong>四小时不是记录；它是过程的属性。</strong> 当纪律已经在一个领域中被应用时，将其规范化到上游是执行，不是设计。正如我们在第 4 篇文章中关于外部审计周期所见（<em>"一天内五个 PR"</em>），以及第 6 篇文章中关于品牌重塑（<em>"四十三分钟"</em>）。框架没有发明这套纪律；它提取了它，并给它起了名字。</p>
</li>
</ol>
<p>接下来，在下一篇文章中，是这个博客所要讲述的弧线的最后一个事件：<em>"模式 1 + 模式 2——链条演化"</em>（<code>H-12</code>）。在 PR #152 之后 27 小时，三个治理关卡结晶为两个命名元模式——<em>事前声明刷新</em>和<em>浮现即修正</em>——附带遥测 schema 和 CLI 辅助工具。这是第 1 篇文章从前方打开的弧线的收尾。</p>
<hr>
<p><em>锚点：<a href="https://github.com/StrangeDaysTech/straymark/issues/150" target="_blank" rel="noopener noreferrer" class="">Issue #150</a>（5 月 14 日 16:59 UTC）。PR <a href="https://github.com/StrangeDaysTech/straymark/pull/152" target="_blank" rel="noopener noreferrer" class="">#152</a> —— <code>fw-4.14.3</code>（5 月 14 日 21:39 UTC）。规范化章节：<code>SPECKIT-CHARTER-BRIDGE.md §"多 Charter 执行期间的 spec 维护"</code>。CHARTER-18 指标报告于 <code>CHARTER-CHAIN-EVOLUTION.md</code>（fw-4.16.0）。私有来源依照 <code>GUIA-EDITORIAL.md §7</code> 转述：Sentinel 仓库中的 <code>AIDEC-2026-05-14-001-speckit-plan-scope-limited-us5-refresh.md</code>。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="charters" term="charters"/>
        <category label="speckit" term="speckit"/>
        <category label="纪律" term="纪律"/>
        <category label="治理" term="治理"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[audit-prompt 是异常值]]></title>
        <id>https://straymark.dev/zh-CN/blog/the-audit-prompt-was-the-outlier</id>
        <link href="https://straymark.dev/zh-CN/blog/the-audit-prompt-was-the-outlier"/>
        <updated>2026-05-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[几十个文件里有一个反着活 —— 一个以英语为正典的框架里，唯一以西班牙语为正典的文件，存在了六个星期，没人发现。一次维护性修复，记录下博客一再遇到的一件事：异常值反过来教你什么是规范。]]></summary>
        <content type="html"><![CDATA[<p><em>几十个文件里有一个反着活 —— 一个以英语为正典的框架里，唯一以西班牙语为正典的文件，存在了六个星期，没人发现。一次维护性修复，记录下博客一再遇到的一件事：异常值反过来教你什么是规范。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-从提交记录的一行说起">1. 从提交记录的一行说起<a href="https://straymark.dev/zh-CN/blog/the-audit-prompt-was-the-outlier#1-%E4%BB%8E%E6%8F%90%E4%BA%A4%E8%AE%B0%E5%BD%95%E7%9A%84%E4%B8%80%E8%A1%8C%E8%AF%B4%E8%B5%B7" class="hash-link" aria-label="1. 从提交记录的一行说起的直接链接" title="1. 从提交记录的一行说起的直接链接" translate="no">​</a></h2>
<p>这是本文的操作性开头。2026 年 5 月 12 日的提交 <code>a8a1ac5</code> 包含如下正文，第一句话是这样的：</p>
<blockquote>
<p><em>"<code>dist/.straymark/audit-prompts/</code> 下的统一 audit-prompt 模板，是框架中唯一一个正典内容以西班牙语存储的制品 —— 其他所有模板、skill、工作流和治理文档，都遵循"英语正典 + <code>i18n/&lt;lang&gt;/</code> 覆盖层"的模式，由 <code>cli/src/utils.rs:146</code> 负责解析。"</em></p>
</blockquote>
<p>就这一个文件。在几十个文件之中。它反着活。而且它就这样活了整整六个星期，没有人发现。</p>
<p>这篇文章刻意写得很短。它所记录的修复 —— PR <a href="https://github.com/StrangeDaysTech/straymark/pull/142" target="_blank" rel="noopener noreferrer" class="">#142</a>、<code>fw-4.13.3</code> 与 <code>cli-3.12.3</code>，合并于 5 月 13 日 —— 并不是什么重大的叙事节点。这是一次维护性工作。但它记录了我在这门手艺中反复遇到的一个规律：异常值有其价值。它们通过对比，教你什么是规范。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-异常值从哪里来">2. 异常值从哪里来<a href="https://straymark.dev/zh-CN/blog/the-audit-prompt-was-the-outlier#2-%E5%BC%82%E5%B8%B8%E5%80%BC%E4%BB%8E%E5%93%AA%E9%87%8C%E6%9D%A5" class="hash-link" aria-label="2. 异常值从哪里来的直接链接" title="2. 异常值从哪里来的直接链接" translate="no">​</a></h2>
<p>audit-prompt 并非生于框架之中。它最初是 Sentinel 的一个本地 <em>skill</em> —— <code>sentinel/.claude/skills/plan-audit/</code> —— 诞生于第 3 篇文章所记录的六 Plan 实验期间，也就是四月。它是一个<em>特定情境下的</em> skill，由操作者（我）以西班牙语编写，理由现在可以坦诚地列出来：这是一次私人实验，操作者说西班牙语，而且这个 skill 此后大概不会有人再读。我用自己的语言随手写了它，因为它只是为我而写。</p>
<p>当实验逐渐凝结成框架 —— 也就是第 4 篇文章所记录的从 fw-4.4.0 到 fw-4.9.0 的演进弧 —— 那个 skill 被移植到了正典路径 <code>dist/.straymark/audit-prompts/audit-prompt.md</code>。移植过程是机械的：复制文件，做够泛化处理使其不再引用 Sentinel 特定的组件，调整 <em>Plan</em> → <em>Charter</em> 的命名。没有做的一件事是：把它翻译成英语。这个文件就以它被编写时的语言进入了框架。没有人做出这个决定，西班牙语就这样成了那一个制品的正典版本。</p>
<p>框架的其余部分 —— 每一个模板、每一个 skill、正典治理文档 —— 自一月起就以英语编写，西班牙语和 zh-CN 作为 <code>i18n/&lt;lang&gt;/</code> 下的覆盖层。audit-prompt 是那个沉默的例外，持续了六周，直到 5 月 12 日。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-如何被发现目视检查">3. 如何被发现：目视检查<a href="https://straymark.dev/zh-CN/blog/the-audit-prompt-was-the-outlier#3-%E5%A6%82%E4%BD%95%E8%A2%AB%E5%8F%91%E7%8E%B0%E7%9B%AE%E8%A7%86%E6%A3%80%E6%9F%A5" class="hash-link" aria-label="3. 如何被发现：目视检查的直接链接" title="3. 如何被发现：目视检查的直接链接" translate="no">​</a></h2>
<p>发现它的不是智能体的涌现性观察。是操作者，靠眼睛发现的。CHANGELOG 条目毫无修辞地记录了这件事：</p>
<blockquote>
<p><em>"在 Sentinel 审计 <code>straymark explore</code> 渲染效果时经验性地浮现出来：用户注意到 audit 周期模板是西班牙语，而其他所有模板和 skill 都是英语。"</em></p>
</blockquote>
<p><code>straymark explore</code> 是框架的 TUI —— 也就是第 14 篇文章将要介绍的交互式文档浏览器。它的价值在于把所有模板并排渲染在同一个视图中。而当所有模板并排显示，其中一个是另一种语言，就显而易见了。就是这样一个观察。</p>
<p>这个数据值得留下，因为它关乎博客的诚实。第 1 篇文章阐述了<em>涌现性观察</em>的模式 —— 智能体标记出没有人要求它标记的事情 —— 第 5 篇文章记录了因<em>结构性可见性</em>缺失而导致该模式不起作用时会发生什么。这个插曲两者都不是。这是一个更平常的观察：框架的一个新界面（TUI）把所有文件同时呈现在视图中，不一致性就变得像你整理书架时发现一本书脊朝内的书一样显眼。工具也能产生可见性。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-修复一次提交里的三个动作">4. 修复：一次提交里的三个动作<a href="https://straymark.dev/zh-CN/blog/the-audit-prompt-was-the-outlier#4-%E4%BF%AE%E5%A4%8D%E4%B8%80%E6%AC%A1%E6%8F%90%E4%BA%A4%E9%87%8C%E7%9A%84%E4%B8%89%E4%B8%AA%E5%8A%A8%E4%BD%9C" class="hash-link" aria-label="4. 修复：一次提交里的三个动作的直接链接" title="4. 修复：一次提交里的三个动作的直接链接" translate="no">​</a></h2>
<p>PR #142 在同一次提交里做了三件事：</p>
<ul>
<li class=""><strong><code>dist/.straymark/audit-prompts/audit-prompt.md</code></strong> 以英语重写为正典版本。312 行。翻译时注意保留了原文的严重程度结构和校准示例。</li>
<li class=""><strong><code>dist/.straymark/audit-prompts/i18n/es/audit-prompt.md</code></strong> 作为覆盖层创建。318 行。原本住在根目录的西班牙语内容，现在回到了它正确的位置。</li>
<li class=""><strong><code>cli/src/commands/charter/audit.rs</code></strong> 接入 i18n 解析器。在此之前，该子命令硬编码了 <code>dist/.straymark/audit-prompts/audit-prompt.md</code> 路径，不查询采用者在 <code>.straymark/config.yml</code> 中的 <code>language</code> 字段。PR 合并后，它会读取项目配置并解析本地化路径 —— 如果采用者设置了 <code>language: es</code>，他们会得到西班牙语覆盖层；如果设置了 <code>language: zh-CN</code>，则回退到英语正典（audit-prompt 尚无 zh-CN 译文）；如果设置了 <code>language: en</code> 或未指定，则得到正典版本。</li>
</ul>
<p>三个新测试覆盖了这三条路径。对英语用户（占多数）零功能变化。对西班牙语用户而言是一次改善：此前，他们收到西班牙语 prompt 是出于偶然（因为正典路径指向了西班牙语）；现在，他们收到西班牙语 prompt 是出于约定（因为解析器做出了决定）。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-顺带的一步">5. 顺带的一步<a href="https://straymark.dev/zh-CN/blog/the-audit-prompt-was-the-outlier#5-%E9%A1%BA%E5%B8%A6%E7%9A%84%E4%B8%80%E6%AD%A5" class="hash-link" aria-label="5. 顺带的一步的直接链接" title="5. 顺带的一步的直接链接" translate="no">​</a></h2>
<p>在做语言切换的同时，操作者借机提取了一处从 Sentinel 起源时遗留下来的具体性。prompt 的 §Step 5 小节 —— 关于发现项的严重程度校准 —— 引用了四月实验中一个真实的案例：<em>"Etapa 12 Pub/Sub stub vs gochannel"</em>。这是一个很有说明性的例子，但它特定于一个没有其他采用者会认识的 Sentinel 组件。</p>
<p>英语译文用一个供应商中立的表述替换了那个示例：<em>"声明的延期，而非缺陷 —— 一个 Charter 引入了一个薄适配层，计划在未来的 Charter 中替换"</em>。那个想法 —— 一个<em>存根</em>，是声明的技术债，而非缺陷 —— 保留下来了。消失的是组件的名称。</p>
<p>这个动作很小，但与博客的原则一脉相承。当有机会在不损失教学性的情况下将框架从某个特定采用者身上解耦，就应该这样做。这与第 3 篇文章为六个 Plan 所记录的、第 4 篇文章为第一个 Charter 所记录的泛化操作如出一辙：经验性内容留在 Sentinel <em>内部</em>；进入框架的是供应商中立的抽象。audit-prompt，在将近一年之后，终于完成了对这条规则的尊重。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-结语">6. 结语<a href="https://straymark.dev/zh-CN/blog/the-audit-prompt-was-the-outlier#6-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="6. 结语的直接链接" title="6. 结语的直接链接" translate="no">​</a></h2>
<p>三个简短的论断：</p>
<ol>
<li class="">
<p><strong>在一个有约定的框架中，单个文件违反约定，是遗留问题的迹象，而不是设计的迹象。</strong> audit-prompt 是异常值，因为它诞生得更早，而当其他制品对齐的时候没有人迁移它。异常值积累来历。</p>
</li>
<li class="">
<p><strong>新工具产生可见性。</strong> 西班牙语到英语的切换，不是通过系统性地审查仓库来决定的。它是因为一个新的 TUI 把所有模板放在同一个屏幕上，不一致性变得显而易见，才被决定的。同样的道理适用于 <code>grep</code>、聚合视图、<code>status</code> 输出：每一个新界面都会暴露出曾经错位的东西。</p>
</li>
<li class="">
<p><strong>小范围的重新标注是去标识化的机会。</strong> 当你触碰一个文件来更改语言时，顺带提取出源自起源的采用者特异性，边际成本为零。博客之前记录过这个模式（Plan → Charter、DevTrail → StrayMark）；这是它的微缩版。框架一次一个文件地变得更加供应商中立。</p>
</li>
</ol>
<p>下一篇文章，是一个已经在第 1 篇文章中擦肩而过、但值得独立处理的插曲：<em>"模式之前的手工纪律"</em>（<code>H-11</code>）。在 <code>Pattern 1</code> 的链式演进机制存在之前，Sentinel 是如何处理 <code>CHARTER-18</code> 的 —— 那段经验性学习，后来在第 1 篇文章中被命名为元模式。</p>
<hr>
<p><em>锚点：PR <a href="https://github.com/StrangeDaysTech/straymark/pull/142" target="_blank" rel="noopener noreferrer" class="">#142</a>。发布版本：<code>fw-4.13.3</code> / <code>cli-3.12.3</code>。关键提交：<code>a8a1ac5</code>。文件：<code>dist/.straymark/audit-prompts/audit-prompt.md</code>（英语正典）+ <code>dist/.straymark/audit-prompts/i18n/es/audit-prompt.md</code>（覆盖层）。CLI：<code>cli/src/commands/charter/audit.rs</code> 接入 i18n 解析器。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="i18n" term="i18n"/>
        <category label="审计" term="审计"/>
        <category label="治理" term="治理"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[TDE 与横向债务]]></title>
        <id>https://straymark.dev/zh-CN/blog/tde-and-transversal-debt</id>
        <link href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt"/>
        <updated>2026-05-12T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Sentinel 关闭了十三个 Charter，里面至少有七处可辨认的横向债务，TDE 却一个都没有创建。文档类型存在，但触发它的操作准则不存在。fw-4.13.0 如何补上这个缺口——以及那一串 PR 怎样顺带把关于 stacked PR 的一课写进了项目规范。]]></summary>
        <content type="html"><![CDATA[<p><em>Sentinel 关闭了十三个 Charter，里面至少有七处可辨认的横向债务，TDE 却一个都没有创建。文档类型存在，但触发它的操作准则不存在。fw-4.13.0 如何补上这个缺口 —— 以及那一串 PR 怎样顺带把关于 stacked PR 的一课写进了项目规范。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-十三个-charter零个-tde">1. 十三个 Charter，零个 TDE<a href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt#1-%E5%8D%81%E4%B8%89%E4%B8%AA-charter%E9%9B%B6%E4%B8%AA-tde" class="hash-link" aria-label="1. 十三个 Charter，零个 TDE的直接链接" title="1. 十三个 Charter，零个 TDE的直接链接" translate="no">​</a></h2>
<p>Issue <a href="https://github.com/StrangeDaysTech/straymark/issues/128" target="_blank" rel="noopener noreferrer" class="">#128</a> 以一个令人不安的不对称开局：</p>
<blockquote>
<p><em>"Sentinel 采用者（主要，fw-4.12.0）在 13 个已关闭的 Charter 中创建了零个 TDE，尽管有 ≥7 处横向债务通过并行机制流转。"</em></p>
</blockquote>
<p>十三个已关闭的 Charter。至少七处可辨认的横向债务——继承自前序 Charter，跨越模块，在会话之间持续存在。零个 TDE。如果这是某个陌生操作者的数据，或许还说得过去。但操作者是我，采用者就是这个在实践中验证框架本身的项目。如果 Sentinel——整个框架被最激进使用的仓库——在十三次尝试中一次都没有产生 TDE，那一定是哪里出了问题。</p>
<p>出问题的不是债务本身——债务通过其他渠道被捕获了（AILOG 内部的 <code>R&lt;N&gt;（新增，不在 Charter 内）</code>，以及 <code>follow-ups-backlog.md</code> 中的条目）。出问题的是，框架中没有任何准则说明"这一具体内容值得创建 TDE，而不是 <code>R&lt;N&gt;</code>"。文档类型存在，操作触发器不存在。</p>
<p>本文记录的是 2026 年 5 月 11 日至 12 日这段经历：如何诊断出缺失的触发器，如何用四条规范标准（<code>fw-4.13.0</code>）填补这个缺口，以及修复问题的 PR 链如何在此过程中留下了一堂关于 "stacked PR" 的操作课，并最终被写入项目的 <code>CLAUDE.md</code>。这是框架在同一弧线中修复了<em>两件不同的事</em>的第一篇博客记录，而其中第二件是无意为之的。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-类型存在触发器不存在">2. 类型存在，触发器不存在<a href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt#2-%E7%B1%BB%E5%9E%8B%E5%AD%98%E5%9C%A8%E8%A7%A6%E5%8F%91%E5%99%A8%E4%B8%8D%E5%AD%98%E5%9C%A8" class="hash-link" aria-label="2. 类型存在，触发器不存在的直接链接" title="2. 类型存在，触发器不存在的直接链接" translate="no">​</a></h2>
<p><code>TDE</code>——<em>Technical Debt Entry</em>（技术债务条目）——自一月份的初始提交以来就存在于框架中（Post 2 记录了这一点：它是 v1.0.0 README 中八种原始类型列表中的一员）。模板在，分类法字段在，<code>06-evolution/technical-debt/</code> 文件夹也在。</p>
<p>缺少的是在规范装置的某个地方写一句话，说*"现在创建一个 TDE"*。Issue #128 毫不留情地指出了这一点：</p>
<blockquote>
<p><em>"阅读面向采用者的文档（<code>AGENT-RULES.md</code>、<code>DOCUMENTATION-POLICY.md</code>、<code>QUICK-REFERENCE.md</code>、<code>CLAUDE.md</code> 自主规则），没有任何明确的触发器说明'现在创建 TDE'。AILOG（随时创建）、AIDEC（考虑替代方案时）、ETH（高风险 PII）、ADR（架构决策）都有触发语言，但 TDE 只是被列为'智能体可以识别'。"</em></p>
</blockquote>
<p><em>"智能体可以识别。"</em> 如果你有心的话就识别吧。进入仓库的智能体读到这句话，耸耸肩，转向 AILOG 和 AIDEC——那两个明确告诉它<em>何时</em>动作。这个缺陷与 Issue #113（Post 5）中的一个如出一辙：制品技术上存在，但规范<em>界面</em>没有给智能体激活它所需的动词。这个案例与 #113 不同之处在于后果。Charter 的情况是看不见就不用新流程。TDE 的情况则更糟：横向债务<em>确实</em>在被捕获——但是碎片化的，没有整合视图，没有跨制品的优先级排序，没有供人类审阅者排序的 <code>assigned_to</code> 或 <code>priority</code> 字段。</p>
<p>Issue 列举后果时措辞直白，因为它详细说明了所有<em>没有发生</em>的事：</p>
<blockquote>
<p><em>"在文档层面没有可查询的'所有待处理技术债务'视图。每个条目没有 <code>影响 × 努力</code> 优先级矩阵。没有向人类审阅者暴露 <code>assigned_to</code> / <code>priority</code> 字段。真正跨越多个 Charter 的架构债务（例如，跨模块的范围授权缺口遗留）被碎片化到每个 Charter 的 R 编号中，而不是合并到一个 TDE 里。"</em></p>
</blockquote>
<p>十三个 Charter 的债务被碎片化为每个 Charter 的 <code>R&lt;N&gt;</code>。从仓库操作者的角度来看：没有整合列表，没有一项被明确标注为继承而来，没有任何债务可以在项目层面进行优先级排序。一切都在仓库里。但没有一样放在仓库为那些从外部进来阅读的人类所构建的地方。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-债务的四条判定标准">3. 债务的四条判定标准<a href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt#3-%E5%80%BA%E5%8A%A1%E7%9A%84%E5%9B%9B%E6%9D%A1%E5%88%A4%E5%AE%9A%E6%A0%87%E5%87%86" class="hash-link" aria-label="3. 债务的四条判定标准的直接链接" title="3. 债务的四条判定标准的直接链接" translate="no">​</a></h2>
<p>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/129" target="_blank" rel="noopener noreferrer" class="">#129</a>（<code>fw-4.13.0</code>，在 Issue 提出整整十三小时后于 5 月 11 日 19:38 UTC 合并）用 <code>AGENT-RULES.md §3</code> 中的新章节——<em>"TDE 与 <code>R&lt;N&gt;（新增，不在 Charter 内）</code>"</em>——填补了这一缺口。四条标准，原文如下：</p>

























<table><thead><tr><th>标准</th><th>适用情形</th></tr></thead><tbody><tr><td><strong>1. 继承自前序 Charter</strong></td><td>该债务字面上继承自上一个 Charter（<em>严格继承</em>），或在遵循上一个 Charter 的模式时重新出现（<em>模式传播</em>）。</td></tr><tr><td><strong>2. 适用于多个模块或 Charter 执行边界</strong></td><td>该债务跨越代码模块<em>或</em>跨越 Charter 之间的执行边界（治理轨迹债务）。</td></tr><tr><td><strong>3. 需要在当前范围之外单独开设 Charter</strong></td><td>解决它需要开设自己的 Charter，超出当前 Charter 的范围。</td></tr><tr><td><strong>4. 需要人类进行优先级排序/分配</strong></td><td>决定优先级或分配超出智能体的职权范围。</td></tr></tbody></table>
<p>操作规则：如果一项债务满足四条标准中的<em>至少一条</em>，它就是一个 TDE。如果一条都不满足，它就留在它出现的那个 Charter 内作为 <code>R&lt;N&gt;</code>。</p>
<p>四条标准各有其逻辑。继承性（1）捕获跨时间持续存在的债务，审阅者希望按来源分组，而不是按 Charter 分组。多模块或多 Charter（2）捕获<em>存在于</em>制品之间而非<em>存在于</em>某一制品内部的债务。独立 Charter（3）承认某些债务足够大，值得拥有自己的周期，因此不适合作为<em>事前</em>声明的风险。人类优先级排序（4）承认自主性的边界：有些决定智能体可以标记但无法解决，这些决定值得有一份文件供人类排队处理。</p>
<p>同一个 PR 还在 TDE 模板的元数据中增加了新字段——<code>promoted_from_followup: FU-NNN | null</code>——以及 <code>FOLLOW-UPS-BACKLOG-PATTERN.md</code> 中关于如何将后续跟进条目晋升为 TDE 的章节。晋升有两种形式，均有字面记录：现有条目的晋升（已在列表上数周的后续跟进条目成熟为 TDE），以及<em>创建时的追溯晋升</em>（创建 TDE 时，承认它源自先前的后续跟进条目并加以标注）。元数据字段完整了追溯链：现在任何 TDE 都可以指回产生它的后续跟进条目（如果有的话）。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-sentinel-的三个-tde实证循环闭合">4. Sentinel 的三个 TDE——实证循环闭合<a href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt#4-sentinel-%E7%9A%84%E4%B8%89%E4%B8%AA-tde%E5%AE%9E%E8%AF%81%E5%BE%AA%E7%8E%AF%E9%97%AD%E5%90%88" class="hash-link" aria-label="4. Sentinel 的三个 TDE——实证循环闭合的直接链接" title="4. Sentinel 的三个 TDE——实证循环闭合的直接链接" translate="no">​</a></h2>
<p>Issue #128 不是靠论证关闭的，而是靠证据。同一天，Sentinel——那个在十三个 Charter 中一个 TDE 都没有产生的同一采用者——创建了三个：</p>
<ul>
<li class="">一个关于 <strong>RequireScope 架构缺口</strong>的 TDE，继承性跨越模块。标准 1 + 2 + 3。</li>
<li class="">一个关于 <strong>HTTP 层缺失测试覆盖</strong>的 TDE，债务跨特性 Charter 持续存在。标准 1 + 4。</li>
<li class="">一个关于<strong>审查早于 Charter 格式 v3 的遗留 AILOG</strong>的 TDE，这个问题需要自己的周期。标准 1 + 3。</li>
</ul>
<p>数小时内三份文档。速度不是关键，关键在于每一份都适用了<em>至少一条</em>标准，且没有任何一份落入灰色地带。标准作为明确的启发式规则发挥了作用——操作者不必自我纠结某项内容是 TDE 还是 <code>R&lt;N&gt;</code>。Issue 在上午开启的实证循环在当天下午以三份文档宣告闭合。</p>
<p>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/136" target="_blank" rel="noopener noreferrer" class="">#136</a>（<code>fw-4.13.1</code>，次日合并）根据同一实践经验对两条标准进行了细化。<em>继承性</em>（1）被明确拆分为<em>严格继承</em>与<em>模式传播</em>，因为 Sentinel 的三个 TDE 之一清晰地展示了第二种情形：债务并非字面上被继承，而是在遵循相同程序时相同模式再度出现。<em>多模块</em>（2）被重新表述为"多个模块<strong>或 Charter 执行边界</strong>"，因为三个 TDE 中的另一个属于治理轨迹型（跨越会话边界，而非跨越代码模块）。这些标准在三个真实案例上经过演练后，只经历了一轮精化。框架再次验证了第 12 条原则：没有任何模式在第二个领域验证之前会结晶成型。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-stacked-pr-的教训">5. Stacked PR 的教训<a href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt#5-stacked-pr-%E7%9A%84%E6%95%99%E8%AE%AD" class="hash-link" aria-label="5. Stacked PR 的教训的直接链接" title="5. Stacked PR 的教训的直接链接" translate="no">​</a></h2>
<p>这里文章故意转了个弯。关闭 Issue #128 的 PR 链 #129 → #131 → #133 留下了一个附带的操作教训——不关于 TDE，而是关于<em>如何不应该</em>叠加分支。这一教训被字面记录在公开仓库 <code>CLAUDE.md</code>（第 181-220 行）的 <strong>"Stacked PRs——避免，或使用合并提交"</strong> 章节中。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="它是怎么出问题的">它是怎么出问题的<a href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt#%E5%AE%83%E6%98%AF%E6%80%8E%E4%B9%88%E5%87%BA%E9%97%AE%E9%A2%98%E7%9A%84" class="hash-link" aria-label="它是怎么出问题的的直接链接" title="它是怎么出问题的的直接链接" translate="no">​</a></h3>
<p>PR #131（<code>cli-3.12.1</code>，验证器修复——它曾拒绝 TDE 上的 <code>status: identified</code>）以 <code>base = #129</code> 打开，因为 <code>#131</code> 需要 <code>#129</code> 的代码才能编译其测试。然后 <code>#129</code> 以<em>压缩合并</em>的方式并入 <code>main</code>。当前的 <code>CLAUDE.md</code> 用值得完整引用的精确措辞描述了这一情形：</p>
<blockquote>
<p><em>"当你把 PR B 叠加在 PR A 的分支上（B 的 base 是 A 的 head，而非 <code>main</code>），而 A 以压缩合并并入 <code>main</code> 时，B 的内容就会被搁置：</em></p>
<p><em>— A 的压缩合并在 <code>main</code> 上创建了一个新提交，其内容等同于 A，但 SHA 与 A 原始提交不同。</em></p>
<p><em>— B 的分支仍然指向 A 的</em>原始<em>提交，而这些提交在 <code>main</code> 上已没有后代。</em></p>
<p><em>— 当 B 被合并时，GitHub 将其合并到 A 的分支（其声明的 base），而不是 <code>main</code>。'合并到 main'从未发生——即使 GitHub UI 显示 B 为 MERGED。"</em></p>
</blockquote>
<p>在 #129/#131 周期中实际产生的结果：GitHub 的 UI 显示 <code>#131</code> 已<em>合并</em>，验证器测试表面上进入了 main，但内容从未到达。PR #133 是后来不得不开启的程序性同步 PR，以将内容送达目的地。诊断耗时：约三小时。理解后的恢复操作耗时：五分钟。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="如何预防">如何预防<a href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt#%E5%A6%82%E4%BD%95%E9%A2%84%E9%98%B2" class="hash-link" aria-label="如何预防的直接链接" title="如何预防的直接链接" translate="no">​</a></h3>
<p><code>CLAUDE.md</code> 记录了两种策略，一种保守，一种务实：</p>
<blockquote>
<p><em>"1. 顺序，而非叠加。等待 PR A 合并到 <code>main</code>，然后将 B 变基到 <code>main</code> 上，以 <code>base = main</code> 作为独立 PR 开启 B。较慢但万无一失。</em></p>
<p><em>2. 如果必须叠加，对父分支使用合并提交（而非压缩）。合并提交保留了共享历史，因此后续将叠加 PR 合并到 <code>main</code> 可以顺畅解决。用更嘈杂的 <code>git log</code> 换取 stacked-PR 的安全性。"</em></p>
</blockquote>
<p>Sentinel 和 StrayMark 默认使用压缩合并。这个选择有助于保持 <code>git log</code> 整洁，每个特性一个决策，每个 PR 一个提交。但代价恰恰在这里：与 "stacked PR" 不兼容。<code>CLAUDE.md</code> 现在所编纂的规则是诚实的：要么不叠加，要么付出提交历史的代价才能叠加。没有第三个选项不会破坏某些东西。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="如何恢复如果已经发生">如何恢复（如果已经发生）<a href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt#%E5%A6%82%E4%BD%95%E6%81%A2%E5%A4%8D%E5%A6%82%E6%9E%9C%E5%B7%B2%E7%BB%8F%E5%8F%91%E7%94%9F" class="hash-link" aria-label="如何恢复（如果已经发生）的直接链接" title="如何恢复（如果已经发生）的直接链接" translate="no">​</a></h3>
<p><code>CLAUDE.md</code> 的恢复部分是该文档中操作性最强的内容。四个步骤：</p>
<blockquote>
<p><em>"1. <code>git checkout -b chore/sync-&lt;B-content&gt;-to-main main</code></em></p>
<p><em>2. <code>git cherry-pick &lt;B 的合并提交 SHA&gt;</code> ——应该是干净的，因为 B 触及的文件在 A 的冲突区域之外（这正是当初可以叠加的原因）。</em></p>
<p><em>3. 推送，以 <code>base = main, head = chore/sync-...</code> 开启 PR。cherry-pick 在 <code>main</code> 上产生新提交，因此不会有冲突。</em></p>
<p><em>4. 如果内容已在 B（原始 PR）中经过审查，分支保护可能需要 <code>--admin</code> 合并——同步 PR 纯属程序性操作。"</em></p>
</blockquote>
<p>最后一步在编辑上很重要：明确说明 <code>--admin</code> 合并<em>仅</em>授权用于此类程序性 PR——即内容已在原始 PR 中经过审查的情形。这是项目"内容经人类审查"规则的唯一例外。</p>
<p>之所以值得将这一教训记录为 TDE 文章的一个子章节——而非独立文章——是因为两者有一个共同特征：都是框架现在明确命名的<strong>横向流程债务</strong>。一个是代码债务（TDE 作为新类型），另一个是工作流债务（"stacked PR" 作为已记录的反模式）。两者同源：是真实的操作摩擦触发了文档的诞生。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-这段经历在仓库中留下了什么">6. 这段经历在仓库中留下了什么<a href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt#6-%E8%BF%99%E6%AE%B5%E7%BB%8F%E5%8E%86%E5%9C%A8%E4%BB%93%E5%BA%93%E4%B8%AD%E7%95%99%E4%B8%8B%E4%BA%86%E4%BB%80%E4%B9%88" class="hash-link" aria-label="6. 这段经历在仓库中留下了什么的直接链接" title="6. 这段经历在仓库中留下了什么的直接链接" translate="no">​</a></h2>
<p>不到二十四小时内四个 PR，三条已记录的教训：</p>
<ul>
<li class=""><strong>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/129" target="_blank" rel="noopener noreferrer" class="">#129</a></strong>（<code>fw-4.13.0</code>，5 月 11 日 19:38 UTC）——TDE 激活触发器。四条标准 + <code>AGENT-RULES.md §3</code> 新章节 + FU → TDE 路径。</li>
<li class=""><strong>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/131" target="_blank" rel="noopener noreferrer" class="">#131</a></strong>（<code>cli-3.12.1</code>，5 月 11 日 19:39 UTC）——验证器接受 TDE 上的 <code>status: identified</code>。没有这个修复，在新触发器下创建的第一个 TDE 会通不过验证。</li>
<li class=""><strong>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/134" target="_blank" rel="noopener noreferrer" class="">#134</a></strong>（5 月 12 日 04:54 UTC）——"stacked PR" 教训记录在 <code>CLAUDE.md</code> 中。它没有修改框架代码，而是修改了仓库的操作规则。</li>
<li class=""><strong>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/136" target="_blank" rel="noopener noreferrer" class="">#136</a></strong>（<code>fw-4.13.1</code>，5 月 12 日 04:54 UTC）——基于 Sentinel 三个真实案例对 TDE 触发器进行细化。</li>
</ul>
<p>关于这段时间线，我最想强调的是：<code>CLAUDE.md</code>（PR #134）在关闭 Issue #128 的同一弧线中被更新了。"stacked PR" 的教训没有等到被遗忘。如果框架的纪律是"每一次重大变化都留下有据可查的痕迹"，这同样适用于操作摩擦，而不仅仅是设计。框架的存在，部分正是为了让昂贵的教训不会消散。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/tde-and-transversal-debt#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>从这个过程中得出的四点结论：</p>
<ol>
<li class="">
<p><strong>没有触发器的文档类型是不可见的类型。</strong> 与 Issue #113 一样，智能体进入仓库时看不到的东西对它来说就不存在。TDE 从一月份起就在技术上存在，但直到四条标准出现在 <code>AGENT-RULES.md §3</code> 中才开始被使用。没有激活动词的结构只是装饰。</p>
</li>
<li class="">
<p><strong>横向债务值得拥有自己的类型。</strong> 将债务碎片化为每个 Charter 的 <code>R&lt;N&gt;</code> 会破坏治理系统最重要的属性：整合视图。四条标准，满足任意一条即可，守住了"这是 Charter 的风险"与"这是项目的债务"之间的界线。</p>
</li>
<li class="">
<p><strong>操作教训也要文档化。</strong> PR #134 在教训浮现的同一天更新了项目的 <code>CLAUDE.md</code>。这不是存档偏执，而是同一规则应用于流程：如果今天这个摩擦耗费了你三小时，就在今天写下这一教训。</p>
</li>
<li class="">
<p><strong>框架从特性和反模式两个方向生长。</strong> 这段经历留下了一个新类型（TDE 作为操作实体）<em>以及</em>一个已记录的反模式（"stacked PR" 与压缩合并不兼容）。两者都是框架的素材，即使只有一个显示为版本号的变更。</p>
</li>
</ol>
<p>下一篇文章是一段简短但具有结构性意义的经历：<em>"西班牙语审计提示词是那个离群值"</em>（<code>H-10</code>）。一个 i18n 惯例细节，揭示了哪种语言一直是规范语言——以及为什么修复它在无意间改变了框架对翻译的理解方式。</p>
<hr>
<p><em>锚点：<a href="https://github.com/StrangeDaysTech/straymark/issues/128" target="_blank" rel="noopener noreferrer" class="">Issue #128</a>。PR <a href="https://github.com/StrangeDaysTech/straymark/pull/129" target="_blank" rel="noopener noreferrer" class="">#129</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/131" target="_blank" rel="noopener noreferrer" class="">#131</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/134" target="_blank" rel="noopener noreferrer" class="">#134</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/136" target="_blank" rel="noopener noreferrer" class="">#136</a>。版本发布：<code>fw-4.13.0</code> / <code>cli-3.12.1</code> / <code>fw-4.13.1</code>。"Stacked PR" 教训已编入仓库 <a href="https://github.com/StrangeDaysTech/straymark/blob/main/CLAUDE.md" target="_blank" rel="noopener noreferrer" class=""><code>CLAUDE.md</code> 第 181-220 行</a>。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="tde" term="tde"/>
        <category label="技术债务" term="技术债务"/>
        <category label="治理" term="治理"/>
        <category label="git工作流" term="git工作流"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[validate 与作为形式层的 schema]]></title>
        <id>https://straymark.dev/zh-CN/blog/validate-and-schemas-as-a-formal-layer</id>
        <link href="https://straymark.dev/zh-CN/blog/validate-and-schemas-as-a-formal-layer"/>
        <updated>2026-05-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[464 行 bash 被一个 flag 替换。从"我检查这看起来像一份文档"变成"我检查这是否满足正典 schema，在你要求的模式下，附带具体的运行结果"。这就是 CLI 路线图 Phase 2 与 validate 的形式化。]]></summary>
        <content type="html"><![CDATA[<p><em>464 行 bash 被一个 flag 替换。从 "我检查这看起来像一份文档" 变成 "我检查这是否满足正典 schema，在你要求的模式下，附带具体的运行结果"。这就是 CLI 路线图 Phase 2 与 validate 的形式化。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-拥有-schema-与针对-schema-进行验证并非同一回事">1. 拥有 schema 与针对 schema 进行验证，并非同一回事<a href="https://straymark.dev/zh-CN/blog/validate-and-schemas-as-a-formal-layer#1-%E6%8B%A5%E6%9C%89-schema-%E4%B8%8E%E9%92%88%E5%AF%B9-schema-%E8%BF%9B%E8%A1%8C%E9%AA%8C%E8%AF%81%E5%B9%B6%E9%9D%9E%E5%90%8C%E4%B8%80%E5%9B%9E%E4%BA%8B" class="hash-link" aria-label="1. 拥有 schema 与针对 schema 进行验证，并非同一回事的直接链接" title="1. 拥有 schema 与针对 schema 进行验证，并非同一回事的直接链接" translate="no">​</a></h2>
<p><code>straymark validate</code> 自 2026 年 3 月 25 日起就已存在 —— PR #27，CLI Phase 1 第一段弧线的组成部分。那时它所做的事情很小：验证 <code>.straymark/</code> 中的文档具有有效的 Markdown 语法、可解析的 frontmatter，以及正确类型的最小字段集。它有用，但称不上<em>执行</em>。</p>
<p>2026 年 5 月 11 日，随着 <code>fw-4.6.0 / cli-3.7.0</code> 发布，改变的是别的东西：CLI 路线图的 Phase 2 关闭了七个 PR 组成的完整弧线（#73 到 #79），并将它们整合进一个 PR（#80）。CHANGELOG 直白地说明了这一点：</p>
<blockquote>
<p><em>"自重新定位以来第一个承载功能的发布……<code>Propuesta/devtrail-cli-roadmap.md</code> 的 Phase 2 以 7 个 bisect-safe PR（#73–#79）落地，在此处汇总供审阅者参考。本次发布关闭了 Sentinel 实验打开的实证循环：Charter 关闭时的遥测、Charter 关闭时的漂移检测，以及针对 <code>review_required: true</code> 文档的正典批准信号。"</em></p>
</blockquote>
<p>对于这篇文章而言，重要的不是七个 PR 作为各自里程碑的意义；而是其中三个触及 <code>validate</code> 命令和 schema 的 PR。3 月份的"我检查这看起来像一份文档"，到 5 月变成了"我检查这是否满足该类型的正典 schema，在你要求的模式下，附带具体的运行结果"。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-从手工-bash-到命令-flag">2. 从手工 bash 到命令 flag<a href="https://straymark.dev/zh-CN/blog/validate-and-schemas-as-a-formal-layer#2-%E4%BB%8E%E6%89%8B%E5%B7%A5-bash-%E5%88%B0%E5%91%BD%E4%BB%A4-flag" class="hash-link" aria-label="2. 从手工 bash 到命令 flag的直接链接" title="2. 从手工 bash 到命令 flag的直接链接" translate="no">​</a></h2>
<p>这次变化中最显眼的部分很小：<code>--staged</code> flag。</p>
<p>在 Phase 2 之前，采用者在提交前验证文档的推荐方式是一个叫做 <code>pre-commit-docs.sh</code> 的 bash 脚本。它位于 <code>dist/.straymark/scripts/</code>，共有 464 行。它做了合理的事情：读取 <code>git diff --cached --name-only</code>，过滤 <code>.straymark/</code> 中的文件，用 <code>awk</code> 解析它们的 frontmatter，检查字段。它能用。而且它是 464 行 bash，每个采用者都必须相信它执行了正确的事情、用正确的版本、用正确的 YAML 解析器。</p>
<p>3 月 28 日的一个 PR（commit <code>d9ea874</code>）用一个 flag 替换了这 464 行：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">straymark validate </span><span class="token parameter variable" style="color:#36acaa">--staged</span><br></div></code></pre></div></div>
<p>同样地读取暂存文件，同样地按 <code>.straymark/</code> 过滤，同样地进行验证。区别在于：今天所有这些都住在二进制文件内的 Rust 中，而不是磁盘上的 bash 里。采用者不必维护脚本；脚本不会在版本之间产生偏差；行为在 Linux、macOS 和 Windows 上完全一致。框架不再要求采用者信任一个松散的脚本，而是给了他们一个带有版本化二进制文件隐含承诺的命令。</p>
<p>这与第 6 篇文章记录的归档纪律模式相同（保留 git 历史而非重写），或第 4 篇文章记录的决策 A1 模式（无需调用 API 的编排）：从"操作者手动执行 X，存在偏差风险"转变为"框架将 X 正典化为一个声明式命令"。<code>--staged</code> 不是一个异域功能；它是被扫除的手工债务。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-三个-v0-schema">3. 三个 v0 schema<a href="https://straymark.dev/zh-CN/blog/validate-and-schemas-as-a-formal-layer#3-%E4%B8%89%E4%B8%AA-v0-schema" class="hash-link" aria-label="3. 三个 v0 schema的直接链接" title="3. 三个 v0 schema的直接链接" translate="no">​</a></h2>
<p>Phase 2 也整合了位于 <code>dist/.straymark/schemas/</code> 的三个 schema：</p>

























<table><thead><tr><th>Schema</th><th>描述内容</th><th>来源</th></tr></thead><tbody><tr><td><code>charter.schema.v0.json</code></td><td>Charter 作为一等实体的结构（第 4 篇）</td><td>Sentinel <code>/plan-audit</code>（6 个周期，格式 v1 → v2 → v3）</td></tr><tr><td><code>charter-telemetry.schema.v0.json</code></td><td>Charter 遥测（第 3 篇，在第 10 篇中扩展）</td><td>Sentinel 的 5 个 <code>PLAN-NN.telemetry.yaml</code> 文件</td></tr><tr><td><code>audit-output.schema.v0.json</code></td><td>外部审计的正典输出（第 4 篇 + 第 10 篇）</td><td>Sentinel 双重审计（Copilot + Gemini + 批判性 Claude）</td></tr></tbody></table>
<p>每个 schema 在其根部都带有一个 <code>$comment</code> 字段。<code>charter.schema.v0.json</code> 中的那个字面上写道：</p>
<blockquote>
<p><em>"EXPERIMENTAL v0。从 Sentinel /plan-audit 结晶而来（6 个周期，格式 v1 → v2 → v3）。在第二个领域（前端、ML 流水线或基础设施即代码）验证之前，不会稳定至 v1.0。"</em></p>
</blockquote>
<p><code>charter-telemetry.schema.v0.json</code> 中的类似：</p>
<blockquote>
<p><em>"EXPERIMENTAL v0。源自 Sentinel 中的 5 个 PLAN-NN.telemetry.yaml 文件（一个项目，Go 后端）—— 与 charter.schema.v0.json 相同的 N=1 领域警告。在第二个领域验证之前，不会稳定至 v1.0。"</em></p>
</blockquote>
<p><code>audit-output.schema.v0.json</code> 中的同理：</p>
<blockquote>
<p><em>"EXPERIMENTAL v0.x。CLI 路线图的 Phase 3。从在 Sentinel 中实证验证的双重审计模式结晶而来。"</em></p>
</blockquote>
<p>三者共享一个明确声明：<em>不经过第二个领域，不进行结晶</em>。这是框架原则 #12，不是以治理文档中的独立段落形式出现，而是<strong>直接写在 JSON Schema 内部，在任何标准验证器都会读取、任何 schema 浏览工具都会向用户呈现的字段中</strong>。框架不隐藏其 schema 的临时性质；它把这一点放在任何人都无法错过的地方。</p>
<p>这比看起来更重要。schema 生态系统（JSON Schema、OpenAPI、Avro）的惯常做法是发布 <code>v1</code>，然后随着变化升版。用 <code>$comment</code> 标注 <code>v0</code> 并声明"这是实验性的，直到第二个领域验证它"，是从一开始就承认第一次结晶并非最终结论。schema 本身是诚实的。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-命令的三种模式">4. 命令的三种模式<a href="https://straymark.dev/zh-CN/blog/validate-and-schemas-as-a-formal-layer#4-%E5%91%BD%E4%BB%A4%E7%9A%84%E4%B8%89%E7%A7%8D%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="4. 命令的三种模式的直接链接" title="4. 命令的三种模式的直接链接" translate="no">​</a></h2>
<p><code>straymark validate</code> 不再是一个只有单一流程的命令。Phase 2 之后，它拥有三种运行模式，每种模式都与生命周期中的一个时刻相耦合：</p>
<ul>
<li class="">
<p><strong><code>all</code> 模式（默认）</strong> —— 不带 flag 的 <code>straymark validate</code>。递归遍历 <code>.straymark/</code>，解析每个 <code>.md</code> 文件，尝试解析其类型（AILOG、AIDEC、ADR、ETH、REQ、TES、INC、TDE、MCARD、SBOM、SEC、DPIA、Charter），应用相应的 schema，并逐文件报告违规。这是<em>完整审计</em>模式。适用于 CI，适用于检查新的仓库，适用于采用者想衡量积累了多少形式债务的场景。</p>
</li>
<li class="">
<p><strong><code>--staged</code> 模式</strong> —— <code>straymark validate --staged</code>。替代 bash 的那个。仅验证 <code>git diff --cached --name-only</code> 报告为已暂存且位于 <code>.straymark/</code> 内的文件。它的自然归宿是 <code>.git/hooks/pre-commit</code>，自 fw-4.6.0 起，<code>straymark init --hooks</code> 会自动安装它。这是<em>运行门控</em>模式 —— 防止损坏文档进入 <code>main</code> 的那个。</p>
</li>
<li class="">
<p><strong><code>--check-pending-reviews</code> 模式</strong> —— <code>straymark validate --check-pending-reviews</code>。三者中最微妙的，也是值得更多篇幅介绍的那个。</p>
</li>
</ul>
<p>在 Phase 2 之前，智能体在 frontmatter 中标记为 <code>review_required: true</code> 的文档（低置信度的 AIDEC、高风险的 AILOG 等）会等待<em>人工批准</em>。但没有正典的方式来<em>发出</em>该批准信号。操作者阅读文档，在心里批准，继续前进。没有结构性的批准痕迹。这正是 Issue <a href="https://github.com/StrangeDaysTech/straymark/issues/67" target="_blank" rel="noopener noreferrer" class="">#67</a>。</p>
<p><code>--check-pending-reviews</code> 关闭了这个漏洞。该命令遍历所有带有 <code>review_required: true</code> 的文档，列出它们的 <code>confidence</code>、<code>risk_level</code>、作者（人类或智能体）以及日期。批准是一个将 <code>review_required: true</code> → <code>review_required: false</code> 的提交，提交信息中带有人类联合署名 —— 而 <code>validate --check-pending-reviews</code> 就会停止列出该文档。批准是一个<em>正典信号</em>，而非与自己的口头约定。</p>
<p>这是关闭智能体循环的部分。框架允许智能体在没有事先人工批准的情况下创建文档（第 1 篇，属性 #2：<em>"无阻断控制的文化许可"</em>）；但在适当时候将其标记为待审；现在有了一个命令来列出待处理项，没有逃生舱口。没有审计文档会无声地丢失。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-phase-2-关闭实证循环">5. Phase 2 关闭实证循环<a href="https://straymark.dev/zh-CN/blog/validate-and-schemas-as-a-formal-layer#5-phase-2-%E5%85%B3%E9%97%AD%E5%AE%9E%E8%AF%81%E5%BE%AA%E7%8E%AF" class="hash-link" aria-label="5. Phase 2 关闭实证循环的直接链接" title="5. Phase 2 关闭实证循环的直接链接" translate="no">​</a></h2>
<p>完整的 CHANGELOG 句子值得保留，因为它凝练了本次发布的意义：</p>
<blockquote>
<p><em>"本次发布关闭了 Sentinel 实验打开的实证循环：Charter 关闭时的遥测、Charter 关闭时的漂移检测，以及针对 <code>review_required: true</code> 文档的正典批准信号（解决 issue #67）。"</em></p>
</blockquote>
<p>三个部分，关闭了一段始于六 Plan 实验（第 3 篇）的弧线：</p>
<ol>
<li class=""><strong>Charter 关闭时的遥测</strong> —— Sentinel 在实验中手工填写的 YAML 块，成为了可验证的 <code>charter-telemetry.schema.v0.json</code>。</li>
<li class=""><strong>Charter 关闭时的漂移检测</strong> —— 145 行的 bash 脚本（<code>check-plan-drift.sh</code>）成为了 <code>straymark charter drift</code> 子命令，集成进验证流程。</li>
<li class=""><strong>正典批准信号</strong> —— 操作者在心里完成的事情，变成了一个具有结构性后果的命令：<code>--check-pending-reviews</code>。</li>
</ol>
<p>这是博客中反复出现的模式：每次框架将 Sentinel 手工实践的内容结晶成命令，都是在实证验证该实践有效之后才进行的。这次的不同之处在于，<strong>验证该结晶的命令是包的一部分</strong>。Phase 2 之前，Sentinel 有模板、声明了 schema，以及文档遵循 schema 的隐含承诺。Phase 2 之后，有了一个<strong>检查</strong>的命令。这不是能力上的变化；这是保证上的变化。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-原则-12转化为-schema">6. 原则 #12，转化为 schema<a href="https://straymark.dev/zh-CN/blog/validate-and-schemas-as-a-formal-layer#6-%E5%8E%9F%E5%88%99-12%E8%BD%AC%E5%8C%96%E4%B8%BA-schema" class="hash-link" aria-label="6. 原则 #12，转化为 schema的直接链接" title="6. 原则 #12，转化为 schema的直接链接" translate="no">​</a></h2>
<p>这篇文章的哲学部分 —— 也是我最想留存记录的部分 —— 是这样的：</p>
<p>三个 v0 schema 的 <code>$comment</code> 编码了框架自 4 月以来一直以散文形式声明的一个原则。第 4 篇将其命名为：<em>"schema 被有意标记为 <code>v0</code> 而非 <code>v1</code>，因为框架的原则 #12 规定，没有第二个领域的验证，schema 不会结晶。"</em> 第 7 篇将其应用于 TDE 设计：<em>"框架再次验证了原则 #12：没有第二个领域，schema 不会结晶。"</em> 第 10 篇将其应用于模式 1 和模式 2：<em>"文档毫不简化地说明：该模式在 Sentinel 中得到了实证验证。等待第二个领域，再进行更高层次的结晶。"</em></p>
<p>Phase 2 做了不同的事情。<strong>它不是声明原则；它把原则写进 schema 本身，写在任何 schema 消费者都会读到的地方。</strong> <code>$comment</code> 不是营销语言；它是协议。任何 JSON Schema 2020-12 验证器都会暴露它；任何解析 schema 的 IDE 都会显示它。这是框架第一次将其形式化的临时性质本身也进行形式化。</p>
<p>如果要用一句话来总结：框架验证它所拥有的，知道它所拥有的是临时的，并将这一点以书面形式写在<em>验证发生的地方</em>。验证临时性是纪律。过早结晶并验证结晶物是神学。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/validate-and-schemas-as-a-formal-layer#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>我从这个过程中得出的四条结论：</p>
<ol>
<li class="">
<p><strong>拥有 schema 与针对 schema 进行验证，并非同一回事。</strong> 第一步是声明性的；第二步是运行性的。Phase 2 做的是第二步，而非第一步。两种框架模式之间的差异 —— <em>"我有承诺"</em> 与 <em>"我有检查承诺的命令"</em> —— 决定了采用者是否能信任所声明的内容。</p>
</li>
<li class="">
<p><strong>"手工 bash → 命令 flag"的模式是结构性的。</strong> <code>--staged</code> 不是一个孤立的功能。它是 Phase 2 版本的同一模式 —— 第 4 篇为外部审计周期记录了它，第 10 篇为 Charter 演化 CLI 辅助工具记录了它：冗长的命令式 → 可读的声明式 → 版本化。每次框架用一个 flag 扫除一个 bash 脚本，它就提升了一层保证。</p>
</li>
<li class="">
<p><strong><code>--check-pending-reviews</code> 关闭了智能体的循环。</strong> 第 1 篇命名为无需预先批准的文化许可，在这里得到完成：智能体可以创建，但标记为待审的文档不会丢失 —— 命令会列出它们，直到人类通过明确的提交批准它们为止。没有这个信号，第 1 篇的文化许可就会是疏忽。</p>
</li>
<li class="">
<p><strong>声明的临时性 &gt; 过早的形式化。</strong> v0 schema 的 <code>$comment</code> 将原则 #12 变成协议，而非教条。任何消费 schema 的工具都知道该形式化是临时的。这是一个年轻框架所能拥有的最强结构性诚实：验证你所拥有的，而不假装你所拥有的是最终定论。</p>
</li>
</ol>
<hr>
<p><em>锚点：PR <a href="https://github.com/StrangeDaysTech/straymark/pull/27" target="_blank" rel="noopener noreferrer" class="">#27</a> —— 原始 <code>validate</code> 版本（2026 年 3 月）。Phase 2：PR #73 至 #79 + PR <a href="https://github.com/StrangeDaysTech/straymark/pull/80" target="_blank" rel="noopener noreferrer" class="">#80</a> —— <code>fw-4.6.0 / cli-3.7.0</code>（合并于 2026-05-11）。Schemas：<code>dist/.straymark/schemas/{charter,charter-telemetry,audit-output}.schema.v0.json</code>。<a href="https://github.com/StrangeDaysTech/straymark/issues/67" target="_blank" rel="noopener noreferrer" class="">Issue #67</a> —— <code>--check-pending-reviews</code> 的起源。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="validate" term="validate"/>
        <category label="schemas" term="schemas"/>
        <category label="governance" term="governance"/>
        <category label="phase-2" term="phase-2"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[对智能体不可见的 Charter]]></title>
        <id>https://straymark.dev/zh-CN/blog/charters-invisible-to-agents</id>
        <link href="https://straymark.dev/zh-CN/blog/charters-invisible-to-agents"/>
        <updated>2026-05-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Issue #113 —— 拥有一个制品与智能体能够看见它之间的差距]]></summary>
        <content type="html"><![CDATA[<p><em>六个小时的工作会话，使用一个能力强的智能体，加载了框架的全部入门资料 —— 一次都没有主动建议使用 Charter。直接被问到时五分钟内就能套用。这不对称的不是能力，是可见性。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-六小时看不见五分钟看见了">1. 六小时看不见，五分钟看见了<a href="https://straymark.dev/zh-CN/blog/charters-invisible-to-agents#1-%E5%85%AD%E5%B0%8F%E6%97%B6%E7%9C%8B%E4%B8%8D%E8%A7%81%E4%BA%94%E5%88%86%E9%92%9F%E7%9C%8B%E8%A7%81%E4%BA%86" class="hash-link" aria-label="1. 六小时看不见，五分钟看见了的直接链接" title="1. 六小时看不见，五分钟看见了的直接链接" translate="no">​</a></h2>
<p>Issue <a href="https://github.com/StrangeDaysTech/straymark/issues/113" target="_blank" rel="noopener noreferrer" class="">#113</a> 开头有这样一句话，事后回看显得既显而易见、又来之不易：</p>
<blockquote>
<p><em>"检测耗时：约 6 小时的会话工作；从未被自主发现。一旦用户提示后解决耗时：约 5 分钟。"</em></p>
</blockquote>
<p>六小时，一个能力强、专注的智能体，加载了框架完整的入门资料 —— <code>STRAYMARK.md</code>、项目章程、<code>CLAUDE.md</code> 清单、可用的 <code>/straymark-*</code> 技能、开始时运行了 <code>/straymark-status</code> —— 却在整整六小时里，从未主动建议使用 Charter。当我直接要求时，五分钟内流程就被纳入了。这种不对称不在于能力，而在于可见性。</p>
<p>这篇文章记录一段简短、具体的事件：Issue #113，于 2026 年 5 月 7 日开启，距 Charter 在框架中正式成为一等实体（fw-4.4.0，5 月 2 日）仅三天。它是本博客第一篇文章的天然对应。那篇文章命名了一切就位时涌现的属性；这篇文章记录了当一个结构性组件存在、但<em>不在智能体首先读取的表面上</em>时会发生什么。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-意外的实验">2. 意外的实验<a href="https://straymark.dev/zh-CN/blog/charters-invisible-to-agents#2-%E6%84%8F%E5%A4%96%E7%9A%84%E5%AE%9E%E9%AA%8C" class="hash-link" aria-label="2. 意外的实验的直接链接" title="2. 意外的实验的直接链接" translate="no">​</a></h2>
<p>这个设置并非刻意设计成实验。它是一个真实项目：用 Rust 从零开始构建的 CLI+TUI 套件，处于第一个 <code>v0.1</code> 版本。智能体是 Claude Opus 4.7，拥有 1M token 的上下文窗口，足以将整个仓库保留在上下文中而无需分页。流程是规范的 SpecKit 流程：<code>/speckit-specify</code>、<code>/speckit-plan</code>、<code>/speckit-tasks</code>，而在任务与实现之间，拆分为 Charter 的想法本应浮现 —— <em>理论上如此</em>。</p>
<p>Issue 以近乎冷静的精确措辞表述如下：</p>
<blockquote>
<p><em>"一个能力强、专注的智能体，遵循规范的项目入门资料（<code>DEVTRAIL.md</code>、项目章程、<code>CLAUDE.md</code> 清单、可用的 <code>/devtrail-*</code> 技能、<code>/devtrail-status</code>），在 plan/tasks 生成过程中，未能自主识别 Charter 作为工作流概念，尽管该项目明显符合其使用场景（多会话实现、多阶段任务、审计价值）。"</em></p>
</blockquote>
<p><em>(该 Issue 仍使用 <code>DEVTRAIL.md</code>，因为按照时间线，更名为 StrayMark 的工作几乎是并行合并的。混合命名是日历上的残留，而非疏忽。)</em></p>
<p>该项目满足 Charter 作为正确单元的每一个条件：多会话实现、多个阶段、审计价值。智能体做了一切正确的事 —— 阅读了章程、运行了 status、识别了可用技能、生成了合理的 SpecKit 计划 —— 却始终没有关联到 Charter。仿佛这个概念根本不存在。对于智能体来说，在它对仓库的模型中，确实不存在。</p>
<p>关于这个 Issue，事后最令人不安的是：智能体并非因能力不足而失败。它失败，是因为框架在无意间构建了一张地图，而其中没有标注 Charter 的位置。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-九个差距汇聚为一">3. 九个差距汇聚为一<a href="https://straymark.dev/zh-CN/blog/charters-invisible-to-agents#3-%E4%B9%9D%E4%B8%AA%E5%B7%AE%E8%B7%9D%E6%B1%87%E8%81%9A%E4%B8%BA%E4%B8%80" class="hash-link" aria-label="3. 九个差距汇聚为一的直接链接" title="3. 九个差距汇聚为一的直接链接" translate="no">​</a></h2>
<p>当操作者（我）开启 Issue 并着手审计问题原因时，问题并非一个，而是九个。而所有这些都是同一个缺陷的不同版本：</p>























































<table><thead><tr><th>#</th><th>差距</th><th>位置</th></tr></thead><tbody><tr><td>1</td><td>框架的规范文档未将 Charter 列为概念</td><td><code>STRAYMARK.md</code>（当时为 <code>DEVTRAIL.md</code>）§6/9/10/11/13/15</td></tr><tr><td>2</td><td><code>CLAUDE.md</code> 清单中没有触发器提示建议使用 Charter</td><td><code>dist/dist-templates/directives/CLAUDE.md</code>（以及 <code>GEMINI.md</code>、<code>copilot-instructions.md</code>）</td></tr><tr><td>3</td><td>项目章程继承了框架的差距</td><td>任何通过 <code>straymark init</code> 安装的 <code>*-constitution.md</code></td></tr><tr><td>4</td><td>不存在 <code>/straymark-charter-new</code> 技能</td><td>技能目录</td></tr><tr><td>5</td><td><code>/straymark-status</code> 的输出中未列出 Charter</td><td>技能 + CLI 子命令 <code>straymark status</code></td></tr><tr><td>6</td><td>审计技能将 Charter 定义为<em>外部</em>制品，而非待生成的表面</td><td><code>/straymark-audit-*</code> 技能</td></tr><tr><td>7</td><td>Charter 模板与其他模板混放，无法区分</td><td><code>dist/.straymark/templates/</code>（根目录）</td></tr><tr><td>8</td><td>SpecKit（<code>plan.md</code>）与 Charter 之间没有概念桥梁</td><td>无显式文档</td></tr><tr><td>9</td><td>智能体最终构建的心智模型是二元的：要么 SpecKit，要么什么都没有</td><td>累积效应</td></tr></tbody></table>
<p>九个失败，一个单一缺陷：Charter 以技术制品的形式存在（有效的 schema、可运行的命令、从 Sentinel 移植的模板），但在智能体入门时构建项目心智模型的任何位置，都没有被命名。</p>
<p>智能体读取 <code>STRAYMARK.md</code> 和章程来理解项目是什么。如果 Charter 不出现，它就不存在。智能体查看可用技能。如果没有 <code>/straymark-charter-new</code>，它就不存在。智能体运行 <code>/straymark-status</code> 来了解仓库中存在哪类文件。如果输出中没有提及 Charter，它们就不存在。智能体在入门过程中经过的每一个表面都重复着同样的沉默。而这些沉默的累积说服了智能体：这个概念不属于这个项目。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-为何这个事件超越了-issue-本身">4. 为何这个事件超越了 Issue 本身<a href="https://straymark.dev/zh-CN/blog/charters-invisible-to-agents#4-%E4%B8%BA%E4%BD%95%E8%BF%99%E4%B8%AA%E4%BA%8B%E4%BB%B6%E8%B6%85%E8%B6%8A%E4%BA%86-issue-%E6%9C%AC%E8%BA%AB" class="hash-link" aria-label="4. 为何这个事件超越了 Issue 本身的直接链接" title="4. 为何这个事件超越了 Issue 本身的直接链接" translate="no">​</a></h2>
<p>值得在此停下来，因为这正是 fw-4.17.0 元模式 —— <em>涌现观察</em>模式 —— 需要<em>先</em>发生才能被表述出来的事件。</p>
<p>#113 之后一周，在 <code>EMERGENT-OBSERVATION-DESIGN.md</code> 中，框架命名了两个属性，其组合能让智能体主动发现没有人要求它发现的事物：制品之间的正式链接（前置数据中的 <code>originating_charter</code>、<code>originating_ailog</code>、<code>originating_spec</code>），以及标记来源之间不一致的文化许可。这两个属性<em>组合</em>后，产生了智能体读取 AILOG、统计 <code>R&lt;N&gt;(new, not in Charter)</code> 条目、并在无人要求的情况下标记差异的效果。</p>
<p>但这一表述有一个前提条件：智能体<em>在进入仓库时能看到</em> Charter。如果 Charter 不在可见表面上，制品之间任何正式链接都无法组合成涌现观察 —— 因为这个组合的其中一个项对智能体来说不存在。</p>
<p>Issue #113 是从反面记录这一前提条件的事件。fw-4.17.0 在该模式存在时命名了它；fw-4.12.0（五天前）填补了妨碍它存在的差距。没有 Issue #113 及其修正，这个元模式就没有立足之处。框架无法发现它看不见的，它也看不见表面没有命名的。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-回应倍增表面">5. 回应：倍增表面<a href="https://straymark.dev/zh-CN/blog/charters-invisible-to-agents#5-%E5%9B%9E%E5%BA%94%E5%80%8D%E5%A2%9E%E8%A1%A8%E9%9D%A2" class="hash-link" aria-label="5. 回应：倍增表面的直接链接" title="5. 回应：倍增表面的直接链接" translate="no">​</a></h2>
<p>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/122" target="_blank" rel="noopener noreferrer" class="">#122</a>（<code>fw-4.12.0</code>，5 月 9 日）以一个单一的操作策略关闭了九个差距：<em>在智能体构建项目模型的每一个位置命名 Charter</em>。PR 的数字是诚实的：+1211 行，−92 行，36 个文件被修改，几乎全是文档和模板。没有代码重构。有的是可见性的重新分配。</p>
<p>主要变更，按组归类：</p>
<ul>
<li class=""><strong>在规范文档中</strong>（<code>STRAYMARK.md</code>）：新增专门介绍 Charter 作为概念的章节（§15），以及在列出文档类型但缺少 Charter 的 §6/9/10/11/13 表格中新增了相应行。</li>
<li class=""><strong>在智能体指令中</strong>（<code>CLAUDE.md</code>、<code>GEMINI.md</code>、<code>copilot-instructions.md</code>）：添加了明确的触发器，教导智能体何时<em>建议</em>使用 Charter，而不仅仅是在被要求时才创建。</li>
<li class=""><strong>在技能中</strong>：新增 <code>/straymark-charter-new</code>（Claude、Gemini、通用版本）。更新 <code>/straymark-status</code> 以列出活跃的 Charter。</li>
<li class=""><strong>在 CLI 中</strong>：向 <code>straymark status</code> 输出添加了 <code>Charters</code> 块，使运行该命令的智能体能在初始信号中看到它。</li>
<li class=""><strong>在模板中</strong>：移至专用子目录 <code>dist/.straymark/templates/charter/</code>，不再与其他模板混放。</li>
<li class=""><strong>新文档</strong>：<code>SPECKIT-CHARTER-BRIDGE.md</code>（171 行，EN/ES/zh-CN）。明确建立了 SpecKit 的 <code>plan.md</code> 与 StrayMark 的 Charter 之间的桥梁 —— SpecKit 功能产生一个或多个 Charter 的四个标准、三种不适用的情形、四个粒度启发式规则，以及 <code>originating_spec ↔ originating_charter ↔ originating_ailog</code> 前置数据链接。</li>
</ul>
<p><code>SPECKIT-CHARTER-BRIDGE.md</code> 的结语毫无隐喻地记录了引发整个变更的案例：</p>
<blockquote>
<p><em>"引用了经验背景（issue #113）：Greenfield Rust CLI/TUI 套件，Claude Opus 4.7 通过规范入口点入门。Charter 最终被采用（2 个 Charter：基础 + MVP），但仅在用户明确提示后 —— 确认差距是系统性的，而非会话特有的。本文档消除了这一差距。"</em></p>
</blockquote>
<p><em>差距是系统性的，而非会话特有的。</em> 这句话是这个 Issue 的操作性教训。不是某天一个分心的智能体；是框架在智能体查看的每一个地方都没有向它讲述 Charter。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-关于结构性可见性的所学">6. 关于结构性可见性的所学<a href="https://straymark.dev/zh-CN/blog/charters-invisible-to-agents#6-%E5%85%B3%E4%BA%8E%E7%BB%93%E6%9E%84%E6%80%A7%E5%8F%AF%E8%A7%81%E6%80%A7%E7%9A%84%E6%89%80%E5%AD%A6" class="hash-link" aria-label="6. 关于结构性可见性的所学的直接链接" title="6. 关于结构性可见性的所学的直接链接" translate="no">​</a></h2>
<p>教训，以散文而非表格的形式书写：</p>
<p>在框架中创建一个制品，与使其对在该框架仓库中工作的智能体可见，并不是同一件事。它们是两个步骤。第一步 —— schema、命令、模板、示例 —— 是可见的步骤：它出现在 CHANGELOG 中，随版本发布，出现在 <code>git log</code> 里。第二步 —— 将制品锚定在智能体进入仓库时的每一个表面上 —— 是不可见的：第二步中没有任何内容本身作为<em>功能</em>出现；它是交叉引用、清单中的行、输出中的条目、文档中的章节。但第二步决定了第一步对智能体来说是否存在。</p>
<p>框架治理的是从制品创建所在的会话之外读取仓库的智能体。每个进入仓库的智能体都从头构建其心智模型，读取规范文档、可用技能、status 输出。如果制品在那里没有被命名，它就不存在于模型中。而没有该制品的模型不会建议使用它，不会审计其缺失，不会标记相关的不一致。如果仓库的表面在入门时没有给智能体正确的名词，智能体的技术能力就无关紧要了。</p>
<p>这就是我现在内部所称的<em>结构性可见性</em>。它不是智能体的属性。它是仓库的属性，进而是框架的责任：每当它将一个新概念具体化时，必须倍增命名该概念的表面。单一表面 —— schema、命令、模板 —— 是不够的。至少有九个，而它们正是 Issue #113 中的那九个。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/charters-invisible-to-agents#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>从这一过程中得到的收获，以四点陈述：</p>
<ol>
<li class="">
<p><strong>作为制品存在与对智能体可见不是同一件事。</strong> Charter 有 schema、命令、模板、示例 —— 却依然不可见。两种状态之间的差距，就是 Issue #113 的九个表面。</p>
</li>
<li class="">
<p><strong>六小时与五分钟。</strong> 一个能力强的智能体可以花数小时未能检测到某件事，而一旦被命名，整合只需数分钟。这种不对称不是在诊断智能体，而是在诊断仓库。</p>
</li>
<li class="">
<p><strong>结构性可见性是框架的责任。</strong> 当框架将一个概念具体化时，不以创建它的命令为终点。终点是该概念被命名在智能体构建仓库模型的每一个表面上：规范文档、指令、技能、status、模板、与其他框架的桥梁。</p>
</li>
<li class="">
<p><strong>没有结构性可见性，就没有涌现观察。</strong> 后来让智能体能够发现来源之间不一致的属性 —— 正式链接 + 文化许可 —— 作为前提条件，需要制品从入门的第一刻起就是可见的。Issue #113 记录了当这一前提条件缺失时会发生什么。</p>
</li>
</ol>
<p>接下来，在下一篇文章中：一个与此相邻的事件 —— <em>更名为 StrayMark</em>（<code>H-08</code>）。Issue #113 的解决与从 DevTrail 更名为 StrayMark 处于同一时间弧内，它们共享一件值得命名的事情 —— 一个框架向其智能体和其采用者清晰声明自身身份的责任。</p>
<hr>
<p><em>锚点：<a href="https://github.com/StrangeDaysTech/straymark/issues/113" target="_blank" rel="noopener noreferrer" class="">Issue #113</a>（开启于 2026-05-07）。<a href="https://github.com/StrangeDaysTech/straymark/pull/122" target="_blank" rel="noopener noreferrer" class="">PR #122</a> —— <code>fw-4.12.0</code>（合并于 2026-05-09）。PR 生成的文档：<a href="https://github.com/StrangeDaysTech/straymark/blob/main/dist/.straymark/00-governance/SPECKIT-CHARTER-BRIDGE.md" target="_blank" rel="noopener noreferrer" class=""><code>SPECKIT-CHARTER-BRIDGE.md</code></a>。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="charters" term="charters"/>
        <category label="智能体" term="智能体"/>
        <category label="可观测性" term="可观测性"/>
        <category label="治理" term="治理"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[更名为 StrayMark]]></title>
        <id>https://straymark.dev/zh-CN/blog/the-rebrand-to-straymark</id>
        <link href="https://straymark.dev/zh-CN/blog/the-rebrand-to-straymark"/>
        <updated>2026-05-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[第四次改名与前三次不同——它不是在寻找概念，而是源于一次商标冲突调查。八天、一份 ADR、四十三分钟内的五个 PR。这是项目第一次有纪律的品牌迁移，也展示了把 "in scope / out of scope" 应用到自己的过去意味着什么。]]></summary>
        <content type="html"><![CDATA[<p><em>第四次改名与前三次不同 —— 它不是在寻找概念，而是源于一次商标冲突调查。八天、一份 ADR、四十三分钟内的五个 PR。这是项目第一次有纪律的品牌迁移，也展示了把 "in scope / out of scope" 应用到自己的过去意味着什么。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-这次改名并非在寻找概念">1. 这次改名并非在寻找概念<a href="https://straymark.dev/zh-CN/blog/the-rebrand-to-straymark#1-%E8%BF%99%E6%AC%A1%E6%94%B9%E5%90%8D%E5%B9%B6%E9%9D%9E%E5%9C%A8%E5%AF%BB%E6%89%BE%E6%A6%82%E5%BF%B5" class="hash-link" aria-label="1. 这次改名并非在寻找概念的直接链接" title="1. 这次改名并非在寻找概念的直接链接" translate="no">​</a></h2>
<p>ADR <code>2026-05-08-001</code> 以一句话开篇，对于读过本博客第 2 篇的人来说，这句话显得有些突兀：</p>
<blockquote>
<p><em>"此决策的动因是对商标的法律确定性，而非产品战略或用户反馈。"</em></p>
</blockquote>
<p>1 月份的三次改名（Chronicle → Monimen → DevTrail）是一场仓促的概念探寻。每一次都确认了一种关于产品本质的直觉，却没有任何一次写过 ADR——提交信息是当时存在的全部文档依据。</p>
<p>5 月份的第四次改名性质截然不同。它不是源于寻找概念，而是来自一次商标冲突调查——于 5 月初通过 Claude.ai 网页会话委托进行——调查结果揭示了 <em>"随着项目获得使用者，商标归属存在法律不确定性"</em>。1 月份项目尚无任何使用者，改名可以即兴而为。到了 5 月，项目已有一位使用者（Sentinel）、286 次 <em>crate</em> 下载、两个未解决的 Issue，以及一份公开承诺结构化治理的宣言。再度即兴更名的门槛已经越过。</p>
<p>本文涵盖三件事：5 月 8 日做出的有纪律的决策；翌日四十三分钟内五个 PR 的操作弧线；以及两天后浮现的残余问题。在这一切之下，还有一个更微妙的区分：当品牌迁移纯粹停留在表面时，与当它在无意间也改变了身份认同时，两者有何不同。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-第一次有纪律的改名">2. 第一次有纪律的改名<a href="https://straymark.dev/zh-CN/blog/the-rebrand-to-straymark#2-%E7%AC%AC%E4%B8%80%E6%AC%A1%E6%9C%89%E7%BA%AA%E5%BE%8B%E7%9A%84%E6%94%B9%E5%90%8D" class="hash-link" aria-label="2. 第一次有纪律的改名的直接链接" title="2. 第一次有纪律的改名的直接链接" translate="no">​</a></h2>
<p>5 月 8 日的新意在于形式。这一次：</p>
<ul>
<li class=""><strong>有前期调查。</strong> 不是先决定改名再找理由，而是先发现冲突，再由此推出决策。</li>
<li class=""><strong>有公开的 ADR。</strong> 明确考量了三个备选方案：保留"DevTrail"并接受商标风险；采用遗留名称与新名称并行的混合品牌策略；以 StrayMark 为名、以"新项目"身份重置到 v0.1.0。三者均以明确论据驳回。</li>
<li class=""><strong>有时间成本测算。</strong> ADR 本身这样量化代价：<em>"改名窗口随时间收窄——每增加一位使用者，日后更改的成本就增加一分……法律风险占主导地位。现在行动，只有一位自有使用者，在实质上比日后被迫行动要廉价得多。"</em></li>
</ul>
<p>测算之所以重要，在于其结构性意义。若品牌迁移再推迟两个月——等到第二位、第三位使用者出现——迁移成本将成倍增长。每个使用者的仓库都需要执行 <code>mv .devtrail .straymark</code>，更新 <code>CLAUDE.md</code>/<code>AGENT.md</code>，重新生成流水线。而只有一位使用者（操作者本人）时，成本是可控且可逆的。ADR 所说的 <em>"现在行动在实质上更廉价"</em> 正是如此：这次品牌迁移是一次最小化未来技术债务的操作，而非一项市场营销决策。</p>
<p>这里与第 2 篇文章之间存在一个无意的呼应。那篇以 <em>"大概不会再有另一次品牌迁移了"</em> 收尾。这篇写于 5 月，知道它发生了。承诺与追认的区别在于：第四次改名不同于前三次，它是由外部环境所迫——商标冲突——任何操作者都无法预见，且以 1 月份所没有的纪律加以应对。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-in-scopeout-of-scopeadr-的精妙之处">3. <em>In scope</em>，<em>out of scope</em>——ADR 的精妙之处<a href="https://straymark.dev/zh-CN/blog/the-rebrand-to-straymark#3-in-scopeout-of-scopeadr-%E7%9A%84%E7%B2%BE%E5%A6%99%E4%B9%8B%E5%A4%84" class="hash-link" aria-label="3-in-scopeout-of-scopeadr-的精妙之处的直接链接" title="3-in-scopeout-of-scopeadr-的精妙之处的直接链接" translate="no">​</a></h2>
<p>这篇文章后续所有内容的骨架，正是 ADR 中区分哪些内容需要改名、哪些内容需要保留的那一节。原文引用如下：</p>
<blockquote>
<p><em>"In scope（项目的'活跃状态'）：源代码中的所有标识符、Cargo 元数据、真实来源路径、30 个技能/工作流文件、公开文档、CI 工作流、GitHub 仓库名称。"</em></p>
</blockquote>
<blockquote>
<p><em>"Out of scope（不可变历史，逐字保留）：所有提交、提交信息和 git 历史；本 ADR 之前发布的所有标签；之前发布的所有版本标题和正文；所有先前的 CHANGELOG.md 章节。"</em></p>
</blockquote>
<p>这一区分是整个品牌迁移的骨架。<em>活跃的</em>内容——路径、命令、URL、发布资产、README——一次性完成改名。<em>历史性的</em>内容——git log、<code>devtrail-cli@3.10.0</code> 标签、先前的 CHANGELOG 章节、已关闭的 Issue——按原样保留。项目不向后改写自己的历史。</p>
<p>这与第 3 篇文章为 Plan → Charter 改名所记录的档案纪律如出一辙，现在被应用于全框架范围的操作性品牌迁移。以表格呈现更为直观：</p>




























































<table><thead><tr><th>层级</th><th>执行改名</th><th>保留不动</th></tr></thead><tbody><tr><td>文件系统路径</td><td><code>.devtrail/</code> → <code>.straymark/</code></td><td>—</td></tr><tr><td>Rust 源码</td><td><code>devtrail-cli</code> → <code>straymark-cli</code></td><td>—</td></tr><tr><td>技能/工作流</td><td>30 个文件 <code>devtrail-*</code> → <code>straymark-*</code></td><td>—</td></tr><tr><td>公开文档</td><td>README、CLAUDE.md、ADOPTION-GUIDE</td><td>—</td></tr><tr><td>CI/CD</td><td>资产名称、二进制路径</td><td>标签前缀（<code>fw-</code>、<code>cli-</code>）不变</td></tr><tr><td>GitHub 仓库</td><td><code>StrangeDaysTech/devtrail</code> → <code>StrangeDaysTech/straymark</code></td><td>—</td></tr><tr><td>CHANGELOG</td><td>顶部新增 <code>fw-4.11.0</code> 条目</td><td>先前章节逐字保留</td></tr><tr><td>已发布标签</td><td>—</td><td><code>fw-4.10.0</code>、<code>cli-3.10.0</code> 等保留</td></tr><tr><td>整个 git log</td><td>—</td><td>含"DevTrail"的提交信息完整保留</td></tr><tr><td>遗留 <em>crate</em></td><td>—</td><td>crates.io 上的 <code>devtrail-cli@3.10.0</code> 不 <em>yank</em></td></tr></tbody></table>
<p>版本连续性是最显而易见的证明。下一个版本不是 <code>0.1.0</code> 或 <code>1.0.0</code>，而是 <code>fw-4.11.0</code> 和 <code>cli-3.11.0</code>。版本号无需任何比喻地宣告：这个项目依然是同一个项目。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-五个-pr四十三分钟">4. 五个 PR，四十三分钟<a href="https://straymark.dev/zh-CN/blog/the-rebrand-to-straymark#4-%E4%BA%94%E4%B8%AA-pr%E5%9B%9B%E5%8D%81%E4%B8%89%E5%88%86%E9%92%9F" class="hash-link" aria-label="4. 五个 PR，四十三分钟的直接链接" title="4. 五个 PR，四十三分钟的直接链接" translate="no">​</a></h2>
<p>5 月 9 日，UTC 时间 06:05 至 06:48 之间，五个 PR 依次合并：</p>



































<table><thead><tr><th>PR</th><th>UTC 时间</th><th>内容</th></tr></thead><tbody><tr><td><a href="https://github.com/StrangeDaysTech/straymark/pull/114" target="_blank" rel="noopener noreferrer" class="">#114</a></td><td>06:05</td><td>合并 ADR 本身。零代码改动——先固定决策。</td></tr><tr><td><a href="https://github.com/StrangeDaysTech/straymark/pull/115" target="_blank" rel="noopener noreferrer" class="">#115</a></td><td>06:42</td><td>174 次 <em>rename</em> + 74 次修改。Rust 源码（38 个文件）、测试（18 个）、<code>dist/.devtrail/</code> → <code>dist/.straymark/</code>（通过 <code>git mv</code> 迁移 122 个文件）、30 个技能 × 3 个平台。</td></tr><tr><td><a href="https://github.com/StrangeDaysTech/straymark/pull/116" target="_blank" rel="noopener noreferrer" class="">#116</a></td><td>06:44</td><td>三语言（EN/ES/zh-CN）公开文档。CHANGELOG 前言更新：<em>"StrayMark（前身为 DevTrail；于 2026-05-08 更名）"</em>。</td></tr><tr><td><a href="https://github.com/StrangeDaysTech/straymark/pull/117" target="_blank" rel="noopener noreferrer" class="">#117</a></td><td>06:45</td><td>发布工作流。资产名称（<code>straymark-cli-*</code>）、版本标题。标签前缀不变。</td></tr><tr><td><a href="https://github.com/StrangeDaysTech/straymark/pull/118" target="_blank" rel="noopener noreferrer" class="">#118</a></td><td>06:48</td><td>版本升至 <code>fw-4.11.0</code> / <code>cli-3.11.0</code>。新增 CHANGELOG 章节。</td></tr></tbody></table>
<p>顺序是刻意为之，由外而内：先是法律依据（ADR），再是内部代码，然后是使用者所见（文档），接着是分发（CI），最后是既成事实（发布）。这一序列具有档案意义：六个月后有人按时间顺序回顾 <code>git log</code>，会先看到决策，再看到实施。因果关系就活在仓库里。</p>
<p>有一处值得特别记录，它打乱了原有计划。ADR 原本设想了九个独立的 PR（每个逻辑层一个）。实际执行时，CLI 测试依赖于<em>硬编码</em>路径——<code>include_str!("../../dist/.devtrail/...")</code>——在不同步更新测试的情况下修改路径，会让 CI 变红。计划压缩为五个，是因为各层在技术上耦合，而非因为概念纪律失败。PR #115 的备注毫不掩饰地记录了这一点：<em>"合并是被紧耦合强制的：测试通过 <code>include_str!</code> 使用 <code>dist/.devtrail/</code> 路径。"</em></p>
<p>四十三分钟内完成了一次触及约两百六十个文件的品牌迁移。这样的速度之所以可能，是因为 ADR 在触碰代码之前已关闭了每一个决策。框架原则第 6 条——<em>"当提案写得足够清晰时，实施就是执行，而非设计"</em>——再次得到验证，与第 4 篇文章中外部审计循环的案例如出一辙。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-为何-straymark-改变的不只是名字">5. 为何 StrayMark 改变的不只是名字<a href="https://straymark.dev/zh-CN/blog/the-rebrand-to-straymark#5-%E4%B8%BA%E4%BD%95-straymark-%E6%94%B9%E5%8F%98%E7%9A%84%E4%B8%8D%E5%8F%AA%E6%98%AF%E5%90%8D%E5%AD%97" class="hash-link" aria-label="5. 为何 StrayMark 改变的不只是名字的直接链接" title="5. 为何 StrayMark 改变的不只是名字的直接链接" translate="no">​</a></h2>
<p>到目前为止，ADR 的立场是明确的：品牌迁移停留在表面，概念身份得以保留。但有一个细节值得指出。</p>
<p>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/120" target="_blank" rel="noopener noreferrer" class="">#120</a>——在主弧线结束数小时后合并，同样是 5 月 9 日——在 README 中新增了"Why StrayMark?"一节。这不是代码，而是一份宣言。它说出了项目此前任何文档都未曾明确阐述的内容：</p>
<blockquote>
<p><em>"代码不过是一场心智博弈留下的化石痕迹。真正的工程发生在决策的混沌之中，发生在经过测算的冒险之中，发生在那些你选择不走的路径之中。传统上，所有这些人类足迹都被当作项目历史中的 stray marks（意外涂抹的痕迹）而丢弃。在 Strange Days Tech，我们相信那些痕迹才是信号，而非噪音。"</em></p>
</blockquote>
<p>从这份宣言的视角来看，名字的选择并非随意。那些 <em>stray marks</em>——不规则的痕迹、大多数项目丢弃的意外轨迹——正是框架着力呈现的内容：AIDEC、AILOG、TDE、Charter。在任何传统项目中都可被视为可丢弃噪音的东西，在这个项目中是审计的原材料。这个名字以 <em>DevTrail</em> 从未做到的方式，将产品论点具象化在了其中。<em>DevTrail</em> 是一条中性的路径；<em>StrayMark</em> 是一种主张。</p>
<p>宣言以三行话收尾，后来成为编辑意义上的操作性 <em>tagline</em>：</p>
<blockquote>
<p><em>"Capture the noise. Weave the signal. Humanize the machine."</em></p>
</blockquote>
<p>这里有品牌迁移的一个小小的有趣张力。ADR 说只有表面改变了；同一天的宣言则表明，身份认同也被阐明了——或许是第一次清晰地阐明。这并不矛盾：操作性品牌迁移的时机被用来写下项目中早已存在、却尚未被命名的东西。身份没有改变，它变得可读了。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-留下的未竟之事">6. 留下的未竟之事<a href="https://straymark.dev/zh-CN/blog/the-rebrand-to-straymark#6-%E7%95%99%E4%B8%8B%E7%9A%84%E6%9C%AA%E7%AB%9F%E4%B9%8B%E4%BA%8B" class="hash-link" aria-label="6. 留下的未竟之事的直接链接" title="6. 留下的未竟之事的直接链接" translate="no">​</a></h2>
<p>即便有公开的 ADR 和有纪律的操作弧线，<em>"完成"</em> 依然是一个近似值。</p>
<p>品牌迁移两天后，当 Sentinel 正在准备其下一轮外部审计时，智能体发现了三类残余问题。这段时间线——在 <code>CHANGELOG.md</code> 中以内联勘误的形式诚实记录——值得公开呈现：</p>
<ul>
<li class=""><strong>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/137" target="_blank" rel="noopener noreferrer" class="">#137</a></strong>（5 月 11 日，22:55）——<em>孤立的技能目录</em>。品牌迁移改写了每个 <code>SKILL.md</code> 内部的 <code>name:</code> 字段，但未重命名三个平台的技能目录（<code>.gemini/skills/devtrail-*</code>、<code>.claude/skills/devtrail-*</code>、<code>.agent/workflows/devtrail-*.md</code>）。三十个孤立产物：名称已新，路径仍旧。Gemini CLI 在启动时报告了十条冲突警告。</li>
<li class=""><strong>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/138" target="_blank" rel="noopener noreferrer" class="">#138</a></strong>（5 月 11 日，23:32）——<em>遗留 Charter 路径</em>。三个框架产物在 <code>fw-4.12.0</code> 已将规范路径迁移至 <code>.straymark/charters/</code> 之后，仍引用 <code>docs/charters/</code>。最糟糕的是：<code>pre-pr.sh</code> 钩子以 <code>[ ! -d docs/charters ]</code> 作为门控条件，导致任何 4.12.0 之后的使用者都会静默 <em>exit 0</em>。这个漂移检查已安装七个月，却一直是空操作。</li>
<li class=""><strong>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/139" target="_blank" rel="noopener noreferrer" class="">#139</a></strong>（5 月 12 日，12:02）——<em>安装脚本失效</em>。<code>install.sh</code> 和 <code>install.ps1</code> 仍指向 <code>REPO=StrangeDaysTech/devtrail</code>、<code>BINARY=devtrail</code>、资产名 <code>devtrail-cli-v*</code>。README 已完成品牌迁移并指向新 URL。结果：任何用户从 5 月 9 日起复制 README 命令，运行安装脚本时都会遭遇 GitHub 404。<em>产品在生产环境中宕机了七十二小时。</em></li>
<li class=""><strong>PR <a href="https://github.com/StrangeDaysTech/straymark/pull/140" target="_blank" rel="noopener noreferrer" class="">#140</a></strong>（5 月 12 日，16:49）——<em>微小残余</em>。<code>.gitignore</code> 第一行仍写着 <code># DevTrail - .gitignore</code>。另外在内部开发模式中新增了 <code>Aparador/</code> 条目。</li>
</ul>
<p>教训不在于纪律不足，而在于纪律<em>不能阻止</em>残余——它让残余变得可发现、可记录、可在公开内联勘误中修复。前三次改名（1 月份）大概也有同等程度的残余；我们从未整理它们，因为那时还没有文档记录的习惯。第四次品牌迁移整理了它们。这是项目第一次在公开仓库和 CHANGELOG 本身中承认，操作者没有在第一遍就发现所有问题。</p>
<p>最令人不安的细节——安装脚本宕机七十二小时——是我最想记录的。如果彼时不是一位独自使用的采用者（操作者本人），而是一个团队，那个窗口期将真实地伤害用户。<em>现在行动，只有一位自有使用者，在实质上更廉价</em>，ADR 如此说。这句话买到的正是：有权在七十二小时内犯错，而不伤及任何人。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/the-rebrand-to-straymark#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>从这一过程中，我得出四点结论：</p>
<ol>
<li class="">
<p><strong>并非所有改名都属于同一类型。</strong> 1 月份的三次是在寻找概念；5 月份的那次是在抵御项目未来的法律债务。只有 5 月份的那次能够借助公开 ADR 来完成，因为只有它诞生于外部证据，而非内部直觉。</p>
</li>
<li class="">
<p><strong>表面与身份的区分属于文档，而非修辞。</strong> ADR 本身宣告了哪些内容被改名、哪些内容被保留。那份清单——路径是的、git log 否；版本标题是的、先前标签否——是品牌迁移的骨架。没有这份明确的清单，<em>"品牌迁移"</em> 可以意味着任何你想要的东西。</p>
</li>
<li class="">
<p><strong>身份可以在不改变的情况下变得可读。</strong> <em>"Why StrayMark?"</em> 宣言没有发明项目原本是什么；它第一次清晰地将其阐明。操作性品牌迁移被用来写下那些已经在代码中存在、却尚未进入 README 的内容。身份没有被重写——它被发布了。</p>
</li>
<li class="">
<p><strong>纪律不能阻止残余；它让残余变得可发现。</strong> 两天后的四个<em>勘误</em> PR 不是品牌迁移的失败；它们是证据，证明这次品牌迁移足够诚实，承认了漏网之鱼。1 月份的三次改名大概也有同等情况；它们从未被记录。</p>
</li>
</ol>
<p>下一篇文章，将涉及一个与最后这点直接相关的情节：<em>TDE 作为命名横切面技术债务的机制</em>（<code>H-09</code>）。这是框架用来阐明如何暴露债务的第一个真实案例——并且留下了一个关于 PR 堆叠的惨痛教训，等到谈及那里时，值得专门用一段来讲述。</p>
<hr>
<p><em>参考锚点：ADR <a href="https://github.com/StrangeDaysTech/straymark/blob/main/docs/decisions/ADR-2026-05-08-rebranding-straymark.md" target="_blank" rel="noopener noreferrer" class=""><code>2026-05-08-001</code></a>。核心弧线：PR <a href="https://github.com/StrangeDaysTech/straymark/pull/114" target="_blank" rel="noopener noreferrer" class="">#114</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/115" target="_blank" rel="noopener noreferrer" class="">#115</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/116" target="_blank" rel="noopener noreferrer" class="">#116</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/117" target="_blank" rel="noopener noreferrer" class="">#117</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/118" target="_blank" rel="noopener noreferrer" class="">#118</a>。宣言：PR <a href="https://github.com/StrangeDaysTech/straymark/pull/120" target="_blank" rel="noopener noreferrer" class="">#120</a>。残余修复：PR <a href="https://github.com/StrangeDaysTech/straymark/pull/137" target="_blank" rel="noopener noreferrer" class="">#137</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/138" target="_blank" rel="noopener noreferrer" class="">#138</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/139" target="_blank" rel="noopener noreferrer" class="">#139</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/140" target="_blank" rel="noopener noreferrer" class="">#140</a>。版本：<code>fw-4.11.0</code> / <code>cli-3.11.0</code>。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="品牌迁移" term="品牌迁移"/>
        <category label="治理" term="治理"/>
        <category label="身份认同" term="身份认同"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Charter 作为一等实体与外部审计周期]]></title>
        <id>https://straymark.dev/zh-CN/blog/charters-and-the-external-audit-cycle</id>
        <link href="https://straymark.dev/zh-CN/blog/charters-and-the-external-audit-cycle"/>
        <updated>2026-05-06T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[从 Sentinel 中的手工仪式到正式 CLI 命令]]></summary>
        <content type="html"><![CDATA[<p><em>每个 Charter 在两小时工作之上还要 40 分钟的复制粘贴。纪律在起作用；仪式却变成了问题。Charter 如何成为 CLI 的一等实体，以及那一条把审计编排留给框架、把 prompt 评估交给任何第三方的架构决策。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-当纪律开始变成仪式">1. 当纪律开始变成仪式<a href="https://straymark.dev/zh-CN/blog/charters-and-the-external-audit-cycle#1-%E5%BD%93%E7%BA%AA%E5%BE%8B%E5%BC%80%E5%A7%8B%E5%8F%98%E6%88%90%E4%BB%AA%E5%BC%8F" class="hash-link" aria-label="1. 当纪律开始变成仪式的直接链接" title="1. 当纪律开始变成仪式的直接链接" translate="no">​</a></h2>
<p>4 月末，Sentinel 以一种写出来略显狼狈的感受关闭了当月第四个 Charter：外部审计是有效的——Copilot 9.25，Gemini 9.5，漂移脚本零误报——但每次关闭都要我手动打开三个文件，把三段 prompt 复制进三个不同的窗口，等待回复，再把三份标定 YAML 粘回遥测文件，然后逐一核对哈希值以确认匹配。每个 Charter 在两小时正式工作之外还要 40 分钟的复制粘贴。纪律在起作用；仪式却变成了问题。</p>
<p>5 月 3 日撰写的 <code>audit-skills-design.md</code> 提案毫不掩饰地点明了这一点：</p>
<blockquote>
<p><em>"如果每次关闭时操作员都必须在文件间手动搬运数据，吞吐量将会崩溃，纪律也将从有益的摩擦变成纯粹的仪式。凡是可以可逆地自动化的，都应当自动化；文档保留用于事后人工审计。"</em></p>
</blockquote>
<p>本文涵盖三天——从 2026 年 5 月 2 日到 6 日——期间两条并行主线同时收尾。其一：Charter 从手工实践变成了 CLI 的一等实体。其二：外部审计周期从使用临时 prompt 的手工仪式变成了一条正式命令（背后有一个反直觉的架构决策，值得单独成节）。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-sentinel-已经在做的与-straymark-尚未拥有的">2. Sentinel 已经在做的，与 StrayMark 尚未拥有的<a href="https://straymark.dev/zh-CN/blog/charters-and-the-external-audit-cycle#2-sentinel-%E5%B7%B2%E7%BB%8F%E5%9C%A8%E5%81%9A%E7%9A%84%E4%B8%8E-straymark-%E5%B0%9A%E6%9C%AA%E6%8B%A5%E6%9C%89%E7%9A%84" class="hash-link" aria-label="2. Sentinel 已经在做的，与 StrayMark 尚未拥有的的直接链接" title="2. Sentinel 已经在做的，与 StrayMark 尚未拥有的的直接链接" translate="no">​</a></h2>
<p>5 月 3 日的提案用一句话点明了当时的状况，措辞算不上优雅，却精准：</p>
<blockquote>
<p><em>"Sentinel 有这些技能，StrayMark 没有。"</em></p>
</blockquote>
<p>Sentinel 已有的：<code>sentinel/.claude/skills/plan-audit/</code> 和 <code>plan-audit-review/</code>——本地技能，能生成 prompt，收到回复后自动标定，并合并到遥测文件中。它们可用，已经过六个周期的验证。但它们耦合了 Sentinel 特有的路径（<code>docs/plans/</code>、<code>internal/modules/</code>、<code>go vet</code>）以及 <em>"Plan"</em> 词汇——而这个词汇在一周前刚刚被重命名为 Charter。</p>
<p>StrayMark 尚未拥有的：没有任何等价物。截至 4 月，框架只有文档 + 技能 + 带 <code>init</code>、<code>update</code>、<code>remove</code> 的 CLI。在 Sentinel 中称为 <em>Plan</em> 的单元——预先声明、事后审计的有界工作单元——作为 CLI 工件并不存在，只以实践的形式存在。</p>
<p>fw-4.4.0（5 月 2 日）以及 fw-4.7 → 4.9（5 月 3-5 日）的演进弧线，几乎是逐字地将 Sentinel 的手工做法移植成任何框架使用者都可以调用的通用命令。从 <code>cli-roadmap.md</code> 摘抄的迁移对照表直白地说明了来源：</p>





























<table><thead><tr><th>Sentinel 工件 <em>(2026 年 4 月)</em></th><th>StrayMark 等价物 <em>(2026 年 5 月)</em></th></tr></thead><tbody><tr><td><code>docs/plans/</code> 中的 <code>TEMPLATE.md</code>（v3 版）</td><td><code>dist/.straymark/templates/charter-template.md</code></td></tr><tr><td><code>scripts/check-plan-drift.sh</code>（145 行 bash）</td><td><code>straymark charter drift</code>（Rust 子命令）</td></tr><tr><td>本地技能 <code>plan-audit</code></td><td>通用技能 <code>straymark-audit-prepare</code></td></tr><tr><td><code>audit/plans/05,06/{copilot,gemini,claude}.md</code> 中的双份报告</td><td><code>straymark charter audit</code> 的正式输出</td></tr><tr><td>手动编辑的遥测 YAML</td><td><code>dist/.straymark/schemas/charter-telemetry.schema.v0.json</code></td></tr></tbody></table>
<p>fw-4.4.0 的 CHANGELOG 直白表述：<em>"将 Charter 模式固化——预先声明、事后验证的有界可审计工作单元——该模式源自 Sentinel 六周期 /plan-audit 实验。"</em> 固化才是关键。4 月还是配了 bash 脚本的定制做法，5 月已是带可验证 schema 的正式命令。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-成为一等实体意味着什么">3. 成为"一等实体"意味着什么<a href="https://straymark.dev/zh-CN/blog/charters-and-the-external-audit-cycle#3-%E6%88%90%E4%B8%BA%E4%B8%80%E7%AD%89%E5%AE%9E%E4%BD%93%E6%84%8F%E5%91%B3%E7%9D%80%E4%BB%80%E4%B9%88" class="hash-link" aria-label="3. 成为&quot;一等实体&quot;意味着什么的直接链接" title="3. 成为&quot;一等实体&quot;意味着什么的直接链接" translate="no">​</a></h2>
<p>PR #65（fw-4.4.0 / cli-3.6.0，5 月 2 日）做了三件具体的事：</p>
<ul>
<li class="">创建 <code>straymark charter new</code> 命令。自动递增编号（<code>NN-slug.md</code>），预填来源（若源自已有 AILOG 则填 <code>originating_ailogs</code>；若源自 SpecKit <code>plan.md</code> 则填 <code>originating_spec</code>），并支持 <code>--type X|S|M|L</code> 标志以在第一时间确定规模。</li>
<li class="">引入 <code>charter-template.md</code>，移植自 Sentinel 的 <code>TEMPLATE.md</code> v3。模板内嵌了 4 月实验逐周期固化的六项规范：本地/生产检查分离、以时间而非故事点计量投入、结构化子章节、<code>R&lt;N&gt;</code> 风险记录、通过 AILOG 的合并后协调进行闭环、以及自动核查清单漂移。</li>
<li class="">随附 <code>dist/.straymark/schemas/charter.schema.v0.json</code>——schema 刻意标注为 <code>v0</code> 而非 <code>v1</code>，因为框架原则 #12 规定：未经第二个领域验证的 schema 不得固化。Sentinel 是一个领域，第二个尚缺。</li>
</ul>
<p>PR #68（5 月 3 日）看似动作小，却举足轻重：<em>原子 Charter 关闭模式</em>，格式 v4。在 Sentinel 中，<em>"若 AILOG 记录了偏差，则在合并后更新 Plan 文档"</em> 这一步骤在 TEMPLATE 中仅作为备注存在，没有系统性触发机制。实践中，每当声明内容与交付结果存在偏差，操作员（我本人）要靠记忆来协调文档——而记忆会失效。偏差会在仓库中停留数天乃至数周，直到下一个周期才被发现。格式 v4 将协调变成关闭的强制步骤，而非边注。</p>
<p>PR #69（同日）解决了一个摩擦细节：手动编号。此前需要自行判断下一个 Charter 是 11 还是 12；现在 <code>straymark charter new</code> 读取目录并提议下一个编号。纯粹的用户体验改进。但这类摩擦累积到二十个 Charter 时，决定的是框架被使用还是被抛弃。</p>
<p>三个 PR 在同一个周日和周一的提交潮中发布。这种节奏是刻意为之：一旦模式固化，用户体验细节必须<em>一起</em>关闭，而不是分散在几个 bump 中，让使用者拿到半成品流程。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-一天三个版本">4. 一天三个版本<a href="https://straymark.dev/zh-CN/blog/charters-and-the-external-audit-cycle#4-%E4%B8%80%E5%A4%A9%E4%B8%89%E4%B8%AA%E7%89%88%E6%9C%AC" class="hash-link" aria-label="4. 一天三个版本的直接链接" title="4. 一天三个版本的直接链接" translate="no">​</a></h2>
<p>随后的 fw-4.7.0、fw-4.8.0、fw-4.9.0 是外部审计周期的连续版本。<code>audit-skills-rollout.md</code> 提案如实记录了这一节奏：</p>
<blockquote>
<p><em>"第一阶段在 1 个日历日内完成（2026 年 5 月 3 日连续 5 个 PR），远快于 1.5-2 周的重点估算。吞吐量来自 <code>audit-skills-design.md</code> 中决策已提前固化（D1/D2/D3），以及带有明确优雅降级的 arborist 启发式——实施期间无待定决策。这为原则 #6 提供了额外的操作验证：提案写得好，实施就是执行，而非设计。"</em></p>
</blockquote>
<p>具体的外部审计周期由三条链式命令组成：</p>
<ol>
<li class=""><code>straymark charter audit prepare &lt;CHARTER-NN&gt;</code> ——从已关闭的 Charter、其关联的 AILOG 以及被修改文件的 diff 生成规范审计 prompt。输出：<code>audit/&lt;CHARTER-NN&gt;/{copilot,gemini,claude}.prompt.md</code> 中的三个 <code>.prompt.md</code> 文件。</li>
<li class=""><em>（人工将这些 prompt 带到所选审计工具，收到回复后粘贴回来。）</em></li>
<li class=""><code>straymark charter audit collect &lt;CHARTER-NN&gt;</code> ——接收回复，对照审计 schema 逐一验证，将标定值合并进 Charter 的遥测文件，并输出审计员之间收敛（或发散）的摘要。</li>
</ol>
<p>提案中的决策 D1 是整个体系的基础：</p>
<blockquote>
<p><em>"技能通过 <code>Bash(straymark charter audit *)</code> 委托给正式实现。模板只存放在 <code>dist/.straymark/audit-prompts/</code> 中。技能与 CLI 之间零漂移可能。"</em></p>
</blockquote>
<p>prompt 只有一个存放位置，流程只有一个实现，技能（Claude、Cursor）通过 Bash 调用它。这是朴素的模式——CLI 是唯一来源——却避免了任何拥有两个接口（技能 + CLI）的框架最终都会出现的顽疾：技能和 CLI 因各自独立更新而说出不同的话。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-为什么-cli-只做编排而不调用-api">5. 为什么 CLI 只做编排而不调用 API<a href="https://straymark.dev/zh-CN/blog/charters-and-the-external-audit-cycle#5-%E4%B8%BA%E4%BB%80%E4%B9%88-cli-%E5%8F%AA%E5%81%9A%E7%BC%96%E6%8E%92%E8%80%8C%E4%B8%8D%E8%B0%83%E7%94%A8-api" class="hash-link" aria-label="5. 为什么 CLI 只做编排而不调用 API的直接链接" title="5. 为什么 CLI 只做编排而不调用 API的直接链接" translate="no">​</a></h2>
<p>这是 5 月最奇特的决策，也是我花费最多精力作出的决策。它在 <code>cli-roadmap.md</code> §0 中被字面记录为 <em>"架构决策 A1"</em>：</p>
<blockquote>
<p><em>"A1（第三阶段）：仅编排，v0 中不含 HTTP API 客户端。路线图 §5.4 原本建议'v0 支持 OpenAI/Google/Anthropic'并处理 API 密钥。实际实现是：CLI 准备 prompt，对照 schema 验证输出，并与遥测集成——但不调用 API。操作员手动将 prompt 粘贴到所选审计工具。"</em></p>
</blockquote>
<p>其理由，同样字面呈现：</p>
<blockquote>
<p><em>"实现 3 个 HTTP 客户端需要 1-2 周加上 API 变更时的持续维护（对于实验性 v0 而言过于超前）；人在回路的模式与 Sentinel 的 <code>/plan-audit</code> 一致；符合原则 #10（'不是 LLM 网关'）；在设计上关闭了 RFC #82。HTTP 客户端推迟到 v1，届时真实使用者以数据证明其必要性。"</em></p>
</blockquote>
<p>三个论点，各有分量。</p>
<p><strong>第一个是务实层面的。</strong> 三个 HTTP 客户端——OpenAI、Anthropic、Google——需要一到两周的实现工作，加上每当三方之一有所变更时的持续维护。对于一条手工形式在 Sentinel 中已有经验验证的命令来说，这是荒谬的成本。这是为一个没有任何使用者提出的问题而做的工程。</p>
<p><strong>第二个关乎延续性。</strong> Sentinel 在 4 月验证的，恰恰是人在回路模式：操作员<em>手动</em>将 prompt 粘贴进 Copilot/Gemini/Claude，阅读回复，再粘回来。那个诞生了整条演进弧线的六个 Plan 实验<em>从未</em>调用过 API。如果 CLI 现在自行调用 API，就是用一个未经验证的模式取代一个已经验证的模式，毫无经验依据。</p>
<p><strong>第三个关乎身份。</strong> StrayMark 不是 <em>LLM 网关</em>。框架原则 #10 明确说明了这一点。LangChain、LiteLLM、LiteLLM Proxy、OpenRouter，以及各类包装器——有数十个产品<em>是</em>这个定位——它们对自身所做的事情是有用的。StrayMark 做的是另一件事：它围绕<em>使用</em>这些模型所完成的工作构建纪律，无论是哪个模型。CLI 是否调用 API 改变的是框架<em>是什么</em>；保持<em>仅编排</em>的定位，使身份保持清晰。</p>
<p>这段话中我最在意的部分：<em>"HTTP 客户端推迟到 v1，届时真实使用者以数据证明其必要性。"</em> 这个决策不是被教条关闭的，而是被设置在一个证据门槛之后延后处理的。如果六个月后某个使用者证明人在回路流程破坏了他们的吞吐量，HTTP 客户端就会落地。在那之前，手动将 prompt 粘贴到另一个窗口所需的四十秒摩擦，与维护三个活跃 SDK 的成本相比，微不足道。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-框架决定不自动化什么">6. 框架决定<em>不</em>自动化什么<a href="https://straymark.dev/zh-CN/blog/charters-and-the-external-audit-cycle#6-%E6%A1%86%E6%9E%B6%E5%86%B3%E5%AE%9A%E4%B8%8D%E8%87%AA%E5%8A%A8%E5%8C%96%E4%BB%80%E4%B9%88" class="hash-link" aria-label="6-框架决定不自动化什么的直接链接" title="6-框架决定不自动化什么的直接链接" translate="no">​</a></h2>
<p>值得用同一决策的另一面来收尾。<code>audit-skills-design.md</code> 提案写道：</p>
<blockquote>
<p><em>"凡是可以可逆地自动化的，都应当自动化；文档保留用于事后人工审计。"</em></p>
</blockquote>
<p>前半句解释了 5 月的三个版本：prompt 自动生成，标定值自动合并，漂移自动检测。后半句——<em>"文档保留用于事后人工审计"</em>——解释了哪些内容是刻意保留为手工的。</p>
<p>框架<em>不</em>自动化的内容：</p>
<ul>
<li class=""><strong>操作员在关闭 Charter 前阅读 AILOG。</strong> <code>straymark charter audit prepare</code> 命令会生成 prompt，但操作员<em>必须</em>自行打开 AILOG 并阅读。如果我们将这一步自动化——由智能体来阅读、总结、决策——就会失去那个使 Charter 存在有意义的人类判断时刻。</li>
<li class=""><strong>接受或拒绝外部审计的决策。</strong> CLI 将标定值合并进遥测文件，但不对其采取行动。如果某位审计员给 Charter 打了 4 分，Charter 不会自动重新开启：操作员决定是重新开启、提出异议，还是声明其为<em>可接受的已知债务</em>。这个决策就是纪律；将其自动化会摧毁纪律。</li>
<li class=""><strong>调用模型。</strong> 如上所述：操作员复制 prompt 的暂停时刻，正是他们决定使用哪个模型、调整哪段 prompt、判断问题措辞是否合理的时刻。那个暂停是功能，不是缺陷。</li>
</ul>
<p>贯穿三者的标准只有一个：<em>自动化可逆的、机械的；将需要判断的保留为手工</em>。四十秒的复制粘贴并不算多，如果换来的是让判断留在人手中。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/charters-and-the-external-audit-cycle#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>我从这个过程中得到的四点结论：</p>
<ol>
<li class="">
<p><strong>经验验证有效的实践无需重新发明即可被正式化。</strong> Sentinel 在 4 月完成了验证工作；5 月只是将其移植进框架。固化是迁移，不是从零设计。</p>
</li>
<li class="">
<p><strong>提案写得好，实施就是执行。</strong> 一天三个版本只有在所有决策都在触碰代码之前关闭的情况下才有可能。原则 #6 的表述更为平实，但这就是它的含义。</p>
</li>
<li class="">
<p><strong>并非所有可以自动化的都应该自动化。</strong> 决策 A1——CLI 做编排但不调用 API——是一个哲学决策的技术表达：人在回路不是需要消除的遗留做法，而是支撑纪律的功能特性。</p>
</li>
<li class="">
<p><strong>框架的身份由它做什么和它不做什么共同定义。</strong> <em>"不是 LLM 网关"</em> 是框架原则 #10 中少数几句值得逐字记住的话之一。StrayMark 之所以把自己做的事情做好，恰恰是因为有许多事情它拒绝去做。</p>
</li>
</ol>
<p>下一篇文章：与本博客起点直接相连的方法论事件——<em>Issue #113：智能体看不见的 Charter</em>——框架在此发现，创建命令和 schema 还不够，如果仓库的<em>接触面</em>不能向智能体表达自身。这是本文的自然对立面：这里我们谈的是什么被正式化了；那里我们谈的是什么没有被看见。</p>
<hr>
<p><em>锚点：PR <a href="https://github.com/StrangeDaysTech/straymark/pull/65" target="_blank" rel="noopener noreferrer" class="">#65</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/68" target="_blank" rel="noopener noreferrer" class="">#68</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/69" target="_blank" rel="noopener noreferrer" class="">#69</a>（Charter 一等实体）。提案 <a href="https://github.com/StrangeDaysTech/straymark/blob/main/docs/decisions/proposals/2026-05-03-audit-skills-design.md" target="_blank" rel="noopener noreferrer" class=""><code>audit-skills-design.md</code></a> · <a href="https://github.com/StrangeDaysTech/straymark/blob/main/docs/decisions/proposals/2026-05-03-audit-skills-rollout.md" target="_blank" rel="noopener noreferrer" class=""><code>audit-skills-rollout.md</code></a> · <a href="https://github.com/StrangeDaysTech/straymark/blob/main/docs/decisions/proposals/2026-05-04-audit-cli-flow.md" target="_blank" rel="noopener noreferrer" class=""><code>audit-cli-flow.md</code></a> · <a href="https://github.com/StrangeDaysTech/straymark/blob/main/docs/decisions/proposals/2026-05-03-cli-roadmap.md" target="_blank" rel="noopener noreferrer" class=""><code>cli-roadmap.md</code></a>（决策 A1 §0）。版本 fw-4.4.0 → fw-4.9.0。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="charters" term="charters"/>
        <category label="审计" term="审计"/>
        <category label="cli" term="cli"/>
        <category label="治理" term="治理"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[为一个论题设计的六个 Plan（以及更名为 Charter 的那天）]]></title>
        <id>https://straymark.dev/zh-CN/blog/six-plans-and-the-rename-to-charter</id>
        <link href="https://straymark.dev/zh-CN/blog/six-plans-and-the-rename-to-charter"/>
        <updated>2026-04-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[首次系统性实验，以及 Plan 变为 Charter 的那一天]]></summary>
        <content type="html"><![CDATA[<p><em>纸面上六个 Plan，实际跑了五个 —— Sentinel 在 4 月 25 日至 28 日之间的第一次系统性实验。正是这一轮把一个手工的模式变成了 Charter，即框架中有边界的工作单元。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-一个说明了很多事情的脚注">1. 一个说明了很多事情的脚注<a href="https://straymark.dev/zh-CN/blog/six-plans-and-the-rename-to-charter#1-%E4%B8%80%E4%B8%AA%E8%AF%B4%E6%98%8E%E4%BA%86%E5%BE%88%E5%A4%9A%E4%BA%8B%E6%83%85%E7%9A%84%E8%84%9A%E6%B3%A8" class="hash-link" aria-label="1. 一个说明了很多事情的脚注的直接链接" title="1. 一个说明了很多事情的脚注的直接链接" translate="no">​</a></h2>
<p>在 2026 年 4 月 30 日撰写的 <code>charter-telemetry.md</code> 提案中，文档顶部有一条术语说明：</p>
<blockquote>
<p><em>"本文档所称'Charter'，在 Sentinel 实验中（PLAN-01..06）称为'Plan'。以下引用的实证数据沿用这些历史 Plan 的原始名称；架构形态和前瞻性示例则使用新的正式名称'Charter'。"</em></p>
</blockquote>
<p>这是一条技术性脚注，平实无华。但它精确记录了制品更名的那个瞬间——更有意思的是，它记录了一项存档决策，本博客同样遵守这一决策：当时被称为 <em>Plan</em> 的，在原始记录中依然叫 <em>Plan</em>。向后改写名称，会伪造溯源记录。</p>
<p>这篇文章涉及五天内发生的两件事：框架的首次系统性实验——Sentinel 的六个 Plan，在 4 月 25 日至 28 日之间执行——以及紧随其后、在 4 月 30 日发生的更名。这并非两件独立的事，一件导致了另一件。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-纸面六个-plan实际五个">2. 纸面六个 Plan，实际五个<a href="https://straymark.dev/zh-CN/blog/six-plans-and-the-rename-to-charter#2-%E7%BA%B8%E9%9D%A2%E5%85%AD%E4%B8%AA-plan%E5%AE%9E%E9%99%85%E4%BA%94%E4%B8%AA" class="hash-link" aria-label="2. 纸面六个 Plan，实际五个的直接链接" title="2. 纸面六个 Plan，实际五个的直接链接" translate="no">​</a></h2>
<p>本次实验设计了六个 Plan（<code>PLAN-01</code> 至 <code>PLAN-06</code>），实际执行了五个。PLAN-04 从未运行：它停留在一份包含七项<em>延期功能</em>的目录清单状态，罗列了我们想做但测试周期无法覆盖的事项。如实说明这一点是应有的诚实，因为验证该论题的文档（<code>thesis-validation.md</code>）在其汇总表中公开记录了这一情况：</p>















































<table><thead><tr><th>Plan</th><th>规模</th><th>内容</th><th>格式</th></tr></thead><tbody><tr><td>PLAN-01</td><td>XS</td><td>治理文档部署</td><td>v1</td></tr><tr><td>PLAN-02</td><td>S</td><td>管理端点（<code>gcp-resource</code>）</td><td>v1</td></tr><tr><td>PLAN-03</td><td>XS</td><td>合约版本升级</td><td>v1</td></tr><tr><td>PLAN-05</td><td>M</td><td>按服务配置异常阈值</td><td><strong>v2</strong></td></tr><tr><td>PLAN-06</td><td>XS</td><td>手动重算基准线</td><td><strong>v3</strong></td></tr><tr><td><em>(PLAN-04 目录)</em></td><td>—</td><td>7 项延期功能的路线图，未执行</td><td>—</td></tr></tbody></table>
<p>五个 Plan，四种不同规模：三个 XS、一个 S、一个 M。领域刻意选取异构——运维部署、两个管理端点、合约升级、带配置逻辑的后端功能。这不是合成基准测试，而是 Sentinel 在 4 月 25 日至 28 日之间的真实代码，被划分为有边界的工作单元，以三个模型（Copilot、Gemini、Claude）作为外部审计方，逐一审计。</p>
<p>第六个 Plan 未能执行，这件事有两层意义。显而易见的一层是：实验超出了时间预算。更耐人寻味的是：PLAN-04 是这批 Plan 中唯一规模较大的——七个累积功能，没有自然边界。偏偏<em>这个</em> Plan 没能跑完，已经在暗示这种格式能承载的规模上限。小型 Plan 可以；路线图式的 Plan 不行。这个经验后来被正式确立，无需任何人专门写明：StrayMark 的 Charter 今天是*"有边界的工作单元，耗时数小时到数天，对应一个分支"*，而非季度路线图。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-实验在自身迭代中演进">3. 实验在自身迭代中演进<a href="https://straymark.dev/zh-CN/blog/six-plans-and-the-rename-to-charter#3-%E5%AE%9E%E9%AA%8C%E5%9C%A8%E8%87%AA%E8%BA%AB%E8%BF%AD%E4%BB%A3%E4%B8%AD%E6%BC%94%E8%BF%9B" class="hash-link" aria-label="3. 实验在自身迭代中演进的直接链接" title="3. 实验在自身迭代中演进的直接链接" translate="no">​</a></h2>
<p>五个已执行的 Plan 产生了三种不同的格式。这不是随意为之，而是因为每个周期都暴露了上一个格式未能捕捉到的内容。</p>
<ul>
<li class="">
<p><strong>v1</strong> —— 前三个 Plan（<code>01</code>、<code>02</code>、<code>03</code>）沿用原始格式执行。在审计 AILOG 中识别出五个反复出现的模式，但没有一个被正式化为模板。它们以习惯的形式存在，而非合约。</p>
</li>
<li class="">
<p><strong>v2</strong> —— 针对 PLAN-05，将这五个模式固化进 <code>TEMPLATE.md</code>，并在 README 中添加了"格式约定"章节。仅限文档层面：尚无可执行的内容。假设是：如果格式明确，外部审计方的判断会更趋于一致。AILOG-020 直言不讳：<em>"内部沿用已久但无名可循的模式，对外部审计方而言是不可见的。正式命名，能把实践变成公开的信号。"</em></p>
</li>
<li class="">
<p><strong>v3</strong> —— 针对 PLAN-06，新增了一个模式（自动清单漂移检测），并且这次引入了<em>可执行工具</em>：<code>scripts/check-plan-drift.sh</code>，约 145 行 bash 脚本，用于验证实际交付内容是否与 Plan 中的声明相符。格式第一次从纯文本转变为带有验证器的形态。</p>
</li>
</ul>
<p>我想指出的是这条曲线的形态。v1 捕捉了潜在实践，v2 为其命名，v3 编写了检查它的程序。这正是任何有效标准化的完整弧线——只不过压缩进了五个 Plan，而非五年。</p>
<p>这也是某个事物最早的具体证据——两个月后，它会在 <code>CHARTER-CHAIN-EVOLUTION.md</code> 中被明确命名为模式 1（<em>pre-declare refresh</em>）和模式 2（<em>amend-on-emergence</em>）。在那里作为元模式被正式确立的内容，最初在 4 月，只是一个无名的习惯。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-最有力的证据外部审计方的收敛">4. 最有力的证据：外部审计方的收敛<a href="https://straymark.dev/zh-CN/blog/six-plans-and-the-rename-to-charter#4-%E6%9C%80%E6%9C%89%E5%8A%9B%E7%9A%84%E8%AF%81%E6%8D%AE%E5%A4%96%E9%83%A8%E5%AE%A1%E8%AE%A1%E6%96%B9%E7%9A%84%E6%94%B6%E6%95%9B" class="hash-link" aria-label="4. 最有力的证据：外部审计方的收敛的直接链接" title="4. 最有力的证据：外部审计方的收敛的直接链接" translate="no">​</a></h2>
<p>论题中最关键的假设是：<em>"结构化记录能减少失效模式，当外部审计方介入时，这种减少是可观测的。"</em> 验证方式：将每个 Plan 的产出——修改的文件、AILOG、TEMPLATE 下的 Plan 文档——提交给三位未参与开发的审计方（Copilot、Gemini，以及 Claude 的批判性分析），比较汇总评分。</p>
<p><code>thesis-validation.md</code> 报告的 PLAN-05 数据（第一个使用 v2 格式的 Plan，也是唯一一个 M 规模的 Plan）十分清晰：</p>
<blockquote>
<p><em>"5 个周期中最佳的综合审计方校准度（Copilot 9.25，Gemini 9.5）。累积假设已验证：TEMPLATE v2 + 丰富的 AILOG 减少了外部审计的歧义面。"</em></p>
</blockquote>
<p>两个异构模型，不同的指令，在这批 Plan 中规模最大的 Plan 上收敛于 9.3/10——不是最小的那个。这是信号，不是噪声。在 v3 的 <code>check-plan-drift.sh</code> 运行时，这一结果得到了复现：</p>
<blockquote>
<p><em>"PLAN-05（含已知漂移）：脚本报告 3 个遗漏文件：<code>evaluator_test.go</code>（F4）、<code>repository.go</code>（F1/R6）、<code>statuscenter/service.go</code>（F5）—— 正好是 Copilot+Gemini 在审计中捕获的 3 处漂移。零误报。"</em></p>
</blockquote>
<p>自动化脚本与两位外部审计方在哪些 Plan 文件未完成这件事上完全一致。这不是在论证脚本有多智能——它不过是 bash 加 <em>grep</em>，再简单不过。这是在论证 Plan 格式一旦正式化，就能让<em>任何检查方</em>（人类、智能体、脚本）看到相同的内容。结构化规范不需要智能就能被审计：它需要的是清晰。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-实验未能证明的事">5. 实验<em>未能</em>证明的事<a href="https://straymark.dev/zh-CN/blog/six-plans-and-the-rename-to-charter#5-%E5%AE%9E%E9%AA%8C%E6%9C%AA%E8%83%BD%E8%AF%81%E6%98%8E%E7%9A%84%E4%BA%8B" class="hash-link" aria-label="5-实验未能证明的事的直接链接" title="5-实验未能证明的事的直接链接" translate="no">​</a></h2>
<p>这一节是我最希望留存在记录中的，因为它考验博客是否愿意诚实。</p>
<p><code>thesis-validation.md</code> 的明确设计目标是打破该论题，而非捍卫它。文档第一行写道：</p>
<blockquote>
<p><em>"本文档将用数据检验这一论题。它不是为了捍卫它，而是试图打破它。目标是：一位不认同该论题的读者，只需阅读本文，就能判断现有证据是支持、修正还是反驳它。"</em></p>
</blockquote>
<p>将论题的六个假设与证据对照后，最终结论如下：</p>








































<table><thead><tr><th>#</th><th>假设</th><th>结论</th></tr></thead><tbody><tr><td>1</td><td>Vibe coding 无法规模化</td><td>部分验证*（无对照组）*</td></tr><tr><td>2</td><td>结构化记录减少失效模式</td><td>已验证</td></tr><tr><td>3</td><td>合规性作为无额外工作的副产品</td><td>已验证*（待真实人工审计方测试）*</td></tr><tr><td>4</td><td>审批很少是非此即彼的</td><td>无证据——待多参与方项目验证</td></tr><tr><td>5</td><td>Stage 比 commit 更适合作为可追溯单元</td><td>已验证</td></tr><tr><td>6</td><td>就地签署优于事后重建</td><td>部分验证*（未测试加密签署）*</td></tr></tbody></table>
<p>三个假设完全验证，两个部分验证，一个<em>无证据</em>，零个被反驳。关于非二元审批的假设未经测试，因为 Sentinel 是单一所有者项目；要得出结论，需要一个有多位签署方的真实团队。加密签署——Sigstore、哈希链、仅追加日志的技术保证——未经演练，因为没有 Plan 涉及流程的那个部分。这在文档中作为明确的<em>空白</em>保留，而非含糊其辞的断言。</p>
<p>实验未能证明的事，比它证明了的更重要。如果我们发布六个绿灯，而非五个诚实的结论，这个博客就成了营销材料。避免这种情况的方式，是直言不讳：这里有我们没有证据的假设，单一操作者项目无法生成这些证据。其他人会生成；框架将随着这些数据的积累而成熟。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-为什么-plan-变成了-charter">6. 为什么 Plan 变成了 Charter<a href="https://straymark.dev/zh-CN/blog/six-plans-and-the-rename-to-charter#6-%E4%B8%BA%E4%BB%80%E4%B9%88-plan-%E5%8F%98%E6%88%90%E4%BA%86-charter" class="hash-link" aria-label="6. 为什么 Plan 变成了 Charter的直接链接" title="6. 为什么 Plan 变成了 Charter的直接链接" translate="no">​</a></h2>
<p>4 月 30 日的更名不是营销行为。原因字面上写在 <code>WHAT-IS-A-CHARTER.md</code> 中：</p>
<blockquote>
<p><em>"GitHub SpecKit 已经用'plan'这个词（<code>/speckit.plan</code>、<code>plan.md</code>）指代另一种制品，这一名称冲突在同时使用两套流程的采用者文档中造成了摩擦。"</em></p>
</blockquote>
<p>SpecKit 已经有了 <code>plan.md</code>，StrayMark 也有。在同时使用两者的采用者文档中，相邻段落里的 <em>plan</em> 指代着不同的东西。更名是为了消除这一冲突。</p>
<p>但在这个务实动机背后，有一个值得点明的细节。这两种制品<em>确实</em>不同，且区别是结构性的：</p>






























<table><thead><tr><th></th><th><strong>SpecKit <code>plan.md</code></strong></th><th><strong>StrayMark Charter</strong></th></tr></thead><tbody><tr><td>粒度</td><td>完整功能（数周，多个用户故事）</td><td>有边界的工作单元（数小时到数天，一个分支）</td></tr><tr><td>主要内容</td><td>技术栈、依赖、项目结构、constitution 检查门</td><td>待修改的具体文件、验证命令、<code>R&lt;N&gt;</code> 风险</td></tr><tr><td>验证方式</td><td>Constitution Check（<em>事前</em>门控）</td><td>漂移检查 + 外部审计（<em>事后</em>门控）</td></tr><tr><td>优化目标</td><td><em>前置清晰度</em></td><td><em>事后可追溯性</em></td></tr></tbody></table>
<p>SpecKit 所称的 <em>plan</em> 更像是带有架构骨架的 ADR。StrayMark 所称的 <em>Charter</em> 更像是带有合约验证和审计锚点的任务卡片。它们是互补制品，而非竞争关系；<code>SPECKIT-CHARTER-BRIDGE.md</code> 在 5 月对此做了明确说明。但在讨论互补性之前，名称冲突必须先得到解决。</p>
<p>我认为值得重点强调的是这一伴随决策：Sentinel 的历史记录——AILOG、遥测数据、<code>check-plan-drift.sh</code>、<code>sentinel/docs/plans/</code> 文件夹——保留了 <em>Plan</em> 这个名称。<code>WHAT-IS-A-CHARTER.md</code> 本身这样论述：</p>
<blockquote>
<p><em>"Sentinel 的历史记录（Plan 01–06、<code>sentinel/docs/plans/</code>、<code>sentinel/scripts/check-plan-drift.sh</code>、AILOG 与 YAML 遥测数据 <code>PLAN-NN.telemetry.yaml</code>）保留原始名称——它们是特定时刻的实证记录，改写历史会伪造这份记录。"</em></p>
</blockquote>
<p><em>改写历史会伪造记录。</em> 对我而言，这句话才是这次更名的真正核心。框架存在的目的是保存溯源记录，而非改写它。当制品更名时，变化是前瞻性的：从 4 月 30 日起，新创建的是 Charter。旧的、实验期间的那些，仍然是 Plan。这种不一致是刻意为之的，而这种不一致<em>本身就是</em>证据。</p>
<p>4 月 30 日更名的另一个伴侣——遥测提案——在同一天出现，事后看来原因显而易见：如果这个涌现出来的模式值得一个新名称，它也值得结构化的度量。<code>charter-telemetry.schema.v0.json</code> 架构，包含投入时间、检测到的漂移、规模、领域等字段，是今天用于机器化度量的工具，而 Sentinel 在 4 月时还在用手写 YAML 进行度量。Plan 被更名为 Charter 的同一天，我们决定开始计数 Charter。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/six-plans-and-the-rename-to-charter#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>从这个过程中，我提炼出四点：</p>
<ol>
<li class="">
<p><strong>实验的设计目标是打破，而非确认。</strong> <em>"它不是为了捍卫，而是试图打破"</em> —— 这字面上就是验证论题的文档的职责定义。任何值得尊重的技术博客都必须能承受这种框架。</p>
</li>
<li class="">
<p><strong>六个设计中的 Plan，四天内执行了五个。</strong> 那个未能执行的（规模最大，包含七个功能的路线图）偶然间成为了工作单元能承载多大规模的第一条证据。今天的 Charter 是通过合约约定有边界的；在 4 月，边界是偶然形成的。</p>
</li>
<li class="">
<p><strong>格式在实验内部自我演进：从习惯，到模板，到脚本。</strong> 五个 Plan 产生了三个版本的格式。v1 → v2 → v3 的曲线，是任何有效标准化的曲线——只是被压缩了。</p>
</li>
<li class="">
<p><strong>更名源于务实，而非营销。历史保存源于原则。</strong> <em>Plan</em> 在 Sentinel 中依然叫 <em>Plan</em>，因为当时它就叫那个名字。<em>Charter</em> 从 4 月 30 日起开始使用。框架不改写自己的存档——这正是它值得追溯的原因。</p>
</li>
</ol>
<p>下一篇将介绍两个相近的里程碑，它们将全新的能力定性地编码为正式规范——<em>Charter 作为一等实体</em>（PR #65，fw-4.4.0）和<em>外部多模型审计周期</em>（<code>audit-skills-design</code>、<code>audit-cli-flow</code>，发布版本 fw-4.7 → fw-4.9）。那就是 Sentinel 实验成为 CLI 功能的地方。</p>
<hr>
<p><em>参考锚点：提案 <a href="https://github.com/StrangeDaysTech/straymark/blob/main/docs/decisions/proposals/2026-04-30-thesis-validation.md" target="_blank" rel="noopener noreferrer" class=""><code>thesis-validation.md</code></a> 与 <a href="https://github.com/StrangeDaysTech/straymark/blob/main/docs/decisions/proposals/2026-04-30-charter-telemetry.md" target="_blank" rel="noopener noreferrer" class=""><code>charter-telemetry.md</code></a>（均为 2026-04-30）。Charter 的规范定义：<a href="https://github.com/StrangeDaysTech/straymark/blob/main/docs/contributors/WHAT-IS-A-CHARTER.md" target="_blank" rel="noopener noreferrer" class=""><code>WHAT-IS-A-CHARTER.md</code></a>。运行时架构：<code>dist/.straymark/schemas/charter-telemetry.schema.v0.json</code>。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="sentinel" term="sentinel"/>
        <category label="charters" term="charters"/>
        <category label="methodology" term="methodology"/>
        <category label="governance" term="governance"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[探索框架]]></title>
        <id>https://straymark.dev/zh-CN/blog/exploring-the-framework</id>
        <link href="https://straymark.dev/zh-CN/blog/exploring-the-framework"/>
        <updated>2026-04-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[到 2026 年 4 月，框架已经有了约 50 个治理之下的 Markdown 文件，它们已经变得不透明。`straymark explore` —— 一个把整个仓库渲染成可导航界面的终端 UI（TUI）—— 在它意外暴露 Post 8 异常值的三周前诞生。新工具会在无意之间制造出可见性。]]></summary>
        <content type="html"><![CDATA[<p><em>到 2026 年 4 月，框架已经有了约 50 个治理之下的 Markdown 文件，它们已经变得不透明。<code>straymark explore</code> —— 一个把整个仓库渲染成可导航界面的 TUI —— 在它意外暴露 Post 8 异常值的三周前诞生。新工具会在无意之间制造出可见性。</em></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-两周后揭露异常值的工具">1. 两周后揭露异常值的工具<a href="https://straymark.dev/zh-CN/blog/exploring-the-framework#1-%E4%B8%A4%E5%91%A8%E5%90%8E%E6%8F%AD%E9%9C%B2%E5%BC%82%E5%B8%B8%E5%80%BC%E7%9A%84%E5%B7%A5%E5%85%B7" class="hash-link" aria-label="1. 两周后揭露异常值的工具的直接链接" title="1. 两周后揭露异常值的工具的直接链接" translate="no">​</a></h2>
<p>本博客 Post 8 记录了一个具体事件：发现了框架中唯一一个正典语言为西班牙语的文件。操作者并非通过系统性检查找到它；而是在审计准备过程中运行了 <code>straymark explore --lang es</code>，在视觉上注意到有一个模板相对其他文件是反向书写的。那篇文章中有这样一句话：</p>
<blockquote>
<p><em>"框架的一个新界面（TUI）将所有文件同时呈现，不一致之处变得显而易见——就像整理书架时，突然发现有一本书脊背朝外。"</em></p>
</blockquote>
<p>本文介绍的就是这个工具。<code>straymark explore</code> 这个 TUI 几乎在它所揭露的发现之前三周就已诞生——于 2026 年 4 月 25 日随 <code>cli-3.4.0</code> 发布——并在此后四个版本中逐步完善，所有版本均在 48 小时内完成。这不是一篇关于代码的文章。它是关于一件具体工具的文章，印证了博客此前已命名的一个论点：新工具会在无意之间制造出可见性。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-为什么在-markdown-框架里构建-tui">2. 为什么在 Markdown 框架里构建 TUI<a href="https://straymark.dev/zh-CN/blog/exploring-the-framework#2-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%9C%A8-markdown-%E6%A1%86%E6%9E%B6%E9%87%8C%E6%9E%84%E5%BB%BA-tui" class="hash-link" aria-label="2. 为什么在 Markdown 框架里构建 TUI的直接链接" title="2. 为什么在 Markdown 框架里构建 TUI的直接链接" translate="no">​</a></h2>
<p>到 2026 年 4 月下旬，框架已经拥有约五十个受治理的 Markdown 文件——<code>PRINCIPLES.md</code>、<code>AGENT-RULES.md</code>、<code>DOCUMENTATION-POLICY.md</code>、<code>QUICK-REFERENCE.md</code>、<code>SPECKIT-CHARTER-BRIDGE.md</code>，加上 <code>dist/.straymark/templates/</code> 中的模板、审计提示，以及每个文件的三语版本（<code>i18n/es/</code>、<code>i18n/zh-CN/</code>）。它们都在磁盘上，都可以用 <code>find</code> 和 <code>cat</code> 浏览，完全可访问。</p>
<p>但同时，也完全不可见。对于第一次克隆框架的使用者，第一个问题不是"规则 N 在哪里？"，而是"这里有什么？"。递归 <code>ls</code> 无法回答这个问题——它只会让人不知所措。<code>grep</code> 需要知道要搜索什么。编辑器一次只打开一个文件。框架已经达到了这样的规模：它自身的表面已经变得不透明。</p>
<p><code>straymark explore</code> 正是为了解决这个问题而诞生的：一个可导航界面，能在三秒内回答"这里有什么？"。它不是 IDE，不是 Read-the-Docs 式系统，也不是静态网站。它是一个 TUI——终端用户界面——运行在使用者的仓库上，对框架的正典文件建立索引，按类别分组展示，并在终端内使用带颜色的 Markdown 查看器让你阅读文件。</p>
<p>构建 TUI 而非 Web 应用的决定，与博客此前论述的两点相符。第一，Post 4 的原则 #10——"StrayMark 不是 LLM 网关"——可以推广为"StrayMark 不是任何已经做了某件事的东西"。我们不与 MkDocs 或 Material for MkDocs 竞争。第二，同一 Post 4 中的 A1 决策：纯编排。TUI 不需要服务器，不需要构建步骤，不需要网络连接：它运行在框架已经存在的使用者仓库上。这是能解决问题的最朴素的一块拼图。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-cli-340-落地的内容">3. <code>cli-3.4.0</code> 落地的内容<a href="https://straymark.dev/zh-CN/blog/exploring-the-framework#3-cli-340-%E8%90%BD%E5%9C%B0%E7%9A%84%E5%86%85%E5%AE%B9" class="hash-link" aria-label="3-cli-340-落地的内容的直接链接" title="3-cli-340-落地的内容的直接链接" translate="no">​</a></h2>
<p>4 月 25 日的 <a href="https://github.com/StrangeDaysTech/straymark/pull/57" target="_blank" rel="noopener noreferrer" class="">PR #57</a>，以 <code>cli-3.4.0</code> 合并，做了三件具体的事：</p>
<ul>
<li class="">引入了 <code>straymark explore</code> 命令。当终端宽度达到 100 列或以上时，采用双面板布局：左侧导航（30%），右侧文档（70%）。较窄的终端折叠为单面板。底部状态栏显示相关快捷键。</li>
<li class="">将框架的正典文件索引为可导航的层级结构：治理文档（<code>AGENT-RULES.md</code>、<code>DOCUMENTATION-POLICY.md</code> 等）、模板（<code>dist/.straymark/templates/</code>）、审计提示，以及使用者目录（<code>docs/charters/</code>、<code>.straymark/07-ai-audit/</code> 等）。每个节点可用 <code>Enter</code> 展开。</li>
<li class="">接入了 i18n 解析器。<code>--lang &lt;code&gt;</code> 选项允许运行 <code>straymark explore --lang es</code>，并在翻译存在时获取西班牙语的所有治理文档。若无翻译，则静默回退到正典英文。提交的字面描述：</li>
</ul>
<blockquote>
<p><em>"通过 explore TUI 贯通 <code>language</code> 配置和新的 <code>--lang</code> 标志，当翻译存在时从 <code>i18n/&lt;lang&gt;/</code> 提供框架治理文档，否则静默回退到英文。"</em></p>
</blockquote>
<p>i18n 解析器部分比 TUI 本身更重要。在 <code>cli-3.4.0</code> 之前，辅助函数 <code>resolve_localized_path</code> 仅由 <code>straymark new</code> 使用（用于解析应以何种语言将模板注入使用者）。PR #57 将其提取到 <code>cli/src/utils.rs:146</code> 作为共享辅助函数，使得**<code>i18n/&lt;lang&gt;/</code> 覆盖层解析方式的单一定义**同时支撑 <code>new</code> 命令和 <code>explore</code> 命令。对使用者而言，行为保持可预期：如果你将某个模板翻译到 <code>i18n/es/</code>，TUI 展示它的方式与生成时使用它的方式一致。没有意外。</p>
<p>Markdown 渲染使用 <code>pulldown-cmark</code> 作为解析器，<code>ratatui</code> 组件负责绘制。语法着色、带边框的代码块、圆角边框的表格、按层级缩进的标题。不炫目，但可读性好。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-三十六小时内的四次精炼">4. 三十六小时内的四次精炼<a href="https://straymark.dev/zh-CN/blog/exploring-the-framework#4-%E4%B8%89%E5%8D%81%E5%85%AD%E5%B0%8F%E6%97%B6%E5%86%85%E7%9A%84%E5%9B%9B%E6%AC%A1%E7%B2%BE%E7%82%BC" class="hash-link" aria-label="4. 三十六小时内的四次精炼的直接链接" title="4. 三十六小时内的四次精炼的直接链接" translate="no">​</a></h2>
<p>4 月 25 日至 27 日之间发生的事，正是 Post 9 几个月后命名为流程属性的东西：当提案写得清晰，实现就是执行。四个 PR，在 36 小时内完成，将 TUI 从"能用"带到"做完"：</p>



































<table><thead><tr><th>PR</th><th>标签</th><th>时间</th><th>新增内容</th></tr></thead><tbody><tr><td><a href="https://github.com/StrangeDaysTech/straymark/pull/60" target="_blank" rel="noopener noreferrer" class="">#60</a></td><td>cli-3.5.0</td><td>4 月 25 日 22:36</td><td><strong>实时语言切换器。</strong> <code>L</code> 键在不退出 TUI 的情况下循环切换显示语言：<code>en → es → zh-CN → en</code>。索引就地重建。</td></tr><tr><td><a href="https://github.com/StrangeDaysTech/straymark/pull/61" target="_blank" rel="noopener noreferrer" class="">#61</a></td><td>cli-3.5.0（同一版本）</td><td>4 月 25 日 23:41</td><td><strong>OS 语言区域自动检测。</strong> 若使用者没有 <code>config.yml</code>，TUI 读取 <code>$LC_ALL</code> / <code>$LANG</code> 并将 POSIX 区域（如 <code>es_MX.UTF-8</code> → <code>es</code>）映射到最近支持的语言。</td></tr><tr><td><a href="https://github.com/StrangeDaysTech/straymark/pull/62" target="_blank" rel="noopener noreferrer" class="">#62</a></td><td>cli-3.5.1</td><td>4 月 26 日 00:07</td><td><strong>元数据面板已翻译。</strong> 25 个新的 i18n 条目用于标签和标题，加入了视觉填充以保持跨语言对齐。</td></tr><tr><td><a href="https://github.com/StrangeDaysTech/straymark/pull/63" target="_blank" rel="noopener noreferrer" class="">#63</a></td><td>cli-3.5.2</td><td>4 月 26 日 00:13</td><td><strong>快捷键清理。</strong> 移除了未记录的 vim 别名 <code>l</code> 和 <code>h</code>（小写 <code>l</code> 与 <code>L</code> 语言切换器冲突）。保留已记录的 <code>j/k/g/G/n/N</code>。</td></tr></tbody></table>
<p>这段弧线的速度重复了同一模式。Post 6 中记录的 fw-4.11.0 的五个 PR（品牌更名为 StrayMark）在四十三分钟内完成。Post 4 中记录的三个审计周期版本在一天内完成。TUI 的曲线类似：设计在 PR #57 中已确定，而精炼则是在操作者开始频繁使用该命令并注意到摩擦点时出现的。</p>
<p>值得记录的轶事是 <code>cli-3.5.2</code>：它移除的 <code>l</code> 和 <code>h</code> 键是 vim 别名——操作者在编辑器中靠肌肉记忆使用它们。但当大写 <code>L</code> 语言切换器几小时前落地时，小写 <code>l</code> 和大写 <code>L</code> 共享同一物理键，视觉上的过渡令人困惑。修复方法是移除未记录的别名：完整字母拼写（<code>j/k/g/G</code>）保留；复制功能的缩写消失。这是一个小的 UX 细节，说明 TUI 决策是针对真实使用调整的，而非 UX 理论。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-tui-两周后揭露的内容">5. TUI 两周后揭露的内容<a href="https://straymark.dev/zh-CN/blog/exploring-the-framework#5-tui-%E4%B8%A4%E5%91%A8%E5%90%8E%E6%8F%AD%E9%9C%B2%E7%9A%84%E5%86%85%E5%AE%B9" class="hash-link" aria-label="5. TUI 两周后揭露的内容的直接链接" title="5. TUI 两周后揭露的内容的直接链接" translate="no">​</a></h2>
<p>此处，文章从另一端接续了 Post 8 的线索。<code>cli-3.4.0</code> 给框架增加的不仅仅是一个导航命令；它是一个<strong>同时可视化的界面</strong>。也就是说：在 TUI 出现之前，框架的文件存在，但只能逐一阅读。有了 TUI 之后，它们可以被一次性看到，分组、标记，以相同的呈现模式。</p>
<p>这改变了操作者能注意到的事情。</p>
<p>5 月 12 日，<code>cli-3.4.0</code> 合并两周半后，操作者在外部审计周期前运行了 <code>straymark explore --lang es</code>。他们打开了"审计提示"组，发现了一件存在已久的事：根目录下的 <code>audit-prompt.md</code> 是用<em>西班牙语</em>写的，而其他所有正典框架文件都以英文作为根目录版本，以 <code>i18n/es/</code> 覆盖层作为西班牙语版本。一个文件，相对于约定是反向的。</p>
<p>这个细节不是通过系统性审查发现的。它是通过视觉对比发现的。这正是 Post 8 第 3 节编纂为论点的内容：</p>
<blockquote>
<p><em>"新工具制造可见性。"</em></p>
</blockquote>
<p>TUI 没有造成这个异常值——它自第一次将 Sentinel 技能 <code>plan-audit</code> 移植到正典框架的提交以来就存在（Post 3 对此有记录）。TUI 也没有寻找异常值；它不是一个不一致性检测工具。它所做的只是把每个文件放在同一屏幕上，以相同的呈现模式，让人眼注意到从一开始就存在的东西。值得记录的结论是结构性的：任何超过五十个正典文件的框架都需要一个同时可视化的界面，不是因为这个界面本身在教学上重要，而是因为没有它，仓库的某些规律性就变得无法观察。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-值得记录的细小技术决策">6. 值得记录的细小技术决策<a href="https://straymark.dev/zh-CN/blog/exploring-the-framework#6-%E5%80%BC%E5%BE%97%E8%AE%B0%E5%BD%95%E7%9A%84%E7%BB%86%E5%B0%8F%E6%8A%80%E6%9C%AF%E5%86%B3%E7%AD%96" class="hash-link" aria-label="6. 值得记录的细小技术决策的直接链接" title="6. 值得记录的细小技术决策的直接链接" translate="no">​</a></h2>
<p>三个值得记录的 TUI 设计细节，均与博客已命名的框架原则相符：</p>
<p><strong>特性标志 <code>tui</code>。</strong> CLI 的 <code>Cargo.toml</code> 将 TUI 声明为可选依赖（<code>ratatui</code> + <code>crossterm</code> + <code>pulldown-cmark</code> 置于默认启用的 <code>tui</code> 标志之后）。只需要 <code>init</code>、<code>update</code>、<code>validate</code> 的使用者可以通过 <code>cargo build --no-default-features</code> 构建不含 TUI 的二进制文件。这关闭了一个在框架中反复出现的子架构决策：默认给使用者提供价值，但当 TUI 对其流程毫无意义时，让他们可以选择更轻量的二进制文件。</p>
<p><strong>文档懒加载。</strong> <code>DocIndex</code> 维护一个 <code>HashMap&lt;PathBuf, Document&gt;</code>，它在每个节点被打开时才填充，而非在启动时。对于拥有一百个治理文件的仓库，这意味着 <code>straymark explore</code> 在 200ms 内启动，只在操作者按下 <code>Enter</code> 时才读取磁盘。这个决策在技术上微不足道；真正不微不足道的是它是有意识做出的。TUI 不与 IDE 竞争；它与 <code>cat</code> 竞争。它必须感觉同样即时。</p>
<p><strong>超链接的历史栈。</strong> 当一个框架文档提及另一个（例如 <code>AGENT-RULES.md §3</code> 引用 <code>DOCUMENTATION-POLICY.md §6</code>），TUI 允许你通过点击跳转到被引用的文档，并用 <code>Esc</code> 返回。内部有一个导航栈，可以恢复之前文档的滚动位置。这一功能将阅读框架从"我打开一个文件，关闭它，再打开另一个"变成了"我在文档之间跟随一场对话而不失去线索"。框架今天所拥有的交叉引用数量——原则、智能体规则、模式、模板之间——使这种导航在结构上重要，而不只是装饰性的。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/exploring-the-framework#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>从这个过程中，我得出了四个结论：</p>
<ol>
<li class="">
<p><strong>超过一定规模的框架需要一个同时可视化的界面。</strong> 这不是人体工学问题；这是仓库某些规律性得以被观察的条件。Post 8 记录了后果；本文记录了工具。</p>
</li>
<li class="">
<p><strong>TUI 不会造成发现；它使发现成为可能。</strong> 审计提示异常值自上一年 5 月就已存在。4 月唯一改变的是，一个屏幕出现了，在那里它可以与同类文件一起被看到。工具对内容没有意见；它改变的是人眼能注意到规律性的条件。</p>
</li>
<li class="">
<p><strong>当设计清晰时，实现是小时，而非冲刺。</strong> 三十六小时内的五个 PR——语言感知、实时切换器、OS 检测、面板 i18n、快捷键清理——之所以可能，是因为设计在 PR #57 中已关闭。这与 Post 4、6 和 9 是同一模式。</p>
</li>
<li class="">
<p><strong>最有趣的技术决策可以是一个编译时标志。</strong> <code>tui</code> 特性标志明确声明 TUI 是可选的，框架在没有它的情况下仍然可以工作，使用者可以自行决定。这种架构上的谦逊，是一个假设使用者工作方式的 CLI 与一个在能发挥作用的地方提供自身的 CLI 之间的区别。</p>
</li>
</ol>
<p>本文使本博客覆盖了第一批留为明确技术债的 <code>H-14</code> 里程碑。<code>PLAN-INVESTIGACION.md §1.43-55</code> 中记录的其余候选里程碑——<code>AGENTS.md</code> 通用注入、完整 EN/ES/zh-CN i18n 覆盖、<code>straymark validate</code> 作为正式层、CLA 助手——仍可作为未来批次的主题。没有承诺的节奏，但已记录在案。</p>
<hr>
<p><em>锚点：PR <a href="https://github.com/StrangeDaysTech/straymark/pull/57" target="_blank" rel="noopener noreferrer" class="">#57</a> — <code>cli-3.4.0</code>（语言感知 explore，2026 年 4 月 25 日）。PR <a href="https://github.com/StrangeDaysTech/straymark/pull/60" target="_blank" rel="noopener noreferrer" class="">#60</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/61" target="_blank" rel="noopener noreferrer" class="">#61</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/62" target="_blank" rel="noopener noreferrer" class="">#62</a> · <a href="https://github.com/StrangeDaysTech/straymark/pull/63" target="_blank" rel="noopener noreferrer" class="">#63</a> — 精炼版本 <code>cli-3.5.0</code> 至 <code>cli-3.5.2</code>（2026 年 4 月 25-26 日）。代码：<code>cli/src/tui/</code>（15 个文件）。使用者文档：<code>docs/adopters/CLI-REFERENCE.md</code> §<code>straymark explore</code>。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="tui" term="tui"/>
        <category label="cli" term="cli"/>
        <category label="i18n" term="i18n"/>
        <category label="structural-visibility" term="structural-visibility"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[四个月里的四个名字]]></title>
        <id>https://straymark.dev/zh-CN/blog/four-names-in-four-months</id>
        <link href="https://straymark.dev/zh-CN/blog/four-names-in-four-months"/>
        <updated>2026-04-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[寻找概念，而非寻找名字]]></summary>
        <content type="html"><![CDATA[<p><em>Chronicle → Monimen → DevTrail → Strange Days Tech → StrayMark。四个月、四次改名 —— 一月里仓促的三次，五月里有纪律的一次。理念没有动；名字动了。</em></p>
<!-- -->
<blockquote>
<p><strong>编者按</strong>：这是本博客第一篇以追溯方式发布的文章。frontmatter 中的 <code>date</code> 对应的是文章所叙述的时间弧线中的某一时刻，而非实际撰写当天。整个博客都以这种方式构建——从后往前，从触发我提笔的那个事件开始（<code>emergent-observation-design</code>，2026 年 5 月 16 日）。假装情况不是这样，对任何人都没有帮助。</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-第二天">1. 第二天<a href="https://straymark.dev/zh-CN/blog/four-names-in-four-months#1-%E7%AC%AC%E4%BA%8C%E5%A4%A9" class="hash-link" aria-label="1. 第二天的直接链接" title="1. 第二天的直接链接" translate="no">​</a></h2>
<p>2026 年 1 月 28 日 17:29（中部时间），这条 commit 落入了仓库：</p>
<blockquote>
<p><code>chore: rebrand Chronicle to Monimen</code></p>
</blockquote>
<p>仅仅一天。确切地说，是项目<em>初始 commit</em> 后的二十九小时。框架刚刚存在，就已经在改名了。</p>
<p>我用过去时态来叙述，但这件事发生在三个月前。而在三个月前，又经历了两次改名之后，它依然还不叫 StrayMark。这个今天以<em>面向 AI 辅助软件开发的文档治理框架</em>自居的项目，有着一段简短、凌乱的早期历史，值得在继续前行之前把它说清楚。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-五个事件四个月">2. 五个事件，四个月<a href="https://straymark.dev/zh-CN/blog/four-names-in-four-months#2-%E4%BA%94%E4%B8%AA%E4%BA%8B%E4%BB%B6%E5%9B%9B%E4%B8%AA%E6%9C%88" class="hash-link" aria-label="2. 五个事件，四个月的直接链接" title="2. 五个事件，四个月的直接链接" translate="no">​</a></h2>









































<table><thead><tr><th>日期（UTC-6）</th><th>时间</th><th>事件</th><th>锚点</th></tr></thead><tbody><tr><td>2026 年 1 月 27 日</td><td>12:14</td><td><em>初始 commit</em>：<strong>Enigmora Chronicle Framework v1.0.0</strong></td><td><a href="https://github.com/StrangeDaysTech/straymark/commit/7c58b6d" target="_blank" rel="noopener noreferrer" class=""><code>7c58b6d</code></a></td></tr><tr><td>2026 年 1 月 28 日</td><td>17:29</td><td>改名：Chronicle → <strong>Monimen</strong></td><td><a href="https://github.com/StrangeDaysTech/straymark/commit/0b772bc" target="_blank" rel="noopener noreferrer" class=""><code>0b772bc</code></a></td></tr><tr><td>2026 年 1 月 29 日</td><td>19:30</td><td>改名：Monimen → <strong>DevTrail</strong>（<code>BREAKING CHANGE</code>）</td><td><a href="https://github.com/StrangeDaysTech/straymark/commit/25ab7a4" target="_blank" rel="noopener noreferrer" class=""><code>25ab7a4</code></a></td></tr><tr><td>2026 年 3 月 1 日</td><td>19:05</td><td>组织改名：Enigmora → <strong>Strange Days Tech</strong>，同时 Rust CLI 诞生</td><td><a href="https://github.com/StrangeDaysTech/straymark/commit/c7e9026" target="_blank" rel="noopener noreferrer" class=""><code>c7e9026</code></a></td></tr><tr><td>2026 年 5 月 8/9 日</td><td>—</td><td>DevTrail → <strong>StrayMark</strong>（ADR + 五个 PR 的弧线）</td><td>ADR <code>2026-05-08-001</code>，PR #114-#118</td></tr></tbody></table>
<p>三天内三次改名。然后是长达一个月的停顿。然后是捆绑 Rust CLI 的组织品牌迁移。再然后又是两个月的停顿。最后是 StrayMark，这次有一份公开的 ADR 作为决策的锚点。</p>
<p>有一个细节值得注意：2026 年 1 月 28 日的 commit（Chronicle → Monimen）没有标注 <code>BREAKING CHANGE</code>；而 1 月 29 日的那次（Monimen → DevTrail）标注了。commit 正文写道：<em>"BREAKING CHANGE: Complete rebrand from Monimen to DevTrail"</em>。差别虽小，却说明问题：第二次改名显得更具决定性。它确实如此——坚持了整整两个月。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-不曾移动的东西">3. 不曾移动的东西<a href="https://straymark.dev/zh-CN/blog/four-names-in-four-months#3-%E4%B8%8D%E6%9B%BE%E7%A7%BB%E5%8A%A8%E7%9A%84%E4%B8%9C%E8%A5%BF" class="hash-link" aria-label="3. 不曾移动的东西的直接链接" title="3. 不曾移动的东西的直接链接" translate="no">​</a></h2>
<p>这是我最感兴趣的一节。项目在四个月内四次改名；理念却纹丝未动。</p>
<p><em>初始 commit</em> 的 README —— 也就是项目还叫 <em>Enigmora Chronicle Framework</em> 时仓库里的那份 —— 以这行字开头：</p>
<blockquote>
<p><strong>Documentation Governance for AI-Assisted Software Development</strong></p>
</blockquote>
<p>这也是今天 StrayMark README 的第一行。四个名字，一句标语。</p>
<p>稍往下，同一份 v1.0.0 README 在一个引用块中明确陈述了项目的核心主张：</p>
<blockquote>
<p><em>"No significant change without a documented trace."</em></p>
</blockquote>
<p>这句话今天是框架的第一原则。它存在于 <code>PRINCIPLES.md §1 — Total Traceability</code> 中。翻译成西班牙语时措辞略有调整，但主张的分量完整保留：没有文档记录的重大变更，就不应发生。</p>
<p>v1.0.0 README 列出的八种文档类型 —— REQ、ADR、TES、INC、TDE、AILOG、AIDEC、ETH —— 全部留存至今。后来新增了一些（MCARD、SBOM、SEC、DPIA），但没有一个取代了原有的类型。一月份的分类体系经受住了时间的考验。</p>
<p><strong>名字动了四次；理念没有动。</strong> 那四个月里改变的不是<em>内容</em>，而是贴在它上面的标签。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-年份打错的-aidec">4. 年份打错的 AIDEC<a href="https://straymark.dev/zh-CN/blog/four-names-in-four-months#4-%E5%B9%B4%E4%BB%BD%E6%89%93%E9%94%99%E7%9A%84-aidec" class="hash-link" aria-label="4. 年份打错的 AIDEC的直接链接" title="4. 年份打错的 AIDEC的直接链接" translate="no">​</a></h2>
<p>有一个小细节，我总是会回想起来。</p>
<p>项目的第一份结构化文档 —— 第一份 AIDEC，即框架自身分类体系中用于记录借助智能体协助所做决策的文档 —— 是 <code>AIDEC-2025-01-27-001-i18n-strategy.md</code>。它存在于 commit <a href="https://github.com/StrangeDaysTech/straymark/commit/7b7193e" target="_blank" rel="noopener noreferrer" class=""><code>7b7193e</code></a>，添加于 2026 年 1 月 27 日 18:01，也就是<em>初始 commit</em> 后六小时。</p>
<p>ID 写的是 <strong>2025</strong>。commit 日期是 <strong>2026</strong>。</p>
<p>正是这份确立了 <code>[TYPE]-[YYYY-MM-DD]-[NNN]</code> 标识符规范的文档，在自己的年份上打了个错字。框架从不完美中起步，没有人及时修正它。任何克隆仓库后仔细查看这个 ID 的人都会注意到 —— 同时也会看到引入它的 commit 日期是正确的。这是我掌握的最好证据，证明审计纪律是后来才会养成的东西，并不会随仓库一起交付。</p>
<p>但比这个错字更有意思的，是那份 AIDEC 做出的决策。2026 年 1 月，José 借助 Claude Opus 4.5 的帮助提出了这个问题：如何对一个为人类而存在、但配置文件由智能体读取的框架进行国际化？该 AIDEC 的论证部分给出了答案：</p>
<blockquote>
<p><em>"AI agents (Claude, Gemini, Copilot, Cursor) process instructions equally well in any language, so translating their config files provides no functional benefit."</em></p>
</blockquote>
<p>那份文档做出的决策 —— 翻译人类阅读的内容（文档、模板），保留智能体读取的内容（CLAUDE.md、GEMINI.md、.cursorrules）为英文 —— 是一个早期直觉：框架有两类截然不同的受众。这个博客本身今天也印证了这一直觉：面向阅读的人类提供中西双语，而编排智能体的技能和提示词则仅保留英文。三个月后，这仍然是正确的选择。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-cli-的诞生以及那个缺失的版本号">5. CLI 的诞生，以及那个缺失的版本号<a href="https://straymark.dev/zh-CN/blog/four-names-in-four-months#5-cli-%E7%9A%84%E8%AF%9E%E7%94%9F%E4%BB%A5%E5%8F%8A%E9%82%A3%E4%B8%AA%E7%BC%BA%E5%A4%B1%E7%9A%84%E7%89%88%E6%9C%AC%E5%8F%B7" class="hash-link" aria-label="5. CLI 的诞生，以及那个缺失的版本号的直接链接" title="5. CLI 的诞生，以及那个缺失的版本号的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5a-项目成为软件的那一天">5a. 项目成为软件的那一天<a href="https://straymark.dev/zh-CN/blog/four-names-in-four-months#5a-%E9%A1%B9%E7%9B%AE%E6%88%90%E4%B8%BA%E8%BD%AF%E4%BB%B6%E7%9A%84%E9%82%A3%E4%B8%80%E5%A4%A9" class="hash-link" aria-label="5a. 项目成为软件的那一天的直接链接" title="5a. 项目成为软件的那一天的直接链接" translate="no">​</a></h3>
<p>2026 年 3 月 1 日 19:05，commit <a href="https://github.com/StrangeDaysTech/straymark/commit/c7e9026" target="_blank" rel="noopener noreferrer" class=""><code>c7e9026</code></a> 落入仓库：</p>
<blockquote>
<p><em>"feat: rebrand to Strange Days Tech, add CLI scaffolder, restructure repo"</em></p>
</blockquote>
<p>在那天之前，这个项目只是文字。是模板、技能、治理规则，以及一堆 Markdown —— 依赖操作者（我本人）手工将其复制到想要使用它的仓库中。有一个叫 <code>copy-devtrail.sh</code> 的 bash 脚本。那是个玩具。</p>
<p>那次 3 月 1 日的 commit 引入了 <code>cli/Cargo.toml</code> 和 <code>cli/src/main.rs</code>。第一个 CLI 叫做 <code>devtrail-cli</code>，版本 <code>2.0.0</code>，有三个命令：</p>
<div class="language-rust codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-rust codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">enum</span><span class="token plain"> </span><span class="token type-definition class-name">Commands</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name">Init</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name">Update</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name">Remove</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> full</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">bool</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>init</code>、<code>update</code>、<code>remove</code>。三个操作。其余的都是后来的事：<code>status</code>、<code>repair</code>、<code>validate</code>、<code>new</code>、<code>compliance</code>、<code>metrics</code>、<code>analyze</code>、<code>audit</code>、<code>explore</code>。但最初的三个依然还在，几乎原封未动。</p>
<p>那次 commit 的意义不在于那三个命令，而在于：从那天起，这个项目不再是一堆存活于自身仓库里的文档，而成为了一个可以安装到其他仓库中的可执行工具。<em>"文档治理项目"</em> 与 <em>"带工具链的框架"</em> 之间的边界，在那个三月的周日被跨越了。这比之前任何一次改名都更为重要。</p>
<p>那天也是项目迁移 GitHub 账号的日子：从 <code>enigmora/devtrail</code> 到 <code>StrangeDaysTech/devtrail</code>。组织在同一个 commit 里将自身从 Enigmora 改名为 Strange Days Tech S.A.S.，同时也发布了 CLI。两个层面的身份认同同步迁移：产品的（Monimen 到 DevTrail）发生在一月；公司的，发生在三月。两个不同层次的身份，按照各自的节拍移动。</p>
<p>（一个对存档准确性重要的补充说明：智能体联合署名 —— 出现在 commit 中的 <code>Co-Authored-By: Claude Opus X.Y</code> 行 —— 并不始于三月。2026 年 1 月 27 日的<em>初始 commit</em> 就已经由 Claude Opus 4.5 联署。AI 联合署名的透明化是从第一行代码起就确立的规则。3 月 1 日改变的不是这个实践，而是规模：CLI 是联合署名第一次产出<em>可执行代码</em>，而非文档。）</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5b-那个缺失的版本号">5b. 那个缺失的版本号<a href="https://straymark.dev/zh-CN/blog/four-names-in-four-months#5b-%E9%82%A3%E4%B8%AA%E7%BC%BA%E5%A4%B1%E7%9A%84%E7%89%88%E6%9C%AC%E5%8F%B7" class="hash-link" aria-label="5b. 那个缺失的版本号的直接链接" title="5b. 那个缺失的版本号的直接链接" translate="no">​</a></h3>
<p>框架版本编号有一个细节，直接看 <code>git tag</code> 就能一目了然：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">fw-2.0.0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">fw-2.1.0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">fw-4.0.0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">fw-4.1.0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">...</span><br></div></code></pre></div></div>
<p>没有 <code>fw-3.x.x</code>。项目刻意从 2 跳到了 4。</p>
<p>这个跳跃对应的是 commit <a href="https://github.com/StrangeDaysTech/straymark/commit/21e03b2" target="_blank" rel="noopener noreferrer" class=""><code>21e03b2</code></a>，日期为 2026 年 3 月 27 日，其正文如此描述这次变更：</p>
<blockquote>
<p><em>"Reposition DevTrail from 'documentation helper' to 'ISO 42001-aligned AI governance platform' across all user-facing docs. Lead with regulatory urgency (EU AI Act Aug 2026) and compliance value proposition."</em></p>
</blockquote>
<p>在三月之前，DevTrail 将自己定位为一个<em>文档辅助工具</em>；从那次 commit 起，它将自己定位为一个<em>符合 ISO 42001 的 AI 治理平台</em>。版本号的跳跃（2 → 4，跳过 3）标志着这一主张转变的幅度。这是一个有意识的选择：仅仅一个大版本的跃升，不足以体现这种差异。</p>
<p>而这正是早期历史开始从回顾的角度产生意义的地方。那个自一月起就存在于仓库中的直觉 —— 一个健壮、规范、可对齐标准的记录体系的构想 —— 直到三月才被明确命名。我们将在下一节谈到第二个名字时，更清楚地看到这一点。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-关于那些名字">6. 关于那些名字<a href="https://straymark.dev/zh-CN/blog/four-names-in-four-months#6-%E5%85%B3%E4%BA%8E%E9%82%A3%E4%BA%9B%E5%90%8D%E5%AD%97" class="hash-link" aria-label="6. 关于那些名字的直接链接" title="6. 关于那些名字的直接链接" translate="no">​</a></h2>
<p>四次改名中，三次没有 ADR。理由只存活在 commit 信息里，有时甚至连那里也找不到：commit <code>chore: rebrand Chronicle to Monimen</code> 没有说明为什么是 Monimen；紧接着的那条 <code>chore: rebrand Monimen to DevTrail</code> 也没有说明为什么是 DevTrail。将 ADR 作为有纪律的制品的实践，是后来才形成的；在一月，名字随着一个 commit 而改变，理由则随着产生它的那场对话一同消散。</p>
<p>但部分词源学是可以追溯的。</p>
<ul>
<li class="">
<p><strong>Chronicle</strong>（1 月 27 日）。历史记录。航海日志。这个名字不言自明：你用一部<em>chronicle</em>（编年史）所做的事，就是按顺序写下发生了什么，以便日后查阅。没有 ADR，但这个词自带含义。</p>
</li>
<li class="">
<p><strong>Monimen</strong>（1 月 28 日）。这是唯一一个我确实记得其直觉、且值得讲述的名字。<em>Monimen</em> 来自对 <em>Monumento</em>（纪念碑）的文字游戏。驱动这个建议的类比是：彼时已经隐约感知到的那些规范 —— AIDEC、AILOG，以及可对齐于新兴 ISO 42001 治理体系的一切 —— 代表着某种坚实而持久的东西。<em>纪念碑</em>是为了长存而竖立的；是一个社群中不可移动的参照点。这正是框架想要成为的。截短的后缀 —— <em>Monimen</em> 而非 <em>Monument</em> —— 是赋予这个词更敏捷、更具软件气质的尝试，少一些大理石的厚重感。它只存活了二十五小时。但那个直觉没有消亡：三月将被明确命名为<em>符合 ISO 42001 的 AI 治理平台</em>的东西，正是那同一冲动的成熟版本。Monimen 是一个有着正确主张、却说错了语言的名字。</p>
</li>
<li class="">
<p><strong>DevTrail</strong>（1 月 29 日）。开发者的足迹。比 Monimen 少了些庄重，多了些具体。那个 commit 标注了 <code>BREAKING CHANGE</code> —— 从五月的视角来看，颇具讽刺意味：前三次改名中最具决定性的那次，恰恰是在三个半月后被替换的那次。</p>
</li>
<li class="">
<p><strong>StrayMark</strong>（5 月 8-9 日）。那个标记，那个留下的痕迹。但这次品牌迁移值得单独成文：已经有了一份公开的 ADR（<code>2026-05-08-001</code>）、五个核心 PR 的弧线（#114-#118），以及 README 中的一份*"为什么叫 StrayMark？"*宣言，值得另行论述。在此，我只是将它作为那次终于有了文档纪律支撑的品牌迁移，画上句号。</p>
</li>
</ul>
<p>重点不在词源本身，而在于：每个名字都印证了一种关于这个产品是什么的独特直觉——<strong>日志</strong>（Chronicle）、<strong>规范支柱</strong>（Monimen）、<strong>开发者足迹</strong>（DevTrail）、<strong>残留痕迹</strong>（StrayMark）。四个名字是对这个概念的四种假设。当概念稳定下来，搜索才会终止 —— 而只有到那时，名字才能安定下来。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-结语">7. 结语<a href="https://straymark.dev/zh-CN/blog/four-names-in-four-months#7-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="7. 结语的直接链接" title="7. 结语的直接链接" translate="no">​</a></h2>
<p>我从这个过程中得出的结论，凝炼为四点：</p>
<ol>
<li class="">
<p><strong>我们不是在寻找名字，而是在寻找概念。</strong> 四次改名是探索的证据，而非优柔寡断的证据。概念在名字的轮替中不断清晰。</p>
</li>
<li class="">
<p><strong>四次改名中，三次没有 ADR。</strong> 将 ADR 作为有纪律的制品的实践是后来才形成的。一月时还没有这个习惯。这不是债务 —— 这是对节奏的诚实。治理习惯是想要记录已然发生之事的产物，而非启动的前提。</p>
</li>
<li class="">
<p><strong>这个项目在 2026 年 3 月 1 日成为了软件。在此之前，它只是文字。</strong> Rust CLI 是那道边界。任何关于 StrayMark <em>作为框架</em>的讨论，都必须承认那次 commit 之前和之后的区别。</p>
</li>
<li class="">
<p><strong>大概不会再有下一次改名了。</strong> 但这个博客不作此承诺。这个项目对自身的可变性保持诚实，做出相反的承诺将背叛第一篇文章的第一个主张。</p>
</li>
</ol>
<p>当我两周前开始写这个博客时，并没有打算回溯早期历史。本意是谈 Charter、谈 emergent observation、谈框架今天实际在做的事情。但这个博客本身就是一个追溯性的行为 —— 它的存在，是因为某件事让我足够惊讶，以至于我开始动笔。而一旦我决定从后往前写，抵达一月就成了必然。抵达三天内的三次改名。抵达年份打错的 AIDEC。抵达 <em>Monimen</em>。</p>
<p>下一篇：框架的第一个真正的方法论实验 —— Sentinel 的六个 Plan，以及 <em>Plan</em> 被重命名为 <em>Charter</em> 的那一天。那才是这个博客本来要讲述的故事真正开始的地方。</p>
<hr>
<p><em>锚点：commit <a href="https://github.com/StrangeDaysTech/straymark/commit/7c58b6d" target="_blank" rel="noopener noreferrer" class=""><code>7c58b6d</code></a> · <a href="https://github.com/StrangeDaysTech/straymark/commit/0b772bc" target="_blank" rel="noopener noreferrer" class=""><code>0b772bc</code></a> · <a href="https://github.com/StrangeDaysTech/straymark/commit/25ab7a4" target="_blank" rel="noopener noreferrer" class=""><code>25ab7a4</code></a> · <a href="https://github.com/StrangeDaysTech/straymark/commit/7b7193e" target="_blank" rel="noopener noreferrer" class=""><code>7b7193e</code></a> · <a href="https://github.com/StrangeDaysTech/straymark/commit/c7e9026" target="_blank" rel="noopener noreferrer" class=""><code>c7e9026</code></a> · <a href="https://github.com/StrangeDaysTech/straymark/commit/21e03b2" target="_blank" rel="noopener noreferrer" class=""><code>21e03b2</code></a>。原始 README：<code>git show v1.0.0:README.md</code>。</em></p>
<p><em>本文档在生成式 AI 工具（Claude 4.7）的协助下撰写；内容的全部责任由人类作者承担。</em></p>]]></content>
        <author>
            <name>José Villaseñor Montfort</name>
            <uri>https://github.com/montfort</uri>
        </author>
        <category label="straymark" term="straymark"/>
        <category label="早期历史" term="早期历史"/>
        <category label="身份认同" term="身份认同"/>
        <category label="治理" term="治理"/>
    </entry>
</feed>