docs(config): 添加AI代理编码行为规范和配置验证工具测试

- 定义了代码风格、注释规则和文档要求
- 规定了测试覆盖范围和安全编码准则
- 实现了配置模式解析和验证功能
- 添加了常量值比较和枚举校验逻辑
- 集成了数字范围和字符串长度验证
- 支持对象数组和嵌套结构验证
This commit is contained in:
GeWuYou 2026-04-10 16:50:39 +08:00
parent e28a1e4ecd
commit 8287bf37fc
2 changed files with 278 additions and 10 deletions

View File

@ -14,6 +14,10 @@ All AI agents and contributors must follow these rules when writing, reviewing,
`git.exe`) instead of the Linux `git` binary.
- If a Git command in WSL fails with a worktree-style “not a git repository” path translation error, rerun it with the
Windows Git executable and treat that as the repository-default Git path for the rest of the task.
- If the shell does not currently resolve `git.exe` to the host Windows Git installation, prepend that installation's
command directory to `PATH` and reset shell command hashing for the current session before continuing.
- After resolving the host Windows Git path, prefer an explicit session-local binding for subsequent commands so the
shell does not fall back to Linux `/usr/bin/git` later in the same WSL session.
## Commenting Rules (MUST)

View File

@ -65,7 +65,7 @@ test("parseSchemaContent should capture nested objects and object-array metadata
assert.equal(schema.properties.phases.items.properties.wave.type, "integer");
});
test("parseSchemaContent should capture const metadata for scalar, object, and array nodes", () => {
test("parseSchemaContent should capture const metadata for scalar, object, array, integer, and boolean nodes", () => {
const schema = parseSchemaContent(`
{
"type": "object",
@ -78,19 +78,61 @@ test("parseSchemaContent should capture const metadata for scalar, object, and a
"type": "object",
"properties": {
"gold": { "type": "integer" },
"currency": { "type": "string" }
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
"const": {
"gold": 10,
"currency": "coin"
"gold": 100,
"items": [
"potion",
"sword"
]
}
},
"dropItemIds": {
"tags": {
"type": "array",
"const": ["potion", "gem"],
"const": ["daily", "quest"],
"items": {
"type": "string"
}
},
"maxAttempts": {
"type": "integer",
"const": 3
},
"allowRetry": {
"type": "boolean",
"const": true
}
}
}
`);
const reorderedSchema = parseSchemaContent(`
{
"type": "object",
"properties": {
"reward": {
"type": "object",
"properties": {
"gold": { "type": "integer" },
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
"const": {
"items": [
"potion",
"sword"
],
"gold": 100
}
}
}
}
@ -98,10 +140,25 @@ test("parseSchemaContent should capture const metadata for scalar, object, and a
assert.equal(schema.properties.rarity.constValue, "common");
assert.equal(schema.properties.rarity.constDisplayValue, "\"common\"");
assert.match(schema.properties.reward.constValue, /"currency":"coin"/u);
assert.match(schema.properties.reward.constDisplayValue, /"currency":"coin"/u);
assert.equal(schema.properties.dropItemIds.constValue, "[\"potion\",\"gem\"]");
assert.equal(schema.properties.dropItemIds.constDisplayValue, "[\"potion\",\"gem\"]");
assert.ok(schema.properties.rarity.constComparableValue);
assert.equal(schema.properties.reward.constValue, "{\"gold\":100,\"items\":[\"potion\",\"sword\"]}");
assert.equal(schema.properties.reward.constDisplayValue, "{\"gold\":100,\"items\":[\"potion\",\"sword\"]}");
assert.equal(
schema.properties.reward.constComparableValue,
reorderedSchema.properties.reward.constComparableValue);
assert.equal(schema.properties.tags.constValue, "[\"daily\",\"quest\"]");
assert.equal(schema.properties.tags.constDisplayValue, "[\"daily\",\"quest\"]");
assert.ok(schema.properties.tags.constComparableValue);
assert.equal(schema.properties.maxAttempts.constValue, "3");
assert.equal(schema.properties.maxAttempts.constDisplayValue, "3");
assert.equal(schema.properties.maxAttempts.constComparableValue, "integer:1:3");
assert.equal(schema.properties.allowRetry.constValue, "true");
assert.equal(schema.properties.allowRetry.constDisplayValue, "true");
assert.equal(schema.properties.allowRetry.constComparableValue, "boolean:4:true");
});
test("parseSchemaContent should preserve empty-string const raw and display metadata", () => {
@ -291,6 +348,139 @@ rarity: rare
assert.match(diagnostics[0].message, /constant value "common"|固定值 "common"/u);
});
test("validateParsedConfig should accept scalar, object, array, integer, and boolean const matches", () => {
const schema = parseSchemaContent(`
{
"type": "object",
"properties": {
"rarity": {
"type": "string",
"const": "common"
},
"reward": {
"type": "object",
"properties": {
"gold": { "type": "integer" },
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
"const": {
"gold": 100,
"items": [
"potion",
"sword"
]
}
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"const": [
"daily",
"quest"
]
},
"maxAttempts": {
"type": "integer",
"const": 3
},
"allowRetry": {
"type": "boolean",
"const": true
}
}
}
`);
const yaml = parseTopLevelYaml(`
rarity: common
reward:
gold: 100
items:
- potion
- sword
tags:
- daily
- quest
maxAttempts: 3
allowRetry: true
`);
assert.deepEqual(validateParsedConfig(schema, yaml), []);
});
test("validateParsedConfig should normalize object const comparisons but keep array const order", () => {
const schema = parseSchemaContent(`
{
"type": "object",
"properties": {
"reward": {
"type": "object",
"properties": {
"gold": { "type": "integer" },
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
"const": {
"gold": 100,
"items": [
"potion",
"sword"
]
}
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"const": [
"daily",
"quest"
]
}
}
}
`);
const normalizedYaml = parseTopLevelYaml(`
reward:
items:
- potion
- sword
gold: 100
tags:
- daily
- quest
`);
const arrayOrderMismatchYaml = parseTopLevelYaml(`
reward:
items:
- potion
- sword
gold: 100
tags:
- quest
- daily
`);
assert.deepEqual(validateParsedConfig(schema, normalizedYaml), []);
const diagnostics = validateParsedConfig(schema, arrayOrderMismatchYaml);
assert.equal(diagnostics.length, 1);
assert.match(diagnostics[0].message, /tags/u);
assert.match(diagnostics[0].message, /constant value \["daily","quest"\]|固定值 \["daily","quest"\]/u);
});
test("validateParsedConfig should report object and array const mismatches", () => {
const schema = parseSchemaContent(`
{
@ -333,6 +523,60 @@ dropItemIds:
assert.match(diagnostics[1].message, /dropItemIds/u);
});
test("validateParsedConfig should cover integer and boolean const scalar normalization and mismatches", () => {
const schema = parseSchemaContent(`
{
"type": "object",
"properties": {
"maxAttempts": {
"type": "integer",
"const": 3
},
"allowRetry": {
"type": "boolean",
"const": true
}
}
}
`);
const normalizedYaml = parseTopLevelYaml(`
maxAttempts: "3"
allowRetry: "true"
`);
const integerMismatchYaml = parseTopLevelYaml(`
maxAttempts: 3.5
allowRetry: true
`);
const booleanConstMismatchYaml = parseTopLevelYaml(`
maxAttempts: 3
allowRetry: false
`);
const booleanTypeMismatchYaml = parseTopLevelYaml(`
maxAttempts: 3
allowRetry: 0
`);
assert.deepEqual(validateParsedConfig(schema, normalizedYaml), []);
const integerDiagnostics = validateParsedConfig(schema, integerMismatchYaml);
assert.equal(integerDiagnostics.length, 1);
assert.match(integerDiagnostics[0].message, /maxAttempts/u);
assert.match(integerDiagnostics[0].message, /integer|整数/u);
const booleanConstDiagnostics = validateParsedConfig(schema, booleanConstMismatchYaml);
assert.equal(booleanConstDiagnostics.length, 1);
assert.match(booleanConstDiagnostics[0].message, /allowRetry/u);
assert.match(booleanConstDiagnostics[0].message, /constant value true|固定值 true/u);
const booleanTypeDiagnostics = validateParsedConfig(schema, booleanTypeMismatchYaml);
assert.equal(booleanTypeDiagnostics.length, 1);
assert.match(booleanTypeDiagnostics[0].message, /allowRetry/u);
assert.match(booleanTypeDiagnostics[0].message, /boolean|布尔/u);
});
test("validateParsedConfig should report numeric range and string length mismatches", () => {
const schema = parseSchemaContent(`
{
@ -1261,6 +1505,26 @@ test("createSampleConfigYaml should preserve empty-string scalar const values",
assert.match(sample, /^name: ""$/mu);
});
test("createSampleConfigYaml should prefer scalar const values over defaults", () => {
const schema = parseSchemaContent(`
{
"type": "object",
"properties": {
"rarity": {
"type": "string",
"const": "common",
"default": "rare"
}
}
}
`);
const sample = createSampleConfigYaml(schema);
assert.match(sample, /^rarity: common$/mu);
assert.ok(!/^rarity: rare$/mu.test(sample));
});
test("parseBatchArrayValue should keep comma-separated batch editing behavior", () => {
assert.deepEqual(parseBatchArrayValue(" potion, bomb , ,elixir "), ["potion", "bomb", "elixir"]);
});