/** * Unit Test — `_base` 骨架必须齐全(28 号设计 验收 6、二轮修复 问题 2) * * 锁的是一条**只有真跑一次招聘才会显形**的静默失效: * `hire()` 雇一个仓库里没有的角色时,从 `_base` 复制骨架毕业出一份新本体,随即用 * `missingBodyDefinition()`(agent-locator)判它齐不齐全 —— 缺一份就停在 status='draft', * 不许被任何公司雇走。而 `_base` 一度只带 agent.md + config.json,于是**每一个**新本体 * 出生即 draft,补齐只能靠 agent-creator 那次 fire-and-forget 的自调用(已知缺陷:单会话锁, * 连招时只有第一个被填充)。症状是「招聘成功了,人却永远上不了岗」,一处报错都没有。 * * 两条断言分工: * ① 逐份查字节数 —— 三份定义都得真有内容,缺一份新本体就少一条提示词到达路径; * 200 字节是「写了点东西」的下限,不是质量标准; * ② 拿**闸门本人**(missingBodyDefinition)判 `_base`:它必须判**三份全缺**。 * * ⚠️ ② 的方向是二轮修复反过来的(原来断言 `[]`)。补齐 `_base` 与「非空即齐全」的旧判据 * 凑在一起会把闸门整个抵消:骨架一拷完就转正,新角色拿着通用 Base Agent 文本直接上岗, * 第二家公司还会把它当可复用本体雇走。所以 `_base` 的三份定义里都埋了 * `BASE_SKELETON_SENTINEL`,「齐全」的判据变成「有内容 **且** 不再是骨架」—— * `_base` 自己**永远**是骨架,也就永远不该被判成齐全。详见 base-graduation-gate.test.js。 */ import { describe, it, expect } from 'vitest'; import fs from 'fs'; import path from 'path'; import { missingBodyDefinition, BASE_SKELETON_SENTINEL } from '../../services/agent-locator.js'; const BASE_DIR = path.resolve(__dirname, '../../../system/agents/_base'); const DEFINITION_FILES = [ 'agent.md', 'soul.md', path.join('.claude', 'skills', 'main_skill', 'SKILL.md'), ]; describe('_base 元 agent 骨架(新本体的出生起点)', () => { it.each(DEFINITION_FILES)('_base/%s 存在且不是占位(去空白后 > 200 字节)', (rel) => { const p = path.join(BASE_DIR, rel); expect(fs.existsSync(p), `${p} 不存在`).toBe(true); expect(fs.readFileSync(p, 'utf-8').trim().length).toBeGreaterThan(200); }); it.each(DEFINITION_FILES)('_base/%s 带着骨架哨兵(人一眼看得见「这份还没填」)', (rel) => { expect(fs.readFileSync(path.join(BASE_DIR, rel), 'utf-8')).toContain(BASE_SKELETON_SENTINEL); }); it('闸门直接判 _base:三份**全部**算缺 —— 骨架不是定义', () => { // 文件全在、内容也不少(上一条断言过),旧的「非空即齐全」判据会把它们全部放行。 expect(missingBodyDefinition('_base')).toEqual(DEFINITION_FILES); }); });