76 lines
3.7 KiB
JavaScript
76 lines
3.7 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { loadPersonalSkillRegistry, planPersonalSkillRun, resolvePersonalSkill } from "./personal-skill-runtime.mjs";
|
|
|
|
const skillId = "JZ-CX-NARRATIVE-DECONSTRUCTION-001";
|
|
const base = {
|
|
event_id: "CASE-001",
|
|
skill_id: skillId,
|
|
channel_id: "SYS-JZ",
|
|
human_actor_id: "TCS-GL-0008∞",
|
|
persona_id: "PER-CX001",
|
|
source: "HUMAN_DIRECT_LANGUAGE",
|
|
input: { work_id: "BOOK-1", chapter_id: "CH-1", source_text: "第一章正文", analysis_goal: "节奏" }
|
|
};
|
|
|
|
test("registry keeps every compiled skill private by default", () => {
|
|
const registry = loadPersonalSkillRegistry();
|
|
assert.equal(registry.default_visibility, "PRIVATE");
|
|
for (const entry of registry.skills) {
|
|
const { pack } = resolvePersonalSkill(entry.skill_id, registry);
|
|
assert.equal(pack.visibility, "PRIVATE", entry.skill_id);
|
|
assert.equal(pack.share_state, "NOT_AUTHORIZED", entry.skill_id);
|
|
assert.equal(pack.marketplace_state, "NOT_AUTHORIZED", entry.skill_id);
|
|
assert.equal(pack.owner_human_anchor, entry.owner_human_anchor, entry.skill_id);
|
|
assert.equal(pack.custodian_persona_id, entry.custodian_persona_id, entry.skill_id);
|
|
assert.ok(pack.provenance && pack.input_schema && pack.deterministic_core, entry.skill_id);
|
|
assert.ok(pack.optional_semantic && pack.persona_judgment && pack.output_schema, entry.skill_id);
|
|
assert.ok(Array.isArray(pack.stop_conditions), entry.skill_id);
|
|
assert.ok(pack.experience_writeback, entry.skill_id);
|
|
}
|
|
});
|
|
|
|
test("personal skill cannot run from another channel or persona", () => {
|
|
assert.equal(planPersonalSkillRun({ ...base, channel_id: "SYS-AW" }).status, "BLOCK");
|
|
assert.equal(planPersonalSkillRun({ ...base, persona_id: "PER-ZQ001" }).status, "BLOCK");
|
|
});
|
|
|
|
test("deterministic core asks for missing exact input before any model", () => {
|
|
const result = planPersonalSkillRun({ ...base, input: { work_id: "BOOK-1" } });
|
|
assert.equal(result.status, "NEEDS_INPUT");
|
|
assert.ok(result.reasons.includes("MISSING_INPUT:chapter_id"));
|
|
});
|
|
|
|
test("semantic ambiguity is explicit instead of guessed", () => {
|
|
const result = planPersonalSkillRun(base);
|
|
assert.equal(result.status, "MODEL_REQUIRED");
|
|
assert.equal(result.external_effect, "NONE");
|
|
});
|
|
|
|
test("model candidates cannot replace persona judgment", () => {
|
|
const semantic_candidates = {
|
|
scene_functions: [], emotion_and_pacing: [], hooks_and_conflicts: [], transferable_patterns: []
|
|
};
|
|
const result = planPersonalSkillRun({ ...base, semantic_candidates });
|
|
assert.equal(result.status, "PERSONA_JUDGMENT_REQUIRED");
|
|
});
|
|
|
|
test("a complete plan remains non-executing and routes external writes through the existing receipt gate", () => {
|
|
const semantic_candidates = {
|
|
scene_functions: [], emotion_and_pacing: [], hooks_and_conflicts: [], transferable_patterns: []
|
|
};
|
|
const result = planPersonalSkillRun({ ...base, semantic_candidates, persona_judgment: { decision: "ACCEPT_FOR_CURRENT_CASE", why: "evidence linked" } });
|
|
assert.equal(result.status, "DETERMINISTIC_READY");
|
|
assert.equal(result.external_effect, "NONE");
|
|
assert.equal(result.external_execution_route, "GH-CHANNEL-RECEIPT-0001");
|
|
});
|
|
|
|
test("sharing, publication, and marketplace listing are never auto-compiled", () => {
|
|
for (const intent of ["SHARE", "PUBLISH", "MARKETPLACE_LIST"]) {
|
|
const result = planPersonalSkillRun({ ...base, intent });
|
|
assert.equal(result.status, "AUTHORIZATION_REQUIRED");
|
|
assert.ok(result.reasons.includes("NO_RELEASE_COMPILED_BY_THIS_PLANNER"));
|
|
}
|
|
const outsider = planPersonalSkillRun({ ...base, intent: "SHARE", human_actor_id: "ICE-GL∞" });
|
|
assert.ok(outsider.reasons.includes("ONLY_OWNER_HUMAN_MAY_DECIDE_RELEASE"));
|
|
});
|