fix(consult): read the live skill tree instead of a hash-pinned snapshot
Independent Staging Quality Gate / validate (push) Successful in 9m18s
Independent Staging Quality Gate / publish (push) Has been cancelled

Manual SKILL.md updates were blocked by registry sha256 and a byte-equal versions/ gate. Consult now loads the operator-maintained tree; rectification and personal-report stay hashed.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-19 10:25:55 +08:00
co-authored by Cursor
parent 233c728176
commit d35828e76d
14 changed files with 315 additions and 109 deletions
+16 -13
View File
@@ -1,6 +1,6 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { existsSync, readFileSync, realpathSync } from "node:fs";
import { join, resolve } from "node:path";
import test from "node:test";
import { Agent } from "@mastra/core/agent";
import {
@@ -17,9 +17,9 @@ function probeAgent(options: Record<string, unknown>) {
return new Agent({ id: "skill-binding-probe", name: "Probe", model, instructions: "x", ...options } as never);
}
function publishedSkillBody() {
const published = readFileSync(resolve(jyotishSkillPackage.resolvedPath, "SKILL.md"), "utf8");
return published.slice(published.indexOf("---", 3) + 4).trim();
function liveSkillBody() {
const live = readFileSync(resolve(jyotishSkillPackage.resolvedPath, "SKILL.md"), "utf8");
return live.slice(live.indexOf("---", 3) + 4).trim();
}
function boundMethodText() {
@@ -29,20 +29,23 @@ function boundMethodText() {
return jyotishSkillMethodBlock.slice(jyotishSkillMethodBlock.indexOf("\n", opened) + 1, closed).trim();
}
test("the method the model follows is the published one, not the working tree's", () => {
const body = publishedSkillBody();
test("the method the model follows is the live skill, not a hashed snapshot", () => {
const body = liveSkillBody();
const bound = boundMethodText();
assert.match(jyotishSkillMethodBlock, new RegExp(`version="${jyotishSkillPackage.version}"`));
// Every bound line still comes from the hash-checked package. The route into
// the loader is that same package, so skill_read can only reach what the
// registry hash covers.
assert.match(jyotishSkillMethodBlock, new RegExp(`name="${jyotishSkillPackage.name}"`));
assert.doesNotMatch(jyotishSkillMethodBlock, /<jyotish-skill name="[^"]+" version="/);
for (const line of bound.split("\n")) {
if (line.length === 0) continue;
assert.ok(body.includes(line), line.slice(0, 80));
}
assert.ok(jyotishSkillRuntimePath.includes(jyotishSkillPackage.sha256));
assert.ok(jyotishSkillRuntimePath.endsWith(jyotishSkillPackage.name));
assert.equal(jyotishSkillRuntimePath.includes("/versions/"), false);
assert.equal(existsSync(join(jyotishSkillRuntimePath, "versions")), false);
assert.equal(
realpathSync(join(jyotishSkillRuntimePath, "SKILL.md")),
realpathSync(join(jyotishSkillPackage.resolvedPath, "SKILL.md")),
);
});
test("the bound method is the runtime excerpt, not the maintainer manual", () => {
@@ -50,7 +53,7 @@ test("the bound method is the runtime excerpt, not the maintainer manual", () =>
// answering contract and the repo-construction notes. Binding the whole
// file is what pushed Mastra past its 500-line warning; the excerpt keeps
// the contract and drops the notes.
const body = publishedSkillBody();
const body = liveSkillBody();
const bound = boundMethodText();
for (const heading of ["商业运行时路由", "关联技法完整调取", "强制工作流", "五层硬约束", "核心方法论", "注意事项", "commercial_skill_truth_overlay"]) {