mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-28 06:23:01 +00:00
fix(doctor): derive host skill manifests (SUP-3488) (#2961)
This commit is contained in:
+10
-11
@@ -4345,7 +4345,7 @@ export async function buildChecks(
|
||||
|
||||
// 2. Skill conformance (SKILL group — gated)
|
||||
if (scope === 'all' && skillsDir) {
|
||||
const conformanceResult = checkSkillConformance(skillsDir);
|
||||
const conformanceResult = skillConformanceCheck(skillsDir);
|
||||
checks.push(conformanceResult);
|
||||
}
|
||||
|
||||
@@ -7432,15 +7432,13 @@ function printAutoFixReport(report: AutoFixReport, dryRun: boolean, jsonOutput:
|
||||
|
||||
|
||||
/** Quick skill conformance check — frontmatter + required sections */
|
||||
function checkSkillConformance(skillsDir: string): Check {
|
||||
const manifestPath = join(skillsDir, 'manifest.json');
|
||||
if (!existsSync(manifestPath)) {
|
||||
return { name: 'skill_conformance', status: 'warn', message: 'manifest.json not found' };
|
||||
}
|
||||
|
||||
export function skillConformanceCheck(skillsDir: string): Check {
|
||||
try {
|
||||
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
||||
const skills = manifest.skills || [];
|
||||
// Host workspaces are allowed to omit a gbrain-specific manifest. Keep
|
||||
// conformance aligned with resolver_health and skill_brain_first by using
|
||||
// the canonical fallback that derives entries from direct SKILL.md files.
|
||||
const manifest = loadOrDeriveManifest(skillsDir);
|
||||
const skills = manifest.skills;
|
||||
let passing = 0;
|
||||
const failing: string[] = [];
|
||||
|
||||
@@ -7460,7 +7458,8 @@ function checkSkillConformance(skillsDir: string): Check {
|
||||
}
|
||||
|
||||
if (failing.length === 0) {
|
||||
return { name: 'skill_conformance', status: 'ok', message: `${passing}/${skills.length} skills pass` };
|
||||
const derivedNote = manifest.derived ? ' (derived from SKILL.md files)' : '';
|
||||
return { name: 'skill_conformance', status: 'ok', message: `${passing}/${skills.length} skills pass${derivedNote}` };
|
||||
}
|
||||
return {
|
||||
name: 'skill_conformance',
|
||||
@@ -7468,7 +7467,7 @@ function checkSkillConformance(skillsDir: string): Check {
|
||||
message: `${passing}/${skills.length} pass. Failing: ${failing.join(', ')}`,
|
||||
};
|
||||
} catch {
|
||||
return { name: 'skill_conformance', status: 'warn', message: 'Could not parse manifest.json' };
|
||||
return { name: 'skill_conformance', status: 'warn', message: 'Could not load or derive skills manifest' };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { describe, test, expect, beforeAll, afterAll, beforeEach } from 'bun:test';
|
||||
import { mkdirSync, rmSync, writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { tmpdir } from 'os';
|
||||
|
||||
describe('doctor command', () => {
|
||||
test('doctor module exports runDoctor', async () => {
|
||||
@@ -120,6 +123,24 @@ describe('doctor command', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('skill conformance derives a valid host manifest when manifest.json is absent', async () => {
|
||||
const { skillConformanceCheck } = await import('../src/commands/doctor.ts');
|
||||
const skillsDir = join(tmpdir(), `gbrain-doctor-skills-${crypto.randomUUID()}`);
|
||||
mkdirSync(join(skillsDir, 'host-only'), { recursive: true });
|
||||
writeFileSync(
|
||||
join(skillsDir, 'host-only', 'SKILL.md'),
|
||||
'---\nname: host-only\ndescription: host-owned skill\n---\n\n# Host-only\n',
|
||||
);
|
||||
try {
|
||||
const check = skillConformanceCheck(skillsDir);
|
||||
expect(check).toMatchObject({ name: 'skill_conformance', status: 'ok' });
|
||||
expect(check.message).toContain('1/1 skills pass');
|
||||
expect(check.message).toContain('derived from SKILL.md files');
|
||||
} finally {
|
||||
rmSync(skillsDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
// v0.31.2 — facts_extraction_health check added in PR1 commit 12.
|
||||
// Reads ingest_log rows with source_type='facts:absorb' (written by
|
||||
// writeFactsAbsorbLog from src/core/facts/absorb-log.ts), groups by
|
||||
|
||||
Reference in New Issue
Block a user