chore(checkpoint): structure check rejects :has() that matches the style attribute
Unfinished: on purpose this check fails on the current tree, at web/styles/15-shell-rail.css:142, until the issue #22 fix reaches this branch. Do not merge into feature/next before that. Done: the check (tools/check_structure.mjs section 5), the lesson entry, AGENTS.md and docs/guides/testing.md. Verified that it reports the 15-shell-rail.css rule, reports both rules of the v1.57.2 file, and passes once line 142 uses a class instead. Next: merge main (with the #22 fix) into feature/next, put this on top, run node tools/run_tests.mjs, then merge feature/next into feature/model-import.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
以下几条每次提交都要做到,没有“这次改动小”的例外;其余各节是怎么做好的指引。
|
||||
|
||||
1. **结构和文档同步。** 按下表更新文档,与代码放在同一次提交里。新增、删除、改名、拆分源码文件一律算改了模块职责:职责清单同时是文件目录,每个源码文件都要有一行。
|
||||
2. **跑结构检查。** `node tools/check_structure.mjs`,失败不提交。它检查每个源码文件都登记在架构文档里、每个前端模块都能从入口引用到、每个前端文件都能按浏览器的方式解析(`node --check` 对 .js 文件会漏掉浏览器报错的写法),并执行规模阈值(第 2 节):新文件不能一开始就超线,已超线的文件比上次提交只能变短或持平。
|
||||
2. **跑结构检查。** `node tools/check_structure.mjs`,失败不提交。它检查每个源码文件都登记在架构文档里、每个前端模块都能从入口引用到、每个前端文件都能按浏览器的方式解析(`node --check` 对 .js 文件会漏掉浏览器报错的写法)、CSS 里没有按 `style` 属性匹配的 `:has()`(会让拖动画布掉帧,见 [lessons.md](docs/architecture/lessons.md)),并执行规模阈值(第 2 节):新文件不能一开始就超线,已超线的文件比上次提交只能变短或持平。
|
||||
3. **不留旧实现。** 被取代的模块、样式、文案 key、文档说明同一次提交删掉(第 6 节)。
|
||||
4. **按风险验证**(第 7 节),只提交本任务的文件,没有明确同意不推送。收尾时工作区干净,或在交付说明里列出每个有意留下的未提交文件。
|
||||
5. **测试产物不进仓库。** 测试代码放 `tests/`;截图、录屏、日志、原始测试数据、审计附件放在 `.agents/`(已忽略,只留本机),不用 `git add -f` 绕过忽略规则。提交的文件里不写本机路径、用户名或 `file:///` 链接,文档之间用相对链接。
|
||||
|
||||
@@ -103,3 +103,23 @@ body:has(#anomalous-toolbox-modal[style*="display: flex"]) #anomalous-sidebar-to
|
||||
**以后怎么做**
|
||||
- 两个仓库的规范都写了安全底线(本仓库 AGENTS.md 第 5 节“安全底线”,Anomalous_TTS 的 ARCHITECTURE.md“不能破的规矩”)。新接口按它写,审查时逐条对照。
|
||||
- 本仓库的现状(2026-10-09 统计):55 个会写文件的接口,只有 MCP、模型导入、整理三处检查了本机请求;删除模型、改模型信息、开始下载、导入备份等在 `--listen` 时局域网里的其他设备也能调用。没有执行下载内容、pickle、eval 的代码。2026-10-12 的 v1.58.1 一次补齐:`api/local_guard.py` 一个中间件把关 `/anomalous/` 下所有会改东西的请求,只接受本机(见 backend.md“Requests from other devices”)。
|
||||
|
||||
---
|
||||
|
||||
## 2026-10-13 · v1.58.0–v1.58.2:同一种 `:has()` 又掉帧(第二次)
|
||||
|
||||
**现象**:#22(Lindemellis):216 个节点的工作流,开着 Prompt Assistant 时一次画布重绘 465–770 ms,拖动只有 2–3 帧。只删掉一条规则就回到 53 ms。
|
||||
|
||||
**根因**:图标栏(46facb9,2026-09-29,随 v1.58.0 发布)在 `web/styles/15-shell-rail.css` 里又写了
|
||||
|
||||
```css
|
||||
#anomalous-container.anomalous-has-rail:has(> .anomalous-nb-modal[style*="display: flex"]) #anomalous-sidebar-wrapper
|
||||
```
|
||||
|
||||
和上面 v1.57.2 那条是同一种写法:`:has()` 里按 `style` 属性匹配。v1.57.2 修好三天后就写回来了,因为经验只写在这份文档里,没有东西拦着;当时的性能实测也只跑了 main 和音频分支,没跑图标栏所在的分支。
|
||||
|
||||
**修复**:工作区开没开由 JS 在容器上加减 `anomalous-workspace-open`,CSS 只看这个 class(#22 的修复)。
|
||||
|
||||
**以后怎么做**
|
||||
- `node tools/check_structure.mjs` 现在会拒绝 `:has()` 里出现 `[style…]`,报出文件和行号。
|
||||
- 同一个坑第二次出现,就把它做成检查,不再只补一段经验。
|
||||
|
||||
@@ -31,7 +31,7 @@ Python 用哪个:
|
||||
## 3. 只跑一部分
|
||||
|
||||
```bash
|
||||
# 结构检查:每个源码文件都登记在架构文档里、前端模块都能被引用到、文件长度不超线
|
||||
# 结构检查:每个源码文件都登记在架构文档里、前端模块都能被引用到、文件长度不超线、CSS 没有按 style 属性匹配的 :has()
|
||||
node tools/check_structure.mjs
|
||||
|
||||
# 一个前端测试
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// Fails when a source file is missing from the architecture map, a web module is
|
||||
// no longer imported from an entry, a new file starts over the size threshold, a
|
||||
// file already over it grew since the last commit (AGENTS.md section 2), or a web file
|
||||
// does not parse as the ES module the browser loads.
|
||||
// does not parse as the ES module the browser loads, or a CSS :has() matches the style
|
||||
// attribute.
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
@@ -81,6 +82,26 @@ for (const line of syntax.split(/\r?\n/).filter(Boolean)) {
|
||||
failures.push(`${path.relative(ROOT, file).split(path.sep).join('/')}: does not parse (${message})`);
|
||||
}
|
||||
|
||||
// 5. No `:has()` that matches the style attribute. Chrome then treats every inline-style
|
||||
// write on the page as a possible change and recalculates styles for the whole page; with
|
||||
// extensions that move overlays every frame, canvas dragging fell to a few fps (v1.57.2,
|
||||
// issue #22). Mirror the state as a class on an ancestor instead.
|
||||
for (const file of [...list('web', '.css'), ...list('web/styles', '.css')]) {
|
||||
const css = read(file).replace(/\/\*[\s\S]*?\*\//g, (comment) => comment.replace(/[^\n]/g, ' '));
|
||||
for (let start = css.indexOf(':has('); start !== -1; start = css.indexOf(':has(', start + 1)) {
|
||||
let depth = 0;
|
||||
let end = start + 4;
|
||||
for (; end < css.length; end++) {
|
||||
if (css[end] === '(') depth++;
|
||||
else if (css[end] === ')' && --depth === 0) break;
|
||||
}
|
||||
if (/\[\s*style\b/i.test(css.slice(start, end))) {
|
||||
const line = css.slice(0, start).split('\n').length;
|
||||
failures.push(`${file}:${line}: :has() matches the style attribute, which makes every inline-style change restyle the whole page; toggle a class instead`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length) {
|
||||
console.error(`\nStructure check failed:\n ${failures.join('\n ')}`);
|
||||
process.exit(1);
|
||||
|
||||
Reference in New Issue
Block a user