feat: 研究编号只许小写、配置路径必须绝对;检验判重两个顺序取平均

任务书 13 第一部分,用户 2026-10-03 同意。
- config:studyId 限定小写字母、数字和 ._-;task、promptDir 必须以 / 开头。
- eval:research.json 路径改绝对,writeConfig 不再转换;quality.ts 第 2、6 问每对 A、B 与 B、A 各问一次取平均,两次原始概率都存。
- tests:大写编号、相对路径各加测试。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 11:42:11 -04:00
parent b516f8a612
commit a42349717c
8 changed files with 88 additions and 30 deletions
+7 -1
View File
@@ -19,6 +19,12 @@ const TYPE_TEXT: Readonly<Record<string, string>> = {
null: "null",
};
/** schema.ts 里文字字段、路径字段的格式,换成人读得懂的话。 */
const PATTERN_TEXT: Readonly<Record<string, string>> = {
"\\S": "不能为空,也不能只有空白",
"^/": "必须是绝对路径(以 / 开头)",
};
function reasonOf(e: SchemaError): string {
const p = e.params as Record<string, unknown>;
switch (e.keyword) {
@@ -39,7 +45,7 @@ function reasonOf(e: SchemaError): string {
case "minItems":
return `至少要有 ${String(p.limit)} 项`;
case "pattern":
return p.pattern === "\\S" ? "不能为空,也不能只有空白" : `格式不对,应符合 ${String(p.pattern)}`;
return PATTERN_TEXT[String(p.pattern)] ?? `格式不对,应符合 ${String(p.pattern)}`;
default:
return e.message;
}
+17 -4
View File
@@ -1,6 +1,5 @@
import { Type, type Static, type TSchema } from "typebox";
import { BATCH_RULES, CELL_RULES, type BatchRuleName, type CellRuleName } from "../rules/index.ts";
import { Id } from "../vocab/index.ts";
// research.json 的字段定义(docs/结构.md 第二部分、第五部分"方法参数""步骤开关")。
// 每个字段都必填,不写默认值;不认识的字段报错。可以没有的值写 null(目前没有这样的字段)。
@@ -15,6 +14,11 @@ export type FieldCategory = (typeof FIELD_CATEGORIES)[number];
const CLOSED = { additionalProperties: false } as const;
/** 文字字段:不能为空,也不能只有空白。 */
const TEXT = { pattern: "\\S" };
/**
* 路径字段必须是绝对路径:从别的目录接着跑一份研究时,相对路径会读到别处,而任务目录没有哈希核对挡着
* (任务书 13 第一部分第 2 条)。
*/
const ABSOLUTE_PATH = { pattern: "^/" };
function user<T extends TSchema>(schema: T, description: string): T {
return { ...schema, description, category: "用户配置" };
@@ -127,8 +131,14 @@ const PositiveInteger = Type.Integer({ minimum: 1 });
export const ResearchConfig = Type.Object(
{
studyId: user(Id, "研究编号:决定的种子由它和轮次、版本算出;只许字母、数字和 ._-,首字符是字母或数字"),
task: user(Type.String(TEXT), "用哪个任务:任务目录的路径。指标、方向、ε、时限等任务事实写在任务目录里"),
studyId: user(
Type.String({ pattern: "^[a-z0-9][a-z0-9._-]{0,127}$" }),
"研究编号:决定的种子由它和轮次、版本算出,Docker 镜像名也带它,所以只许小写字母、数字和 ._-,首字符是小写字母或数字",
),
task: user(
Type.String(ABSOLUTE_PATH),
"用哪个任务:任务目录的绝对路径。指标、方向、ε、时限等任务事实写在任务目录里",
),
models: user(
Type.Object(
{
@@ -172,7 +182,10 @@ export const ResearchConfig = Type.Object(
),
"轻量检查的次数与时长上限:只用来检查语法、接口和极小的样例,不能借它跑正式规模的求解",
),
promptDir: method(Type.String(TEXT), "提示词目录:各角色的角色提示词和各类任务消息放在这里"),
promptDir: method(
Type.String(ABSOLUTE_PATH),
"提示词目录的绝对路径:各角色的角色提示词和各类任务消息放在这里",
),
devEntry: method(
Type.Boolean(),
"开发入口(方法.md 第九部分):正式研究写 false;开发调试时才写 true,打开过的研究不能当验收证据",