refactor(consult): chat binds the method's truth boundaries, not the engine manual (BUG-1256 step 2)
Independent Staging Quality Gate / validate (push) Successful in 14m35s
Independent Staging Quality Gate / publish (push) Successful in 3m37s

The method block bound into every chat turn quoted most of SKILL.md (benchmark
scores, CLI flags, oracle queues, file paths) plus the shared method, whose
output contract asks for JSON verdicts, A/B/C/D confidence, audit tables, raw
data and web verification - the opposite of the chat shape. Chat now quotes
only the truth-boundary sections and adds CHAT_METHOD_BOUNDARIES for the
limits from the dropped sections it still has to keep. SKILL.md and the shared
method file are unchanged for reports and skill_read.

Natal system prompt 55,242 -> 26,591 characters. DeepSeek A/B on public
golden charts (flash 10 questions): input 24,560 -> 14,025 tokens, 37.8 ->
30.3 s, no regression in automatic checks or reading; a high-rigor request
still says plainly that no external check was done. Full suite 4970 / fail
24, identical to b8385adb; build keeps / Static.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
This commit is contained in:
Jesse_Chen
2026-10-07 08:48:04 +08:00
co-authored by Claude Opus 5.5
parent 6a372bc3a8
commit c76fdda7ab
6 changed files with 142 additions and 50 deletions
+44 -36
View File
@@ -5,7 +5,6 @@ import {
resolveLiveJyotishSkill,
resolveLiveJyotishSkillRuntimePath,
} from "../lib/skill-package-registry.ts";
import { sharedConsultationMethodMarkdown } from "../lib/consultation-methodology.ts";
const skill = resolveLiveJyotishSkill();
@@ -18,24 +17,41 @@ export const jyotishSkillPackage = skill;
export const jyotishSkillRuntimePath = resolveLiveJyotishSkillRuntimePath(skill);
/**
* Headings from the commercial SKILL.md that govern answering a natal chart.
* The rest of that file is a local-agent / maintainer manual (CLI indexes,
* construction rules, celebrity case catalogs) and stays in the package for
* skill_read rather than being stuffed into every consultation prompt.
* What a chat answer takes from the commercial SKILL.md (BUG-1256). The file is
* mostly the engine and report manual: benchmark scores, CLI flags, oracle
* queues, file paths, and output contracts written for reports ("only output
* verdict + confidence + audit + raw evidence", "every transit prediction gives
* a period + action + [A]/[B]/[C]", "every reading must be web-verified").
* Bound into every chat turn, those contradicted the chat shape and made up
* most of a 55k-character system prompt. Chat keeps the truth-boundary
* sections only; the full file stays in the package for skill_read.
* `body: true` keeps the section's own text (blockquotes and file-reading
* lists dropped); `subsections` are the `###` parts kept under it.
*/
const RUNTIME_METHOD_HEADINGS = [
"商业运行时路由",
"关联技法完整调取",
"强制工作流",
"五层硬约束",
"强制规则",
"当前最硬的未闭环点",
"核心方法论",
"强制规范速查",
"注意事项",
const CHAT_METHOD_SELECTION = [
{ section: "商业运行时路由", body: true, subsections: ["Skill truth overlay 硬边界", "Ashtottari"] },
{ section: "关联技法完整调取", body: false, subsections: ["P0/P1 观察层"] },
{ section: "核心方法论", body: false, subsections: ["三层验证法"] },
] as const;
const DROPPED_RUNTIME_SUBHEADINGS = ["开源复用边界冻结"] as const;
/** Lines of a kept section body that only tell an agent which files to open. */
function isFilePointerLine(line: string): boolean {
return line.startsWith(">") || /^\d+\.\s/.test(line) || line.includes("渐进读取");
}
/**
* The boundaries from the dropped sections that a chat answer still has to
* keep, in the product's own words (BUG-1256). Outside the verbatim skill
* block, so the excerpt stays a faithful quote of the live file.
*/
export const CHAT_METHOD_BOUNDARIES = `<chat-method-boundaries>
以下是方法书里没有摘进来的章节中,对话回答仍要守的边界(全文仍在技能目录里):
- 判断顺序:先看本命有没有这件事的底子,再看大运有没有把它激活,再看够不够落到现实,最后才谈时间;不从一段大运或一次行运直接跳到「会发生」。
- 谈应期至少要 Vimshottari 与 Narayana 两套大运同向;两套冲突时降低把握,不给确定说法。
- 大运起止日期只引用卡上给出的,不自己推算;大运精细边界和 Shadbala 绝对值并未全部完成外部校准,说日期和力量时不说「已校准」「精确到」。
- 不宣称全球第一、所有技法已封顶或完美精度。
- 用户要求「高严谨、拉满三大引擎、联网核验、拿真实案例验证」时:这次对话里没有做外部引擎比对、全网资料核验和真实案例校准,照实说明,结论降一级,不假装做过。
</chat-method-boundaries>`;
function publishedSkillBody(): string {
const raw = readFileSync(resolve(skill.resolvedPath, "SKILL.md"), "utf8");
@@ -43,21 +59,6 @@ function publishedSkillBody(): string {
return (frontmatter ? raw.slice(frontmatter[0].length) : raw).trim();
}
function dropSubheadings(section: string, dropped: readonly string[]): string {
const lines = section.split("\n");
const kept: string[] = [];
let skipping = false;
for (const line of lines) {
if (line.startsWith("### ")) {
skipping = dropped.some((heading) => line.includes(heading));
} else if (line.startsWith("## ")) {
skipping = false;
}
if (!skipping) kept.push(line);
}
return kept.join("\n").trim();
}
/**
* Mastra answers a skill activation with the entrypoint *and* a flat listing of
* every file in the package, which for this package measured 129,651 bytes: the
@@ -97,8 +98,17 @@ function boundMethod(): string {
const kept = sections.flatMap((section) => {
const heading = section[0] ?? "";
if (!RUNTIME_METHOD_HEADINGS.some((wanted) => heading.includes(wanted))) return [];
const text = dropSubheadings(section.join("\n"), DROPPED_RUNTIME_SUBHEADINGS);
const wanted = CHAT_METHOD_SELECTION.find((entry) => heading.includes(entry.section));
if (!wanted) return [];
const parts: string[][] = [[]];
for (const line of section) {
if (line.startsWith("### ")) parts.push([line]);
else parts[parts.length - 1]!.push(line);
}
const [own = [], ...subsections] = parts;
const body = wanted.body ? own.filter((line) => !isFilePointerLine(line)) : own.slice(0, 1);
const keptSubsections = subsections.filter((part) => wanted.subsections.some((name) => (part[0] ?? "").includes(name)));
const text = [...body, ...keptSubsections.flat()].join("\n").replace(/\n{3,}/g, "\n\n").trim();
return text.length > 0 ? [text] : [];
});
@@ -118,9 +128,7 @@ function methodBlock(reportSkeleton: string) {
${BOUND_METHOD_MARKER}
${boundMethod()}
</jyotish-skill>
<jyotish-shared-method>
${sharedConsultationMethodMarkdown()}
</jyotish-shared-method>`;
${CHAT_METHOD_BOUNDARIES}`;
}
/** Method body + marker, without the natal Level 2 report skeleton. Window agents use this. */
+30 -14
View File
@@ -59,13 +59,20 @@ test("the bound method is the runtime excerpt, not the maintainer manual", () =>
const body = liveSkillBody();
const bound = boundMethodText();
for (const heading of ["商业运行时路由", "关联技法完整调取", "强制工作流", "五层硬约束", "核心方法论", "注意事项", "commercial_skill_truth_overlay"]) {
// 原值: 摘录含「商业运行时路由 / 关联技法完整调取 / 强制工作流 / 五层硬约束 / 核心方法论 / 注意事项 / commercial_skill_truth_overlay」与「全谱系真实调用」「静态分析10步」
// 新值: 只含诚实边界几节(运行时路由正文、truth overlay、Ashtottari、P0/P1 观察层、三层验证法);工程与报告用的章节留在技能目录
// 原因: 2026-10-07 聊天提示词精简(BUG-1256 第二步):方法书只摘诚实边界几节,工程记录、跑分、脚本、文件路径和与聊天形状冲突的输出合同不再进每轮系统提示
for (const heading of ["商业运行时路由", "关联技法完整调取", "核心方法论"]) {
assert.ok(body.includes(heading), heading);
assert.ok(bound.includes(heading), heading);
}
for (const phrase of ["全谱系真实调用", "P0/P1 观察层", "网页对话的技法审计表由界面折叠展示", "静态分析10步"]) {
for (const phrase of ["P0/P1 观察层", "网页对话的技法审计表由界面折叠展示", "候选出生时间不得写成 confirmed", "Skill truth overlay 硬边界", "三层验证法"]) {
assert.ok(bound.includes(phrase), phrase);
}
for (const heading of ["强制工作流", "五层硬约束", "注意事项", "强制规范速查", "当前最硬的未闭环点", "MEVG 强制外部验证门控", "Transit Actionable Output", "全谱系真实调用", "静态分析10步"]) {
assert.ok(body.includes(heading), heading);
assert.equal(bound.includes(heading), false, heading);
}
for (const heading of ["施工判断原则", "冲顶路线", "37大子命令", "验证与错题体系", "开源复用边界冻结"]) {
assert.ok(body.includes(heading), heading);
assert.equal(bound.includes(heading), false, heading);
@@ -296,16 +303,25 @@ test("only chart-answering agents bind the method", () => {
}
});
test("the system block binds the shared method sections verbatim from the package", () => {
const shared = sharedConsultationMethodMarkdown();
const opened = jyotishSkillMethodBlock.indexOf("<jyotish-shared-method>");
const closed = jyotishSkillMethodBlock.indexOf("</jyotish-shared-method>");
assert.ok(opened >= 0 && closed > opened);
const boundShared = jyotishSkillMethodBlock.slice(
opened + "<jyotish-shared-method>".length,
closed,
).trim();
assert.equal(boundShared, shared.trim());
assert.match(shared, /Full-Spectrum Invocation Contract/);
assert.ok(shared.includes(readFileSync(resolve(jyotishSkillPackage.resolvedPath, "references/event_judgment_skeleton.md"), "utf8").trim()));
test("chat binds the method boundaries, not the report-shaped shared method (BUG-1256)", async () => {
// 原值: 系统块逐字绑定共享方法(Full-Spectrum Invocation Contract + event_judgment_skeleton.md)
// 新值: 不再绑定共享方法;改为 <chat-method-boundaries>,用产品自己的话保留聊天仍要守的边界
// 原因: 2026-10-07 聊天提示词精简(BUG-1256 第二步):方法书只摘诚实边界几节,工程记录、跑分、脚本、文件路径和与聊天形状冲突的输出合同不再进每轮系统提示;共享方法要求输出 JSON verdict + A/B/C/D 置信度 + 审计表 + 原始数据 + 联网验证,与聊天形状冲突
const { CHAT_METHOD_BOUNDARIES } = await import("../src/mastra/skill-binding.ts");
for (const block of [jyotishSkillMethodBlock, jyotishSkillMethodCoreBlock]) {
assert.equal(block.includes("<jyotish-shared-method>"), false);
assert.equal(block.includes("Full-Spectrum Invocation Contract"), false);
assert.ok(block.includes(CHAT_METHOD_BOUNDARIES));
}
for (const kept of ["不从一段大运或一次行运直接跳到「会发生」", "Vimshottari 与 Narayana 两套大运同向", "大运起止日期只引用卡上给出的", "不宣称全球第一", "没有做外部引擎比对、全网资料核验和真实案例校准,照实说明"]) {
assert.ok(CHAT_METHOD_BOUNDARIES.includes(kept), kept);
}
// The shared method is still the package's own file, for reports and skill_read.
assert.match(sharedConsultationMethodMarkdown(), /Full-Spectrum Invocation Contract/);
});
test("the chat method block stays small (BUG-1256)", () => {
// It was 25,872 characters, most of the 55k natal system prompt.
assert.ok(jyotishSkillMethodBlock.length < 4_000, `${jyotishSkillMethodBlock.length}`);
assert.ok(jyotishSkillMethodCoreBlock.length < 4_000, `${jyotishSkillMethodCoreBlock.length}`);
});