75d7cffd27
按任务书 12 实现 src/flow:从账本现算下一步的纯函数、主循环、恢复入口、派模型任务和跑正式运行两个共用动作、冻结配置、基线、第零轮三个研究者阶段和入口分配,走到第 1 轮时停下。materials 导出 finalEntries、加 researcherId 和 StudyRecord。 审阅采纳两条:研究编号本机已用过时冻结前报错;docker run 没启动时写完结果就停下,不在本进程里无限起新编号。 eval/ 新增第零轮检验:起研究命令行、回放假模型、检验用 Jev 客户端、六问质量评估、16-real-1 回放、杀进程与两份同时跑。tsc 与 depcruise 也检查 eval/,新增 eval 只经 index.ts 用正式代码的规则。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
139 lines
5.0 KiB
JavaScript
139 lines
5.0 KiB
JavaScript
// 模块之间的依赖规则,来源是 docs/结构.md 第二部分(模块表)和第六部分(检查规则)。
|
||
// 模块按 src/<模块名>/ 认;新建模块目录后自动受这些规则检查。规则主要管 src 下的模块,tests/、eval/ 不受"只有某模块能用某外部包"的限制;
|
||
// eval/ 另有一条:只能经 src/<模块>/index.ts 用正式代码(任务书 12 第三部分第 4 条)。
|
||
|
||
// 模块表"依赖"一列:每个模块除了自己和 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: ["rules"],
|
||
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: "eval-through-index",
|
||
comment:
|
||
"eval/ 像用户一样用正式代码,只能经 src/<模块>/index.ts 引用(CLAUDE.md\"测试\":正式代码和检验代码完全分开)",
|
||
severity: "error",
|
||
from: { path: "^eval/" },
|
||
to: { path: "^src/", pathNot: "^src/[^/]+/index\\.ts$" },
|
||
},
|
||
{
|
||
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"],
|
||
},
|
||
},
|
||
};
|