97a43591a3
- npm 包、TypeScript、node --test、dependency-cruiser;结构.md 第六部分的检查规则一次写全,临时违规实测均报错。 - vocab:基础类型与编号格式、规范序列化与哈希、角色/阶段/提交名单、指标与运行定义及结果、模型用量、16 种提交的 TypeBox 字段定义。 - 测试:规范序列化预期串手写,哈希预期值用 sha256sum 算出;每种提交一份合法样例。 - 结构.md 第八部分补 vocab 对外内容;WORK_LOG 记下用户决定。 用户决定(2026-10-01):"同意 A,vocab 上限改为 1100 行";"同意 A:解释的 cells 去掉 minItems,改为可选";"3、4、5、6:同意保留你的写法";答案路径与保障动手次数"tasks 只记读到的原始内容",对表由以后有位置表的模块现算。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
130 lines
4.5 KiB
JavaScript
130 lines
4.5 KiB
JavaScript
// 模块之间的依赖规则,来源是 docs/结构.md 第二部分(模块表)和第六部分(检查规则)。
|
||
// 模块按 src/<模块名>/ 认;新建模块目录后自动受这些规则检查。规则只管 src 下的模块,tests/ 不受"只有某模块能用某外部包"的限制。
|
||
|
||
// 模块表"依赖"一列:每个模块除了自己和 vocab 以外,还能引用哪些模块。plugin 装配时可用各模块。
|
||
const ALLOWED = {
|
||
plugin: [
|
||
"flow", "export", "config", "materials", "submissions",
|
||
"ledger", "agents", "runner", "tasks", "workspace", "jev", "rules",
|
||
],
|
||
flow: [
|
||
"ledger", "rules", "materials", "submissions", "agents",
|
||
"runner", "tasks", "workspace", "config", "jev",
|
||
],
|
||
export: ["ledger", "rules", "materials"],
|
||
config: [],
|
||
materials: ["ledger", "rules"],
|
||
submissions: ["jev"],
|
||
ledger: [],
|
||
agents: [],
|
||
runner: [],
|
||
tasks: [],
|
||
workspace: [],
|
||
jev: [],
|
||
rules: [],
|
||
vocab: [],
|
||
};
|
||
const MODULES = Object.keys(ALLOWED);
|
||
const anyOf = (names) => `(${names.join("|")})`;
|
||
|
||
// 外部的东西各由哪些名字认出来。核心模块带不带 node: 前缀都认;
|
||
// npm 包装好后解析成 node_modules/ 下的路径,没装时是包名本身,两种都认。
|
||
const npm = (names) => `(^|node_modules/)${anyOf(names)}(/|$)`;
|
||
const SUBPROCESS = [
|
||
"^(node:)?(child_process|cluster)$",
|
||
npm(["cross-spawn", "execa"]),
|
||
];
|
||
const SQLITE = ["^node:sqlite$", npm(["better-sqlite3", "sqlite3"])];
|
||
const PI = npm(["@earendil-works/[^/]+"]);
|
||
|
||
const directionRules = MODULES.map((name) => ({
|
||
name: `direction-${name}`,
|
||
comment: `${name} 只能引用自己、vocab 和模块表里列出的模块:${ALLOWED[name].join("、") || "无"}`,
|
||
severity: "error",
|
||
from: { path: `^src/${name}/` },
|
||
to: { path: "^src/", pathNot: `^src/${anyOf([name, "vocab", ...ALLOWED[name]])}/` },
|
||
}));
|
||
|
||
module.exports = {
|
||
forbidden: [
|
||
{
|
||
name: "no-circular",
|
||
comment: "不许循环引用",
|
||
severity: "error",
|
||
from: {},
|
||
to: { circular: true },
|
||
},
|
||
{
|
||
name: "unknown-module",
|
||
comment: `src 下只有模块表里的模块目录:${MODULES.join("、")}`,
|
||
severity: "error",
|
||
from: { path: "^src/", pathNot: `^src/${anyOf(MODULES)}/` },
|
||
to: {},
|
||
},
|
||
...directionRules,
|
||
{
|
||
name: "through-index",
|
||
comment: "别的模块只能通过目录下的 index.ts 引用它",
|
||
severity: "error",
|
||
from: { path: "^src/([^/]+)/" },
|
||
to: { path: "^src/[^/]+/", pathNot: ["^src/$1/", "^src/[^/]+/index\\.ts$"] },
|
||
},
|
||
{
|
||
name: "outside-src",
|
||
comment: "src 下的文件只许引用 src/、node_modules/ 与 Node 内置模块,不能借 src 外的文件绕开规则",
|
||
severity: "error",
|
||
from: { path: "^src/" },
|
||
to: { pathNot: ["^src/", "^node_modules/"], dependencyTypesNot: ["core"] },
|
||
},
|
||
{
|
||
name: "pure-allowlist",
|
||
comment:
|
||
"rules 和 vocab 不碰文件、数据库、网络、子进程和 Pi:除了 src 下允许的模块," +
|
||
"只许引用 typebox 和 node:crypto",
|
||
severity: "error",
|
||
from: { path: "^src/(rules|vocab)/" },
|
||
to: { pathNot: ["^src/", "^node_modules/typebox/", "^(node:)?crypto$"] },
|
||
},
|
||
{
|
||
name: "sqlite-only-ledger",
|
||
comment: "只有 ledger 能用 SQLite",
|
||
severity: "error",
|
||
from: { path: "^src/", pathNot: "^src/ledger/" },
|
||
to: { path: SQLITE },
|
||
},
|
||
{
|
||
name: "pi-only-agents-plugin",
|
||
comment: "只有 agents 和 plugin 能用 Pi",
|
||
severity: "error",
|
||
from: { path: "^src/", pathNot: "^src/(agents|plugin)/" },
|
||
to: { path: PI },
|
||
},
|
||
{
|
||
name: "subprocess-only-runner-workspace",
|
||
comment: "只有 runner 和 workspace 能起子进程",
|
||
severity: "error",
|
||
from: { path: "^src/", pathNot: "^src/(runner|workspace)/" },
|
||
to: { path: SUBPROCESS },
|
||
},
|
||
{
|
||
name: "tasks-not-runner",
|
||
comment: "tasks 不许引用 runner:runner 只管怎么跑,tasks 只管怎么判,由 flow 串起来",
|
||
severity: "error",
|
||
from: { path: "^src/tasks/" },
|
||
to: { path: "^src/runner/" },
|
||
},
|
||
],
|
||
options: {
|
||
doNotFollow: { path: "node_modules" },
|
||
tsPreCompilationDeps: true,
|
||
tsConfig: { fileName: "tsconfig.json" },
|
||
// 按 Node 的 ESM 规则读 npm 包的 exports(dependency-cruiser 默认不读),
|
||
// 否则 typebox 这类只在 exports 里给入口的包解析不到。
|
||
enhancedResolveOptions: {
|
||
extensions: [".ts", ".js"],
|
||
exportsFields: ["exports"],
|
||
conditionNames: ["import", "node", "default"],
|
||
},
|
||
},
|
||
};
|