zhuyuan-ice-gl-zy001/ZCODE-DEV-20260824/host-adapters/zcode-task-anchor-gate.mjs

183 lines
9.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
export const DEFAULT_ANCHOR_ROOT = "/Volumes/JZAO/HoloLake/persona-runtime/task-language-anchors/zcode";
export const DEFAULT_ZERO_CORE_TASK_ROOT = "/Volumes/JZAO/HoloLake/persona-runtime/task-language-anchors/zero-core";
export const FIRST_PATH_NAME = "00-冰朔自然语言瞄点";
export const STAGE_RECEIPT_DIR = "10-阶段回读回执";
export const ENGINEERING_MAP_PATH = "01-自然语言到工程阶段映射/current.tcs";
export const STAGE_TTL_MS = 30 * 60 * 1000;
export const STAGE_TOOL_BUDGET = 20;
function safeToken(value, fallback = "unknown") {
const token = String(value ?? "").trim().replaceAll(/[^a-zA-Z0-9._-]/gu, "-");
return token || fallback;
}
function sha256(value) { return crypto.createHash("sha256").update(value).digest("hex"); }
function readJson(file) {
try { return JSON.parse(fs.readFileSync(file, "utf8")); } catch { return null; }
}
function atomicWriteText(file, value) {
fs.mkdirSync(path.dirname(file), { recursive: true });
const temporary = `${file}.${process.pid}.${crypto.randomUUID()}.tmp`;
fs.writeFileSync(temporary, value, { encoding: "utf8", mode: 0o600 });
fs.renameSync(temporary, file);
}
function atomicWriteJson(file, value) { atomicWriteText(file, `${JSON.stringify(value, null, 2)}\n`); }
export function taskNumberFor(sessionId) {
return `CODEX-TASK-${safeToken(sessionId, "unknown-session")}`;
}
export function taskRootFor(sessionId, anchorRoot = process.env.GH_CODEX_TASK_ANCHOR_ROOT ?? DEFAULT_ANCHOR_ROOT) {
return path.join(anchorRoot, taskNumberFor(sessionId));
}
export function registerTaskAnchor({ sessionId, turnId, rawText, channelId, occurredAt = new Date().toISOString() }) {
if (!String(rawText ?? "").trim()) throw new Error("RAW_TEXT_REQUIRED");
const taskNumber = taskNumberFor(sessionId);
const taskRoot = taskRootFor(sessionId);
const anchorRoot = path.join(taskRoot, FIRST_PATH_NAME);
const manifestPath = path.join(taskRoot, "task.json");
const prior = readJson(manifestPath);
const rawTextSha256 = sha256(rawText);
const safeTurn = safeToken(turnId, crypto.randomUUID());
const duplicate = prior?.events?.find((item) => item.turn_id === safeTurn && item.raw_text_sha256 === rawTextSha256);
if (duplicate) return { ...duplicate, task_number: taskNumber, task_root: taskRoot, first_path: anchorRoot };
const sequence = String((prior?.event_count ?? 0) + 1).padStart(6, "0");
const stem = `${sequence}-${safeTurn}-${rawTextSha256.slice(0, 12)}`;
const rawPath = path.join(anchorRoot, `${stem}.raw.txt`);
const metadataPath = path.join(anchorRoot, `${stem}.json`);
atomicWriteText(rawPath, rawText);
const event = {
schema: "guanghu.codex-task-natural-language-anchor-event/v1",
task_number: taskNumber,
thread_id: safeToken(sessionId, "unknown-session"),
turn_id: safeTurn,
sequence: Number(sequence),
channel_id: channelId ?? "UNKNOWN",
source: "BINGSHUO_DIRECT_NATURAL_LANGUAGE",
raw_text_path: rawPath,
raw_text_sha256: rawTextSha256,
occurred_at: occurredAt,
};
atomicWriteJson(metadataPath, event);
const events = [...(prior?.events ?? []), { ...event, metadata_path: metadataPath }];
const first = events[0];
const current = events.at(-1);
const manifest = {
schema: "guanghu.codex-task-natural-language-anchor/v1",
task_number: taskNumber,
thread_id: safeToken(sessionId, "unknown-session"),
first_path: anchorRoot,
first_event_path: first.raw_text_path,
first_event_sha256: first.raw_text_sha256,
current_event_path: current.raw_text_path,
current_event_sha256: current.raw_text_sha256,
event_count: events.length,
events,
updated_at: occurredAt,
};
atomicWriteJson(manifestPath, manifest);
atomicWriteJson(path.join(anchorRoot, "first.json"), { task_number: taskNumber, raw_text_path: first.raw_text_path, raw_text_sha256: first.raw_text_sha256 });
atomicWriteJson(path.join(anchorRoot, "current.json"), { task_number: taskNumber, raw_text_path: current.raw_text_path, raw_text_sha256: current.raw_text_sha256 });
return { ...event, metadata_path: metadataPath, task_root: taskRoot, first_path: anchorRoot };
}
export function verifyTaskAnchor({ sessionId, turnId, controlEpoch, stage, deliverable }) {
if (!stage?.trim()) throw new Error("STAGE_REQUIRED");
if (!deliverable?.trim()) throw new Error("DELIVERABLE_REQUIRED");
const taskRoot = taskRootFor(sessionId);
const manifest = readJson(path.join(taskRoot, "task.json"));
if (manifest?.schema !== "guanghu.codex-task-natural-language-anchor/v1") throw new Error(`TASK_ANCHOR_MISSING:${taskRoot}`);
const firstRaw = fs.readFileSync(manifest.first_event_path, "utf8");
const currentRaw = fs.readFileSync(manifest.current_event_path, "utf8");
const engineeringMapPath = path.join(taskRoot, ENGINEERING_MAP_PATH);
const engineeringMap = fs.readFileSync(engineeringMapPath, "utf8");
const developmentLink = readJson(path.join(taskRoot, "development-task-link.json"));
let developmentTask = null;
let dualStartAnchors = [];
let developmentEngineeringMap = null;
if (developmentLink?.schema === "guanghu.codex-to-development-task-link/v1") {
developmentTask = readJson(developmentLink.development_task_manifest);
if (
developmentTask?.schema !== "guanghu.zero-core-development-task/v1" ||
developmentTask.current_host_thread !== safeToken(sessionId, "unknown-session")
) throw new Error("DEVELOPMENT_TASK_NOT_BOUND_TO_CURRENT_HOST_THREAD");
dualStartAnchors = developmentTask.dual_start_anchors.map((anchor) => {
const raw = fs.readFileSync(anchor.raw_text_path, "utf8");
if (sha256(raw) !== anchor.raw_text_sha256) throw new Error(`DEVELOPMENT_DUAL_START_HASH_MISMATCH:${anchor.role}`);
return { ...anchor, raw };
});
developmentEngineeringMap = fs.readFileSync(developmentTask.tcs_engineering_map, "utf8");
}
if (sha256(firstRaw) !== manifest.first_event_sha256) throw new Error("FIRST_ANCHOR_HASH_MISMATCH");
if (sha256(currentRaw) !== manifest.current_event_sha256) throw new Error("CURRENT_ANCHOR_HASH_MISMATCH");
const checkedAtUnixMs = Date.now();
const receipt = {
schema: "guanghu.codex-task-stage-anchor-read-receipt/v1",
task_number: manifest.task_number,
session_id: safeToken(sessionId, "unknown-session"),
turn_id: safeToken(turnId, "unknown-turn"),
control_epoch: controlEpoch,
stage: stage.trim(),
declared_deliverable: deliverable.trim(),
first_event_path: manifest.first_event_path,
first_event_sha256: manifest.first_event_sha256,
current_event_path: manifest.current_event_path,
current_event_sha256: manifest.current_event_sha256,
engineering_map_path: engineeringMapPath,
engineering_map_sha256: sha256(engineeringMap),
development_task_number: developmentTask?.development_task_number ?? null,
development_task_manifest: developmentLink?.development_task_manifest ?? null,
development_dual_start_sha256: dualStartAnchors.map((anchor) => anchor.raw_text_sha256),
development_engineering_map_path: developmentTask?.tcs_engineering_map ?? null,
development_engineering_map_sha256: developmentEngineeringMap ? sha256(developmentEngineeringMap) : null,
checked_at: new Date(checkedAtUnixMs).toISOString(),
checked_at_unix_ms: checkedAtUnixMs,
expires_at_unix_ms: checkedAtUnixMs + STAGE_TTL_MS,
capability_calls_used: 0,
capability_calls_max: STAGE_TOOL_BUDGET,
progress_semantics: "ONLY_TARGET_ARTIFACT_TEST_STATE_OR_ACCEPTANCE_ADVANCEMENT_COUNTS",
};
const receiptRoot = path.join(taskRoot, STAGE_RECEIPT_DIR);
const receiptPath = path.join(receiptRoot, `${receipt.turn_id}-${safeToken(stage)}-${checkedAtUnixMs}.json`);
atomicWriteJson(receiptPath, receipt);
atomicWriteJson(path.join(receiptRoot, "current.json"), { ...receipt, receipt_path: receiptPath });
return { manifest, receipt, receiptPath, firstRaw, currentRaw, engineeringMap, developmentTask, dualStartAnchors, developmentEngineeringMap };
}
function valueAfter(argv, name) { const index = argv.indexOf(name); return index >= 0 ? argv[index + 1] : null; }
function runCli() {
const [command, ...argv] = process.argv.slice(2);
if (command !== "verify") {
process.stderr.write("usage: codex-task-anchor-gate.mjs verify --session-id ID --turn-id ID --control-epoch ID --stage NAME --deliverable TEXT\n");
process.exit(2);
}
try {
const result = verifyTaskAnchor({
sessionId: valueAfter(argv, "--session-id"), turnId: valueAfter(argv, "--turn-id"),
controlEpoch: valueAfter(argv, "--control-epoch"), stage: valueAfter(argv, "--stage"), deliverable: valueAfter(argv, "--deliverable"),
});
process.stdout.write([
"CODEX_TASK_ANCHOR_STAGE_GATE PASS", `task_number=${result.manifest.task_number}`,
`development_task_number=${result.developmentTask?.development_task_number ?? "NONE"}`,
`first_path=${result.manifest.first_path}`, `receipt_path=${result.receiptPath}`,
...result.dualStartAnchors.flatMap((anchor, index) => [
`--- 零点原核正式开发双启动瞄点 ${index + 1}:${anchor.role} ---`,
anchor.raw,
]),
...(result.developmentEngineeringMap ? ["--- 零点原核正式开发TCS工程阶段与因果闭环 ---", result.developmentEngineeringMap] : []),
"--- 冰朔首条自然语言(原文) ---", result.firstRaw,
"--- 冰朔当前自然语言(原文) ---", result.currentRaw,
"--- 自然语言到工程阶段映射(TCS) ---", result.engineeringMap,
"--- 本阶段唯一交付物 ---", result.receipt.declared_deliverable,
"提醒:语料、报告、仓库副本和研究量增长本身不算产品进度。",
].join("\n"));
} catch (error) {
process.stderr.write(`CODEX_TASK_ANCHOR_STAGE_GATE FAIL ${error.message}\n`);
process.exit(1);
}
}
if (import.meta.url === `file://${process.argv[1]}`) runCli();