状态:已批准并已在生产中。 没有任何决定悬而未决:形态(D-043 和 D-044)、开关(D-045 和 D-046)、看板(D-047),以及带无障碍权重的初始严重级别(D-048,第 10 节)都已确定。本文件以本方法的全部八种语言发布,其两段提示词已属于提示词 API:自该处起,build/prompts.mjs 要求八种语言中的英文正文逐字节相同,否则构建失败。日期:2026-10-22。
本附录的决策登记:docs/DECISIONES.md,D-043 至 D-048。合规看板(第 12 节)已在 D-047 中以两个数字定案。实现工作活在 PND-002 里。
台阶 9 已经在测量代码,而且是用一道不放行的闸门在测:单一格式化器、尺寸上限(每个文件三百行、每个函数四十行)、每个函数的复杂度、重复代码、死代码、已声明且被使用的依赖、带棘轮的覆盖率,以及在存在真实用户之处做变异测试。这个数字会写进 docs/CODE_QUALITY.md。那一半已经解决。
没有解决的是产品的另一半。我核对了台阶语料:其中没有一处提到可用性,也没有一处提到人/角色或功能分组。今天界面靠两样东西支撑:所有者看上十秒的截图,以及手工测试用例。两者都很好,但两者都靠人、都昂贵、都会遗忘。一个系统可以带着荣誉通过质量闸门,却完全不可用:一片全绿,而人找不到在哪里收款。
还有一层第二层的问题,它确实是技术问题,但在界面上看得出来:当同一个产品被多个角色使用(管理、运营、查询、客户)时,功能在三处不同的地方被启用——数据库、菜单和路由守卫——而这三处写在不同的时刻,并且会自己走偏。症状总是同一个:有人看到一个不属于他的按钮,或者更糟,一个明明有权限的人找不到那个页面。
下面这两套启发式规则用同一种形态对付这两件事:一份简短的可验证陈述目录、少数几个自行测量的工具,以及一条为项目需要越界时准备的、被声明出来的例外通道。
诱惑是列一份规则清单,然后把它绑到第一个项目的技术栈上。那种做法老得很快:React 的规则不适用于 Angular,Angular 的规则不适用于带模板的服务端,两年之后清单有一半是死的。这个提案把三层分开。
第 1 层 — 与技术栈无关的内核(不可谈判)。 陈述的是结果,不是工具。“只有一个格式化器,而且没有人跟它争”在 TypeScript、PHP 或 Go 里同样成立。内核用稳定的标识符逐条枚举,不随项目而变。
第 2 层 — 按技术栈的适配器(可配置)。 对内核的每条规则,每个前端家族声明*用什么*测量、*用哪个阈值*。一个用 Vite 的 React 项目、一个 Next 项目、一个 Angular 项目、一个带模板的服务端、一个移动端和一个命令行,是同一个内核的六个不同适配器。用户所要求的“按前端可配置”,全部住在这里。
第 3 层 — 项目的决定(声明出来的)。 具体阈值与豁免。它们不是凭空编的:向所有者索取,并写下来,就像架构台阶的规模测算一样。如果某个阈值没有被声明,就运行适配器的那个,并如实登记为该状态。
每条规则永远用同一张六字段记录来写:
| 字段 | 内容 |
|---|---|
id | 稳定标识符(CQ-07、UX-12)。永不重用。 |
| 陈述 | 一句关于可观察结果的祈使句。 |
| 为什么 | 它避免的具体伤害。如果没有伤害,这条规则就是多余的。 |
| 如何验证 | 工具:自动闸门、查询矩阵、端到端测试或人工用例。 |
| 严重级别 | BLOQUEA(阻断:闸门给红,块不关闭)、AVISA(警告:开一个待办项)、REGISTRA(记录:只留在文档里)。 |
| 适用范围 | 生效的角色。不在其中就不运行,而这不是例外:是范围。 |
例外必须被声明,绝不省略。 一条规则被关掉,要有一份四字段记录:标识符、一行理由、谁批准(所有者,不是代理)以及复查日期。一条被关掉却没有记录的规则,是闸门的故障,不是一项决定。这和方法已经在用的判据一样:不接受为了“通过”而被抬高的上限。
任何启发式规则,如果它的验证是一种意见,就没有用。工具是四个,每条规则要声明自己属于哪一个。
内核延展台阶 9:它不替换、也不与它矛盾,而是给每一件已经在测的事加上名字、严重级别和适配器,并补上今天缺的东西,也就是被当作代码来看的界面。
| id | 家族 | 陈述 | 验证 | 严重级别 |
|---|---|---|---|---|
| CQ-01 | 形态 | 只有一个格式化器,无人干预地运行,并且不容争辩。 | 闸门 | 阻断 |
| CQ-02 | 形态 | 每个仓库只有一个 linter,它的规则对所有人相同;关掉一条规则要写出理由,绝不在某个孤立的文件里关。 | 闸门 | 阻断 |
| CQ-03 | 尺寸 | 任何文件不超过声明的上限(起点为 300 行),任何函数不超过自己的上限(起点为 40 行)。 | 闸门 | 阻断 |
| CQ-04 | 复杂度 | 任何函数不超过声明的路径上限。 | 闸门 | 阻断 |
| CQ-05 | 重复 | 不引入新的重复:被复制的块要么被抽取出去,要么被说明理由。 | 闸门 | 阻断 |
| CQ-06 | 死代码 | 没有死代码:未使用的函数、变量、导入、分支和依赖。 | 闸门 | 阻断 |
| CQ-07 | 类型 | 如果语言允许类型,项目就认真使用类型;“任意”类型是一条声明出来的例外,不是习惯。 | 闸门 | 警告 |
| CQ-08 | 测试 | 覆盖率不下降:今天的数字就是明天的底线。 | 闸门 | 阻断 |
| CQ-09 | 测试 | 在有真实用户时,变异得分按块测量,每个在新代码里存活下来的变异体开一个待办项。 | 闸门 + 登记 | 阻断 |
| CQ-10 | 依赖 | 每个依赖都已声明、被使用,并且没有带来与它贡献不相称的第三方依赖树。 | 闸门 | 警告 |
| CQ-11 | 密钥 | 仓库里没有凭据、密钥或连接串,新增的历史里也没有。 | 闸门 | 阻断 |
| CQ-12 | 界面 | 界面不做决定:任何页面或组件都不包含业务规则(什么可以做、收多少钱、接下来是什么状态)。它们住在领域里,由视图去查询。 | 关闭时审查 + 人工用例 | 阻断 |
| CQ-13 | 界面 | 每一次对服务端的调用都经过一个唯一的、带类型的客户端;没有任何页面自己手工拼请求。 | 闸门(检索) | 阻断 |
| CQ-14 | 界面 | 共享状态只有一处存放地;页面之间复制状态是故障,不是捷径。 | 关闭时审查 | 阻断 |
| CQ-15 | 界面 | 所有可见文本都来自翻译目录;没有任何用户可见字符串被写在组件内部。 | 闸门(检索) | 阻断 |
| CQ-16 | 界面 | 每个可交互元素都带稳定标识符和角色,好让自动测试按名字而不是按位置找到它。 | 闸门(检索) | 阻断 |
| CQ-17 | 界面 | 所有颜色、间距和字体都来自设计令牌;样式表里没有任何孤立的魔法值。 | 闸门(检索) | 阻断 |
| CQ-18 | 界面 | 基本无障碍自行验证:对比度、字段标签、可见焦点、主要流程中的键盘导航、带替代文本的图片。 | 闸门 | 警告 |
CQ-12 到 CQ-18 这些规则,正是让“代码质量”在前端有意义的东西。没有它们,项目可以拥有百分之九十的变异得分,却有一个组件用自己的算术在算总价:两个世界中最坏的那一个,因为重复的计算通过了后端的全部测试。
初始严重级别(D-048)。 从第一天起就阻断的,是十四条有验证器、且不依赖需要讨论的阈值的规则:CQ-01 至 CQ-06、CQ-08 以及 CQ-11 至 CQ-17。CQ-09 在有真实用户之处阻断,因为那才是被测量之处。警告的有三条,而且只有三条:CQ-07(类型)、CQ-10(依赖)和 CQ-18(无障碍),因为它们需要项目先做校准。格式化器、linter、尺寸和复杂度不放宽:台阶 9 今天就已经阻断它们,这里是延展它,不是替换它;遗留债务用一条声明出来的例外、或用棘轮的基线来解决,绝不靠降低规则的严重级别。而 CQ-12 和 CQ-14 是本表中仅有的两条以人工红、而不是机器红来阻断的规则:它们在待办项关闭时验证,把人工用例摆在面前,判据与 UX-22 和 UX-27 相同,而不是在每次提交时。如果项目撑不住这种关闭,规则降为 AVISA,那是因为有登记的决定,不是因为遗忘。
按前端家族的适配器。 第 2 层如何被实例化的一个例子(名称只是示意;项目自己选择并写下来):
| 概念 | 单页应用(React、Vue、Angular) | 带模板的服务端 | 原生移动端 | 命令行 |
|---|---|---|---|---|
| CQ-12 视图中的领域 | 针对组件尺寸与依赖的 lint 规则 + 审查 | 控制器审查 | 视图模型审查 | 命令层审查 |
| CQ-13 唯一客户端 | 由 API 契约生成 | 服务端唯一服务 | 生成的客户端 | SDK 的唯一客户端 |
| CQ-15 文本 | 目录 + 禁止可见字面量的规则 | 模板引擎的目录 | 资源目录 | 目录 |
| CQ-16 标识符 | 每个元素的声明式属性 | 标记中的标识符 | 无障碍标识符 | 稳定输出 |
| CQ-17 令牌 | 样式表中的令牌,无孤立值 | 同上 | 同上 | 不适用 |
| CQ-18 无障碍 | 闸门中的自动分析器 | 对生成的 HTML 分析 | 无障碍分析器 | 不适用 |
列与列之间变的是工具和阈值。不变的是陈述。这正是让启发式规则保持与技术栈无关、又不让它沦为装饰的原因。
五个家族。第一个和第二个是所有者明确要求的;另外三个之所以存在,是因为没有它们,分组和角色会以同样的方式退化,只是更慢。
母规则:一个功能按人来做的事分组,而不是按它出自哪个模块分组。 代码按实体组织(“发票”“客户”“付款”);人不这样。没有人是为了“管理实体”进入系统的:人是为了收款、发货、结账而进来。如果菜单显示的是实体,人就得当翻译。菜单*就是*工作。
| id | 陈述 | 验证 | 严重级别 |
|---|---|---|---|
| UX-01 | 菜单的每个分组都用一个用户的工作来命名(用他的词汇,用项目的术语表),绝不用技术模块或数据实体命名。 | 人工用例 + 矩阵审查 | 警告 |
| UX-02 | 每个功能恰好有一个归口。如果它服务两个分组,就选一个归口,从另一个链接过去;入口不复制。 | 查询矩阵 | 阻断 |
| UX-03 | 没有孤儿:每个声明的功能都能从入口页面在三步之内到达。 | 沿路由的自动遍历 | 阻断 |
| UX-04 | 分组的大小是合理的:少于三个功能说明它本来不是分组;多于七个说明它是个抽屉。 | 查询矩阵 | 警告 |
| UX-05 | 每个分组都有一个入口页面,说明在这里做什么、主操作是什么。没有哪个分组一打开就是一张空表。 | 人工用例 | 警告 |
| UX-06 | 人经常做、连在一起做的流程住在一起:如果“录入一笔付款”和“查看该客户的账户”在同一次会话里被使用,它们共用一个分组。 | 人工用例(任务测试) | 记录 |
| UX-07 | 首次点击测试:所有者面对一个具体任务(“你在哪里收一笔付款?”),说出一个地方。如果说得出两个,就有重复;如果一个都说不出,就有孤儿。每个块关闭时运行。 | 人工用例 | 警告 |
这里是最出产的那个零件,而理由是工程上的,不是设计上的:启用只在矩阵里声明一次,其余的一切都从它派生。 今天经典错误是把权限写三遍(菜单里、路由守卫里和数据过滤里),然后三者分开。以矩阵为唯一生产者,菜单、路由、服务端权限和测试脚本都是消费者,而方法已经要求的生产者/消费者对称就自己变得可验证了。
矩阵有四列,每个功能一行:*功能*、*归口*(B1 的分组)、*拥有它的角色*、*进入路由*。仅此而已。
| id | 陈述 | 验证 | 严重级别 |
|---|---|---|---|
| UX-08 | 每个功能都在矩阵里声明哪些角色拥有它。没有角色的功能不存在:要么给它分配角色,要么删掉。 | 查询矩阵 | 阻断 |
| UX-09 | 每个声明的角色至少有一个功能和自己的入口点。空角色是设计时没人用过的角色。 | 查询矩阵 | 阻断 |
| UX-10 | 导航由矩阵生成。手写的菜单是故障,不是捷径。 | 闸门(检索) | 阻断 |
| UX-11 | 隐藏不等于保护:每条路由除了藏起入口,还要在服务端校验权限。手写地址不足以进入。 | 端到端测试 | 阻断 |
| UX-12 | 不多不少:对每个角色来说,可到达的功能集合恰好等于它在矩阵里那一行。 | 按角色的端到端测试 | 阻断 |
| UX-13 | 拒绝要说清:如果一个人进入不属于他的功能,他看到一个消息,说明为什么、该向谁申请权限。绝不是空白页面,绝不是没有理由的灰按钮。 | 人工用例 | 警告 |
| UX-14 | 同一个按钮对所有人意味着同一件事:如果两个角色共用标签,就共用效果。如果效果随角色而变,标签就变,或者功能拆成两个。 | 人工用例 | 警告 |
| UX-15 | 数据也按角色过滤:看到列表的人看到属于他的那些行,而不是在浏览器里把行藏起来的完整列表。 | 端到端测试 + 服务端审查 | 阻断 |
| UX-16 | 矩阵和用户手册在与功能同一个待办项里更新。没有矩阵行的新功能不关闭。 | 待办项关闭 | 阻断 |
大多数“不能用”的投诉,是不会说出“什么都没有”“正在加载”或“失败了”的页面。四个状态,永远,在每张列表和每个详情页面上。
| id | 陈述 | 验证 | 严重级别 |
|---|---|---|---|
| UX-17 | 每张列表都有四个已处理的状态:有数据、空、加载中、出错。空状态区分“还没有”和“你的搜索没有找到”。 | 人工用例 + 审查 | 警告 |
| UX-18 | 当某事耗时,要看得见它在工作,如果很长还可以取消。一个毫无反应的按钮是第一号抱怨。 | 人工用例 | 警告 |
| UX-19 | 错误说明发生了什么、可以做什么,并且不抹掉已录入的内容。既不是只有一个技术代码,也不是一句“发生了错误”。 | 人工用例 | 警告 |
| id | 陈述 | 验证 | 严重级别 |
|---|---|---|---|
| UX-20 | 一个页面有一个明显的主操作;其余是次要的,并且看起来就是次要的。 | 人工用例 | 警告 |
| UX-21 | 每个破坏性操作都要在命名受影响对象的前提下请求确认;能撤销时,就撤销而不是确认。 | 人工用例 | 警告 |
| UX-22 | 失败的表单保留已录入的内容,并在字段所在的位置标出有问题的字段。 | 人工用例 | 阻断 |
| UX-23 | 没有任何操作把页面留在死路上:保存、删除或取消之后,人知道自己停在哪里、怎么继续。 | 人工用例 | 警告 |
| id | 陈述 | 验证 | 严重级别 |
|---|---|---|---|
| UX-24 | 界面说项目术语表的语言:同一个对象在页面里、手册里和邮件里叫同一个名字。一个同义词就是一个缺陷。 | 人工用例 + 审查 | 警告 |
| UX-25 | 主要流程里的一切都能不用鼠标、用屏幕阅读器完成;焦点永远可见,也不会被困住。 | 闸门(CQ-18)+ 人工用例 | 警告 |
| UX-26 | 状态不只靠颜色传达;每一张有信息量的图片都有替代文本;文本在正常字号下无需缩放即可阅读。 | 闸门(CQ-18) | 警告 |
| UX-27 | 每个触及页面的待办项都交付来自测试环境的真实截图,所有者在关闭之前看它。截图不是可选项,也不是一张效果图。 | 待办项关闭 | 阻断 |
本附录不发明新的台阶:它挂在已经存在的台阶上。这避免了闸门重复,也避免了可用性清单通常的命运——被读一次。
它放在哪里(D-043)。 它横贯四个阶段,主要重量在建设循环(台阶 5 到 12),因为那才是做决定和施工的地方。它不是自己的一个台阶:一个编号台阶会迫使整条梯子重编号、改变步数计数,并让本附录在建设转绿之前就受制于合同必须译成八种语言的义务,却不增加任何循环本来没有的闸门。
它如何挂接(D-044)。 一份唯一来源、五个锚点和两个前置入口。规则文本只住在这里,提示词引用它;绝不复制它。把四十五条规则重复进循环的八条提示词里,会稀释它们(它们与每个台阶自己的工作竞争,代理每次遵守得都不一样)、使它们走偏(同一文本的两份副本会自己不同步,正是台阶 11 所追的那种反模式),也不会让它们更横向:让一条规则横向的,是每个块都经过同一道闸门,而不是规则被写在所有地方。
| 时刻 | 增加什么 | 为什么在这里 |
|---|---|---|
| 前置入口 1 — 范围定义 | 用户的术语表 | 没有术语表,UX-01 和 UX-24 就没有可比较的东西。 |
| 前置入口 2 — 范围定义 | 角色清单 | 没有这份清单,B2 的矩阵会在台阶 5 被编出来,而由它派生的一切都生在无根据之上。 |
| 锚点 1 — 台阶 5(计划) | 每个触及页面的待办项都声明三件事:属于哪个收敛分组(B1)、哪些角色拥有它、哪些验收标准是按角色的(“给定角色 X,这个人能做 Y,不能做 Z”)。没有这份声明,待办项过不了这个台阶。 | 分组和角色在这里*诞生*:它们是计划决定,不是代码决定。它是产出最高的锚点,也是今天不存在的那个。 |
| 锚点 2 — 台阶 8(红测试) | 矩阵变成一个先被看着失败的测试:对每个角色,可到达的内容对照它那一行(UX-11、UX-12、UX-15)。 | 没有这个,矩阵就是一份没人看的文档。 |
| 锚点 3 — 台阶 9(质量) | 完整的 CQ 目录,连同它的适配器和已声明的阈值;docs/CODE_QUALITY.md 按规则报告,而不是一个单一数字。以及作为闸门的矩阵(UX-02、UX-03、UX-08、UX-09、UX-10、UX-16)。 | 这是文本唯一作为指令逐字进入的地方,也是每个块都带着棘轮运行的闸门。 |
| 锚点 4 — 台阶 10(人工用例) | 每个用例对每个受影响角色各执行一次,由矩阵派生,而不是手写。只用一个角色跑过的用例不算执行过。 | 人工用例是方法中唯一的人类验证;如果不按角色展开,B2 的一半永远不会被看。 |
| 锚点 5 — 台阶 12(验证与诚实报告) | 报告说明测了什么、什么留在警告状态、哪条规则被关掉,连同它的理由、批准人和复查日期。 | 零静默伤害:一个被关掉的分组不能被读成已完成。 |
| 用户手册 | 按 B1 的分组组织,并按分组声明哪些角色进入。 | 手册和菜单用同样的词命名,否则术语表就破了。 |
| 每个块的关闭 | 首次点击测试(UX-07)、真实环境的截图(UX-27)以及矩阵的两个端到端测试。 | 这是三项无法自动化的验证。 |
状态(D-049)。 五个锚点已接好:台阶 5、8、9、10、12 的起始提示词以八种语言引用本附录,英文表述在全部语言中只有一段且完全相同,旁边是本地语言译文。本文件与其余方法一起以八种语言在网站发布,其两段提示词已作为契约 anexo-h 进入 docs/html/api/prompts.json:自该处起,build/prompts.mjs 在八种语言中逐字节比对,不同则构建失败。
台阶 6、7 和 11 顶多收到一行:台阶 6 已经把阈值和例外登记为决定(它是声明式例外的天然位置),而台阶 11 正是“矩阵被写在四个地方”那个案子撞上来的地方。不给它们加自己的文本,是为了不稀释它们。
三个产物,都在 docs/ 里:CODE_QUALITY.md(延展)、USABILITY.md(新增,带按规则的结果和已声明的例外)和 PROFILE_MATRIX.md(矩阵,它既是文档,也是菜单的来源)。
每个项目一个文件声明第 2 层和第 3 层。下面是按建议形态写的例子,不是最终取值:
{
"version": 1,
"frontend": "react-vite",
"medido_por": {
"formato": "prettier",
"lint": "eslint",
"tipos": "tsc",
"complejidad": "eslint-complexity",
"duplicacion": "jscpd",
"muertos": "knip",
"cobertura": "vitest-coverage",
"mutacion": "stryker",
"accesibilidad": "axe-core"
},
"umbrales": {
"lineas_por_archivo": 300,
"lineas_por_funcion": 40,
"caminos_por_funcion": 10,
"cobertura_minima": 0,
"mutacion_objetivo": 0
},
"excepciones": [
{
"regla": "CQ-17",
"motivo": "遗留的可打印报表,未使用设计令牌",
"autoriza": "所有者",
"revisar": "2027-01-31"
}
]
}
那两个零是故意的,而且正是要紧的部分:最低覆盖率由棘轮用实测值确定,变异目标由所有者在项目有用户时确定。如果它们是零而没有人声明过,闸门会把它报告为未声明的阈值,而不是自己编一个。
代码质量。
Set up and maintain the code-quality gate for this project, configured for its frontend family. The core rules are the same in every project; what changes is how each one is measured and at which threshold. Install one formatter and one linter with a single shared configuration; enforce size limits per file and per function, complexity per function, duplication, dead code, declared-and-used dependencies, type checking where the language allows it, coverage with a ratchet, and secret scanning. Where the project has real users, add mutation testing scoped to the changed lines and record the score per block. Then extend the gate to the frontend itself: no business rule inside a screen, one typed client for every server call, one home for shared state, no user-visible string outside the translation catalogue, a stable identifier and role on every interactive element, styling only through tokens, and automatic basic accessibility checks. Every rule has a stable id, a severity (blocks, warns, records) and a named verifier. Thresholds and exceptions are declared by the owner in one configuration file; a rule switched off without a written exception is a gate failure, not a decision. Report the result per rule in
docs/CODE_QUALITY.md, not as a single number.
为本项目搭建并维护代码质量闸门,按它的前端家族来配置。内核规则在每个项目里都一样;变的是每条规则用什么测量、在哪个阈值上测量。安装一个格式化器和一个 linter,共用一份配置;强制每文件和每函数的尺寸上限、每函数的复杂度、重复、死代码、已声明且被使用的依赖、语言允许时的类型检查、带棘轮的覆盖率以及密钥扫描。在项目有真实用户之处,增加限定在改动行上的变异测试,并按块记录得分。然后把闸门延展到前端本身:页面里不放业务规则、每次服务端调用只有一个带类型的客户端、共享状态只有一处存放地、翻译目录之外不出现任何用户可见字符串、每个可交互元素都有稳定标识符和角色、样式只通过设计令牌、以及基本的无障碍自动检查。每条规则都有稳定编号、严重级别(阻断、警告、记录)和有名字的验证器。阈值和例外由所有者在同一个配置文件里声明;一条被关掉却没有书面例外的规则属于闸门的故障,不是一项决定。在 docs/CODE_QUALITY.md 里按规则报告结果,不要汇成一个数字。
可用性。
Build the usability criteria for this project before writing the first screen, and enforce them where the method already has hooks. Group functionality by what the person came to do, never by the internal module or the data entity: every menu group is named with a user job in the project glossary, every functionality has exactly one canonical home, no functionality is orphaned, and every group is reachable in no more than three steps from the entry screen. Then declare accessibility in one matrix and derive everything from it: each functionality lists the profiles that have it, each functionality has at least one profile, each profile has at least one functionality and its own entry point, the navigation is generated from the matrix instead of written by hand, and every route checks the permission on the server because hiding an entry is not protecting it. Verify the matrix per profile with an end-to-end check that compares what is reachable with the matrix row: neither less nor more. Require the four states on every list (with data, empty, loading, error), a visible working indicator with the option to cancel long operations, an error message that says what happened and what to do without losing entered data, a primary action per screen, confirmation or undo for destructive actions, and a form that keeps what was typed. Run each manual test case once per affected profile, run the first-click check at the close of every block, and require a real screenshot of the test environment on every item that touches a screen. Record the result per rule, with owner-declared exceptions, in
docs/USABILITY.md, and keep the matrix indocs/PROFILE_MATRIX.mdas the single source of truth.
在写第一个页面之前,为本项目建立可用性准则,并在方法已经有挂接点的地方强制它们。按人来做的事分组功能,绝不按内部模块或数据实体分组:菜单的每个分组都用项目术语表里的一个用户工作来命名,每个功能恰好有一个归口,没有任何功能成为孤儿,每个分组都能从入口页面在三步之内到达。然后用一个矩阵声明无障碍,并从中派生一切:每个功能列出拥有它的角色,每个功能至少有一个角色,每个角色至少有一个功能和自己的入口点,导航由矩阵生成而不是手写,每条路由都在服务端校验权限,因为把入口藏起来保护不了任何东西。按角色用端到端测试验证矩阵,把可到达的内容与矩阵那一行比较:不多不少。要求每张列表都有四个状态(有数据、空、加载中、出错)、一个可见的进行中指示并能取消长操作、一条说明发生了什么和该做什么且不丢失已录入数据的错误消息、每个页面一个主操作、破坏性操作的确认或撤销,以及一个保留已输入内容的表单。每个手工测试用例对每个受影响角色跑一次,每个块关闭时跑首次点击测试,并要求每个触及页面的待办项都附上测试环境的真实截图。按规则记录结果,连同所有者声明的例外,写进 docs/USABILITY.md,并把矩阵保存在 docs/PROFILE_MATRIX.md 里作为唯一真相来源。
| # | 决定 | 登记 |
|---|---|---|
| 1 | 横贯建设循环,不是自己的台阶。 梯子不重编号,步数计数不变,本附录也不继承翻译一个台阶合同的义务。 | D-043 |
| 2 | 一份唯一来源,带五个锚点和两个前置入口, 而不是把四十五条规则复制进循环的八条提示词里。 | D-044 |
| 3 | 两个彼此独立的开关(质量和可用性)、每条规则三种状态(阻断、警告、关掉)、按前端家族的适配器、由所有者声明的阈值和例外。 | D-045 |
| 4 | 开关的纪律: 它住在项目配置里,不在提示词里;关掉只有在有理由、批准人和复查日期的登记决定下才算数;关掉在报告里是可观察的;开关要通过开和关两种方式来测。 | D-046 |
| 5 | 初始严重级别,逐条规则定下: 质量里阻断的有十四条(CQ-01 至 CQ-06、CQ-08、CQ-11 至 CQ-17),CQ-09 在有真实用户之处阻断,警告的有三条(CQ-07、CQ-10、CQ-18)。可用性里阻断的有十一条(UX-02、UX-03、UX-08 至 UX-12、UX-15、UX-16、UX-22、UX-27),UX-06 只记录,其余警告。既不是“全部都阻断”——项目根本起不来——也不是“全部都警告”——启发式规则就成了装饰。 | D-048 |
| 6 | 无障碍从警告起步,并在所有者声明时升级为阻断,前提是产品有组织之外的最终用户。默认值不靠编:升级要被声明,用与任何阈值相同的机制(D-045)。 | D-048 |
阻碍撰写提示词的那两个问题已经回答:第 1 个落在第 5 行,第 2 个落在第 6 行。但被批准的那份建议有两处事实错误,写在这里是为了它们不再回来:
还有一条声明出来的限制,因为它是同类中唯一的一条:CQ-12 和 CQ-14 以人工红来阻断。 没有任何工具能证明一个页面不做业务决定。它们在待办项关闭时验证,把人工用例摆在面前,判据与 UX-22 和 UX-27 相同;如果项目撑不住这种关闭,它们因登记的决定降为 AVISA。一条阻断却没人验证的规则,比一条警告的规则更糟。
翻译。 已完成:本附录以八种语言存在,其两段提示词属于提示词 API,八种版本的英文正文逐字节相同(D-043 如此要求,build/prompts.mjs 在每次构建时验证)。西班牙语仍是正典:若某次译文与西班牙语不一致,以西班牙语为准,并在同一次修改中修正译文。
一条启发式规则,如果开关它不改变任何东西,那就不是闸门,是装饰。实现的验收测试(PND-002)是一个夹具项目,里面故意种下每个家族的违规:
| 场景 | 要求的结果 |
|---|---|
| 质量开启,带违规 | 红,并指出规则和违规所在的文件。 |
| 质量关掉有登记 | 绿,且报告说明它被关掉、从何时起、谁批准的。 |
| 质量关掉没有登记 | 红。没有书面理由就关掉是故障,不是规则的缺席。 |
| 可用性开启,有角色越界到达 | UX-12 变红,并指出角色和功能。 |
| 可用性开启,有功能在矩阵里没有行 | 关闭待办项时 UX-08 变红(UX-16)。 |
| 两组都关掉 | 两边都绿,报告里有两行关掉说明。 |
| 种下一条警告规则的违规 | 绿,并开一个待办项,指出规则和文件:警告不等于批准。 |
| 种下一条阻断规则的违规 | 红,并指出规则、文件和补救办法。而且没有看板:平均值不关闭块。 |
| 看板:一条规则带登记被关掉 | 合规率可以上升,但闸门覆盖率下降,数字连同解释它的依据一起公布。只有一个数字时这个场景会被漏掉;这就是两个数字存在有其理由的证明(D-047)。 |
| 看板:两次运行,规则集不同 | 两个数字都带着各自的指纹(目录版本、启用与关掉的规则、适配器、工具版本)显示,并且不画成一条连续序列:它们是两把不同的尺子,说出这一点是结果的一部分。 |
| 看板:规则集无效(规则被关掉却无登记) | 不输出数字。输出是红,不是一个带星号的看板。 |
| 看板:合规率很高,但有一条阻断规则是红的 | 该块不关闭。看板做平均;绿由每条规则逐条决定。 |
| 看板:只碰自动化部分就提高了百分比 | 百分比上升,而人类验证那一行不动;它单独报告,报告不把两件事加成一个数字。 |
要看的不仅是颜色:要看报告里那两行关掉说明是否存在。一个没有这一行的绿色结果,是这项测试唯一无法仅凭颜色发现的故障,所以它靠读输出来验证。
状态。 2026-10-22 在 D-047 中决定:看板带两个数字、带规则集指纹、带棘轮、把人做的部分排除在百分比之外,并且依据是强制的。它不授权关闭块。以下是实现(PND-002)必须满足的规格。
它是什么。 它是闸门已经输出的按规则结果的一个确定性派生。它不测任何新东西,也不判断任何新东西:它数数。如果看板能改变一条规则的裁决,同一件事就有了两个真相来源,而那正是本附录在 UX-10(导航由矩阵派生)和 CQ-14(状态只有一处存放地)里所对抗的。
它不是什么。 它不是验收标准。没有任何数字授权关闭一个块:绿还是红,由每条阻断的规则逐条决定。看板描述趋势、指出该看什么。如果它开始被当作关闭阈值来用,它就不再有用,因为把数字抬上去最短的路是关掉规则。
两个数字,永远不是一个。
| 数字 | 公式 | 测量什么 | 如何变动 |
|---|---|---|---|
| 合规率 | 通过的规则 ÷ 启用的规则 | 正在被看的那部分有多干净 | 修好时上升 |
| 闸门覆盖率 | 启用的规则 ÷ 目录中声明的规则 | 本附录有多少真的在看 | 关掉一条规则时下降 |
为什么是两个:关掉一条规则会抬高合规率,并降低覆盖率。只有一个数字时,关掉是免费的,看板奖励的是失明。有两个朝相反方向移动的数字,这条捷径就消失了:要展示更好的看板,得去修代码,而不是关规则。而覆盖率不测量就无法提高。
规则集的指纹。 每条记录都保存它用什么尺子算出来:目录版本、启用与关掉的规则、前端适配器以及工具版本。没有这些,昨天的 82 和今天的 91 尽管写得一样,却不是同一种测量:它们是两套不同的规则集,序列是假的。两个没有指纹的数字制造进步的幻觉,那比没有看板更糟。如果指纹变了,记录被标为不可比较,或者历史按现行尺子重算;绝不不加说明地画在一起。
棘轮。 合规率不会从一个块到下一个块自行下降,除非有一条解释它的登记决定。这与 CQ-08 中测试覆盖率已经拥有的机制相同:今天的数字是明天的底线。下降不被禁止;没有登记就下降,被禁止。
不进账的东西。 只有人才能判断的东西——首次点击测试(UX-07)、任务测试(UX-06)、截图(UX-27)以及所有人工用例——按块登记为通过或失败,带名字和日期,并作为人类验证覆盖率单独报告。它不进合规率百分比。如果混在一起,结果就是一个百分之六十测量、百分之四十意见的数字,而没人能说出哪一部分是哪一部分。所以第 9 节仍然有效:看板不发明可用性评分,它数规则。
如何证伪它。 扩充第 11 节的验收测试:
| 场景 | 要求的结果 |
|---|---|
| 种下一条阻断规则的违规 | 合规率恰好下降一条规则除以启用规则数,并出现指出规则的红色。 |
| 一条规则带登记被关掉 | 合规率可以上升;覆盖率下降一条规则除以声明规则数;报告指出规则、理由和复查日期。 |
| 没有登记就关掉 | 红,并且不输出看板:无效的规则集不产生数字。 |
| 同一棵树、同一份配置,跑两次 | 同一个数字,完全相同。 |
| 两个块之间配置改变 | 两条记录都被标为不可比较,或者历史按现行尺子重算。 |
| 看板被用作关闭标准 | 不当使用,不是有效场景:绿由规则决定,不是由平均值决定。 |
它出现在哪里。 在台阶 12 的诚实报告里(锚点 5),并且按这个顺序:先是闸门的二元裁决,然后是两个数字,然后是什么被关掉、关到什么时候,最后是相对上一个块的变动及其被命名的原因。永远带着依据:*下降了四分:CQ-15,收款页面上有三条字符串在目录之外*。没有依据的数字是噪音;依据才让它对一个不读代码的所有者变得可执行。
已声明的风险。 看板会被所有者读,而他不读代码,所以他会希望它上升。数字变成目标,而目标会挤掉目的。防线就是上面那五条,而且正好五条:关掉时相互矛盾的两个数字、让不同尺子无法比较的指纹、不允许无登记下降的棘轮、把人做的部分排除在百分比之外,以及强制的依据,加上绝不用平均值谈判的闸门。这五条里任何一条倒了,看板就关掉:一个被误用的看板,比没有看板害处更大。
本附录的每一项决定都留在 docs/DECISIONES.md,带日期、作者、原文引用和受影响的文件:D-043(形态)、D-044(机制)、D-045(开关)、D-046(开关的纪律)、D-047(带两个数字的看板)。实现及其验收标准活在 PND-002 里。
没有任何决定悬而未决:形态(D-043 和 D-044)、开关(D-045 和 D-046)、看板(D-047),以及带无障碍权重的初始严重级别(D-048)都已确定。正文以八种语言流通,并属于提示词 API,这关闭了日程上仅剩的一项。